Trigger to disable insert into a table for a specific day in oracle - oracle

How can i correctly write a trigger for this task:
create or replace trigger M_t2
after insert on emp
begin
if ( to_char(sysdate,'DY' ) = 'TUE' ) then
dbms_output.put_line('cannot insert into emp on tuesday');
end if;
end;
/
This does not work as i am still able to insert like this:
insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values('7935','BOLT','ANALYST',7698,sysdate,900,100,10);

dbms_output doesn't stop you doing anything, and you won't even see that message if your client isn't set up to show the output.
To prevent an action you'd need to raise an exception:
if ( to_char(sysdate,'DY' ) = 'TUE' ) then
raise_application_error(-20001, 'cannot insert into emp on tuesday');
end if;
The DY value is NLS-dependent so this could be circumvented by having a session with a different language, so you should take that into account using the optional third parameter to to_char(). It might as well be a before-insert trigger too:
create or replace trigger M_t2
before insert on emp
begin
if ( to_char(sysdate, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH' ) = 'TUE' ) then
raise_application_error(-20001, 'cannot insert into emp on tuesday');
end if;
end;
/
insert into emp ...
ERROR at line 1:
ORA-20001: cannot insert into emp on tuesday
ORA-06512: at "<schema>.N_T2", line 3
ORA-04088: error during execution of trigger '<schema>.M_T2'

Please look at this: http://www.techonthenet.com/oracle/triggers/after_insert.php
I think you must use :new clause and use variable

Related

How to capture errors inside trigger

I want to create trigger which should capture all error whatever it happens inside the trigger.
And it should store in some log table.
Here my challange is how to capture the error occur in DML statments.
THat DML statments error captured in log with respective table name along with column name and error message.
It should also caputre all other errors like no_data_found with exact line number.
Please look at my code below.
If any changes need tell us.
create or replace TRIGGER user_name.sample_trg
AFTER UPDATE ON user_name.transaction_tb
FOR EACH ROW
DECLARE
variable_ln number;
l_err varcha2(4000);
BEGIN
select column_value
into variable_ln
from tb1
where colum_1 = :NEW.colum_1
IF UPDATING THEN
INSERT
INTO hisotry_tb
(
column1,
column2,
column3,
column4,
)
VALUES
(
:NEW.column1,
:NEW.column2,
:NEW.column3,
:NEW.column4,
);
END IF;
IF INSERTING THEN
INSERT
INTO hisotry_tb
(
column5,
column6,
column7,
column8,
)
VALUES
(
:NEW.column5,
:NEW.column6,
:NEW.column7,
:NEW.column8,
);
END IF;
EXCEPTION
WHEN OTHERS THEN
l_err := DBMS_UTILITY.FORMAT_ERROR_STACK || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
INSERT INTO log (erro_msg, trigger_name, column_name)
VALUES (l_err, 'sample_trg', '?');
DBMS_OUTPUT.put_line (l_err);
END;

why it is working without PRAGMA AUTONOMOUS_TRANSACTION

I have a misunderstanding regarding to
PRAGMA AUTONOMOUS_TRANSACTION
directive.
As far as I know, it is used in logging or auditing procedure, to run independently by the main program (autonomous, procedure, function or trigger).
I have an UPDATE on a table which generated DUP_VAL_ON_INDEX. In this exception, I call a logging procedure which logs the error into a table. In the logging procedure I didn't specified PRAGMA AUTONOMOUS_TRANSACTION directive but it still makes the insert in my logging table.
Here is my code:
create table TEST_PRAGMA
( COL_1 number primary key
, COL_2 number
);
--
insert into TEST_PRAGMA values (1, 200);
insert into TEST_PRAGMA values (2, 200);
--
create table T_LOG
( msg_num number primary key
, MSG_DATE timestamp(6)
, INFO_MSG varchar2(10)
, LONG_MSG varchar2(100)
);
--
create sequence SEQ_TEST start with 1 increment by 1 nocache nocycle;
Package:
create or replace package pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, p_long_msg varcahr2);
end PKG_LOGGING;
--
create or replace package body pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, P_LONG_MSG VARCHAR2)
as
begin
insert into T_LOG
( MSG_NUM
, MSG_DATE
, INFO_MSG
, LONG_MSG
)
values
( P_MSG_NUM
, P_MSG_DATE
, P_INFO_MSG
, P_LONG_MSG
);
commit;
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
end PKG_LOGGING;
--
set SERVEROUTPUT on;
begin
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
Because I didn't specified the PRAGMA directive, I was expecting not to log the error even if the logic is correct.
Can anyone explain me why it is still logs my error and provide a sample where it does not log the code if I do not specify the PRAGMA AUTONOMOUS_TRANSACTION directive, please?
Thank you,
Can anyone explain me why it is still logs my error and provide a
sample where it does not log the code if I do not specify the PRAGMA
AUTONOMOUS_TRANSACTION directive, please?
The error is being Inserted in Log table since you are handling it as an Exception handling. You need to understand the behavior of AUTONOMOUS transaction as being an Independent piece of code which executes even if the main calling proc/pkg fails. It's not being handled as a part of Exception Handling. As shown in below demo you can see proc marked as AUTONOMOUS is called in BEGIN block directly rather than in Exception block to understand the behavior.
DECLARE
l_salary NUMBER;
--Private Proc marking as Autonomous transaction
procedure nested_block ‬
as
pragma AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE emp
SET salary=salary+15000
WHERE emp_no=1002;
COMMIT;
END;‭
--Main Block ‬
BEGIN
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no=1001;
Dbms_output.put_line('Before Salary of 1001 is'||l_salary);
SELECT salary
INTO l_salary
FROM emp WHERE emp_no=1002;
Dbms_output.put_line('Before Salary of 1002 is '|| 1_salary);
UPDATE emp
SET
salary = salary + 5000
WHERE emp_no = 1001;
--Calling Autonomous transaction
nested_block;
--And rolling back previous updates.
ROLLBACK;
SELECT salary INTO
l_salary
FROM emp
WHERE emp_no = 1001;
dbms_output.put_line('After Salary of 1001 is'|| l_salary);
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no = 1002;
dbms_output.put_line('After Salary of 1002 is ' || l_salary);
end;
Output:
The output will have the Update done in Autonomous transaction. Updates done in the main block will be rolledback but not the one which is done in private proc marked as Autonomous
Before Salary of 1001 is 15000
Before Salary of 1002 is 10000
After Salary of 1001 is 15000
After Salary of 1002 is 25000
PKG_LOGGING.PRC_LOG() has a commit statement so it will commit.
Suppose your code looked liked this:
set SERVEROUTPUT on;
begin
insert into TEST_PRAGMA values (3, 300);
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'info', 'inserted a record');
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
How many records would you have in TEST_PRAGMA? Three. Because the insert was committed when we called PKG_LOGGING.PRC_LOG(), and consequently the rollback in the exception handler had no effect. And that's why we should use PRAGMA AUTONOMOUS_TRANSACTION in audit and logging routines: so we can successfully persist our logging messages without affecting the broader transaction.
So you should add PRAGMA AUTONOMOUS_TRANSACTION to PKG_LOGGING.PRC_LOG().
Incidentally, I think you should be careful with an error handler like this in a logging package:
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
In some situations we definitely would want to stop our process if we can't log vital information. But other times we want logging to fail gracefully. For instance I need the overnight batch run to abend if it can't record errors, because that log is my only way of knowing what - if anything - went wrong, and it's better for the whole thing not to run that for it to run incompletely and me not to know that some things failed. But if I'm just writing some trace messages in Test I might prefer the long running process to conclude without a complete set of trace rather than abend because the logging table has run out of space.
Also, using raise_application_error() is not necessary. Just issue raise; after the rollback and be done with it.

How to handle exception within trigger

I want to add exception handling within trigger.
I created Trigger as below.
I want to add exception handling within that. So that my trigger never failed if there are any invalid condition.
CREATE OR REPLACE TRIGGER system_notification_audit
AFTER
INSERT OR
UPDATE
on system_notification
FOR EACH ROW
begin
insert into system_notification_log
select :NEW.ID , :NEW.NAME, :NEW.Description, :NEW.PREFERENCE, :NEW.FREQUENCY,
:NEW.IS_HIGH, :NEW.IS_REQUIRED, :NEW.UPDATED_BY, :NEW.UPDATED_DATE
from dual
where :OLD.PREFERENCE <> :NEW.PREFERENCE
OR :OLD.FREQUENCY <> :NEW.FREQUENCY OR :NEW.IS_HIGH <> :OLD.IS_HIGH OR :NEW.IS_REQUIRED <> :OLD.IS_REQUIRED;
END;
Simply Ignoring an Exception isn't always a good way to design your program. At least log the errors somewhere for later observation.
Note that your INSERT statement can be rewritten using a simple insert ( without a select ) and an IF condition.
CREATE OR REPLACE TRIGGER system_notification_audit AFTER
INSERT OR UPDATE ON system_notification
FOR EACH ROW
BEGIN
IF
:old.preference <>:new.preference OR :old.frequency <>:new.frequency
OR :new.is_high <>:old.is_high OR :new.is_required <>:old.is_required
THEN
INSERT INTO system_notification_log (
id,
name,
description,
preference,
frequency,
is_high,
is_required,
updated_by,
updated_date
) VALUES (
:new.id,
:new.name,
:new.description,
:new.preference,
:new.frequency,
:new.is_high,
:new.is_required,
:new.updated_by,
:new.updated_date
);
END IF;
EXCEPTION WHEN OTHERS THEN
pr_trigger_logs(trig_name => 'system_notification_audit',
err_msg => DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
-- calling an error logging procedure.
END;
/
I won't give you the definition of procedure pr_trigger_logs. As an exercise, I'll let you come up with it!.

subquery in oracle trigger

I want include condition in my trigger, based on data from a table. As oracle to do allow subquery in trigger, how it can be achived. Please find my code below. Trans_code_master holds the list of valid code, which can be changed.
Thanks in advance for all your help.
CREATE OR REPLACE TRIGGER CUST_TRG
BEFORE INSERT OR UPDATE ON CUST_ALL_TRANS
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
WHEN (NEW.TRANSACTION_CODE IN(SELECT TRANS_CODE FROM TRANS_CODE_MASTER))
BEGIN
INSERT INTO CUST_DEPO_TRANS
(
CUST_ID
,AC_ID
,TRANSACTION_CODE
)
VALUES(
:NEW.CUST_ID
,:NEW.AC_ID
,:NEW.TRANSACTION_CODE
)
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END;
/
Its not possible in write query in when clause. Try the below way
CREATE OR REPLACE TRIGGER CUST_TRG
BEFORE INSERT OR UPDATE ON CUST_ALL_TRANS
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_CNT NUMBER;
BEGIN
SELECT COUNT(1) INTO V_CNT
FROM TRANS_CODE_MASTER
WHERE TRANS_CODE = NEW.TRANSACTION_CODE;
IF V_CNT > 0 THEN
INSERT INTO CUST_DEPO_TRANS
(
CUST_ID
,AC_ID
,TRANSACTION_CODE
)
VALUES(
:NEW.CUST_ID
,:NEW.AC_ID
,:NEW.TRANSACTION_CODE
)
END IF;
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END;
/

Oracle Mutating table - trigger error

I get these error when updating a table
ORA-04091: table HAMZA.EXPEDITION is mutating, trigger/function may not see it
ORA-06512: at "HAMZA.LIVRAISONFINIE", line 6
ORA-04088: error during execution of trigger 'HAMZA.LIVRAISONFINIE'
Here are tables used
Expedition(Id_Expedition , Date_Livraison);
Commande(Numero_Commande,..);
cmdalivrer (id_cmd,id_etape);
CREATE OR REPLACE TRIGGER livraisonfinie
AFTER UPDATE ON Expedition
FOR EACH ROW
DECLARE
date_liv Expedition.date_livraison%type;
BEGIN
SELECT :NEW.date_livraison INTO date_liv from Expedition;
IF date_liv <> TO_DATE('00/00/00 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
THEN
INSERT INTO Commande (etat) VALUES ('livree');
DELETE FROM cmdalivrer CMD WHERE :NEW.numero_commande=CMD.id_cmd;
DELETE FROM Expedition E WHERE E.date_livraison = date_liv;
END IF ;
END;
/
Thanks for helping .
You can accomplish what you're trying to do using a compound trigger:
CREATE OR REPLACE TRIGGER livraisonfinie_compound
AFTER UPDATE ON Expedition
COMPOUND TRIGGER
TYPE DATE_TABLE IS TABLE OF EXPEDITION.DATE_LIVRAISON%TYPE;
tblDates DATE_TABLE;
BEFORE STATEMENT IS
BEGIN
tblDates := DATE_TABLE();
END BEFORE STATEMENT;
AFTER EACH ROW IS
BEGIN
IF :NEW.date_livraison <> TO_DATE('00/00/00 00:00:00', 'yyyy/mm/dd hh24:mi:ss')
THEN
INSERT INTO Commande (etat) VALUES ('livree');
DELETE FROM cmdalivrer CMD WHERE :NEW.numero_commande=CMD.id_cmd;
tblDates.EXTEND;
tblDates(tblDates.LAST) := :NEW.date_livraison;
END IF ;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
IF tblDates.COUNT > 0 THEN
FOR i IN tblDates.FIRST..tbDates.LAST LOOP
DELETE FROM Expedition E
WHERE E.date_livraison = tblDates(i);
END LOOP;
END IF;
END AFTER STATEMENT;
END livraisonfinie_compound;
You can read further on compound triggers here.
Best of luck.

Resources