Alter trigger from another trigger - oracle

I wish to have alter trigger under another trigger.
CREATE TRIGGER izmeni_naziv_kupac
AFTER UPDATE OF naziv ON Kupac
FOR EACH ROW
DECLARE
novi_naziv varchar2(255);
pibK INT;
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER ZabranaPromeneNazivaKupaca DISABLE';
pibK := :new.pib;
novi_naziv := :new.naziv;
UPDATE Porudzbina
SET naziv_kupca = novi_naziv
WHERE kupac_PIB = pibK;
EXECUTE IMMEDIATE 'ALTER TRIGGER ZabranaPromeneNazivaKupaca ENABLE';
END;
Problem is that system sent error message:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "SYSTEM.IZMENI_NAZIV_KUPAC", line 5
ORA-04088: error during execution of trigger 'SYSTEM.IZMENI_NAZIV_KUPAC'

This is not a practical way to manage trigger activation, and for the same reason, I'd advise against using 'autonmous' to work around it because its very rare to want to turn off a trigger for all users, because typically that breaks an application.
More likely is that you want to disable a trigger for this current session, for example, to perform some sort of maintenance activity etc. For this task, you would code the trigger to have a flag in order to let you control its execution.
Thus your code would be along the lines of (psuedo-code):
trigger my_trigger
before insert ...
begin
if i_am_meant_to_fire then
...
... my normal code
...
end if;
end;
where you then have session level control over when the trigger should take any action. It is always enabled, but might not do anything based on the flag.
A full walkthrough of some options here
https://youtu.be/P1OFbNhgT1k

Related

How to create a trigger that wont hold the entire transaction till it complete?

There is a procedure which have insert operation in middle for a table. The table contain a trigger and because of that the entire transaction getting hold. Is there are anyway to make trigger run on separate session and after insert operation procedure runs without waiting for the trigger to complete.
Both procedure and the trigger are
PRAGMA AUTONOMOUS_TRANSACTION
You could try runnig the trigger part as dbms_job ... as follows:
CREATE OR REPLACE TRIGGER myrigger
AFTER INSERT
ON mytable
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
l_job number;
begin
dbms_job.submit( l_job, 'MYPACKAGE.MYFUNCTION(''' || :new.myField || ''');' );
END ;
/
If Trigger is based on insert operation, it will hold the current session, until that trigger action is completed. This is required to maintain integrity at database side. It may be possible to design a procedure & trigger in a better way if the requirements are known.

Oracle 'after create' trigger to grant privileges

I have an 'after create on database' trigger to provide select access on newly created tables within specific schemas to different Oracle roles.
If I execute a create table ... as select statement and then query the new table in the same block of code within TOAD or a different UI I encounter an error, but it works if I run the commands separately:
create table schema1.table1 as select * from schema2.table2 where rownum < 2;
select count(*) from schema1.table1;
If I execute them as one block of code I get:
ORA-01031: insufficient privileges
If I execute them individually, I don't get an error and am able to obtain the correct count.
Sample snippet of AFTER CREATE trigger
CREATE OR REPLACE TRIGGER TGR_DATABASE_AUDIT AFTER
CREATE OR DROP OR ALTER ON Database
DECLARE
vOS_User VARCHAR2(30);
vTerminal VARCHAR2(30);
vMachine VARCHAR2(30);
vSession_User VARCHAR2(30);
vSession_Id INTEGER;
l_jobno NUMBER;
BEGIN
SELECT sys_context('USERENV', 'SESSIONID'),
sys_context('USERENV', 'OS_USER'),
sys_context('USERENV', 'TERMINAL'),
sys_context('USERENV', 'HOST'),
sys_context('USERENV', 'SESSION_USER')
INTO vSession_Id,
vOS_User,
vTerminal,
vMachine,
vSession_User
FROM Dual;
insert into schema3.event_table VALUES (vSession_Id, SYSDATE,
vSession_User, vOS_User, vMachine, vTerminal, ora_sysevent,
ora_dict_obj_type,ora_dict_obj_owner,ora_dict_obj_name);
IF ora_sysevent = 'CREATE' THEN
IF (ora_dict_obj_owner = 'SCHEMA1') THEN
IF DICTIONARY_OBJ_TYPE = 'TABLE' THEN
dbms_job.submit(l_jobno,'sys.execute_app_ddl(''GRANT SELECT
ON '||ora_dict_obj_owner||'.'||ora_dict_obj_name||' TO
Role1,Role2'');');
END IF;
END IF;
END IF;
END;
Jobs are asynchronous. Your code is not.
Ignoring for the moment the fact that if you're dynamically granting privileges that something in the world is creating new tables live in production without going through a change control process (at which point a human reviewer would ensure that appropriate grants were included) which implies that you have a much bigger problem...
When you run the CREATE TABLE statement, the trigger fires and a job is scheduled to run. That job runs in a separate session and can't start until your CREATE TABLE statement issues its final implicit commit and returns control to the first session. Best case, that job runs a second or two after the CREATE TABLE statement completes. But it could be longer depending on how many background jobs are allowed to run simultaneously, what other jobs are running, how busy Oracle is, etc.
The simplest approach would be to add a dbms_lock.sleep call between the CREATE TABLE and the SELECT that waits a reasonable amount of time to give the background job time to run. That's trivial to code (and useful to validate that this is, in fact, the only problem you have) but it's not foolproof. Even if you put in a delay that's "long enough" for testing, you might encounter a longer delay in the future. The more complicated approach would be to query dba_jobs, look to see if there is a job there related to the table you just created, and sleep if there is in a loop.

How to update a table using an AFTER INSERT trigger in that same table?

I have 2 triggers on a table A one is BEFORE INSERT and one AFTER INSERT :
CREATE OR REPLACE TRIGGER DEMO_TRG
AFTER INSERT
ON A
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
UPDATE A
SET STATUS = 'DONE'
WHERE id_id =:new.p_id;
commit;
END;
/
Why the update command in the after insert doesn't work (the status is not set to DONE)? Did I missed something?
Most likely the issue is due to the AUTONOMOUS_TRANSACTION - when you do that, you're effectively switching to a different transaction, which won't know about your uncommitted transaction (ie. your insert), so there is nothing for it to update.
What you should do is amend the column (:new.status := 'DONE') in your before trigger. Or, even better, avoid triggers and have the logic in a stored procedure and don't allow anyone to directly insert into the table, although I appreciate this may be a big switch if you have lots of applications inserting directly into tables etc

Is it possible to disable oracle trigger on Oracle Unit Test startup

We have a table that has a trigger used to write the row to a different database, that we don't want to execute during out unit tests. Is there a way to disable and reenable the trigger during startup and tear down?
In the start up I have tried
BEGIN
ALTER TRIGGER CAMMS_DATA.TR_CO_DTL_AI DISABLE;
END;
and get the error
Startup User PL/Sql Code (1/4) failed: ORA-06550: line 4, column 5:
PLS-00103: Encountered the symbol "ALTER" when expecting one of the following:
DDL's can be executed only with EXECUTE IMMEDIATE
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER CAMMS_DATA.TR_CO_DTL_AI DISABLE';
END;
But be aware that DDL also means automatic COMMIT

Reverse a commit value in oracle sql

I've wrote a procedure as follows
Create or replace procedure raise_salary(
v_id IN emp.empno%TYPE) AS
Begin
update emp
set sal = sal*1.10
where empno=v_id;
commit;
End;
I realized that i didn't create an exception to rollback the value.
I want to go back to its original value. the value cannot be decreased as i get the following error.
ORA-20187: Salaries cannot be decreased
how do I go back to the original value. I've dropped the procedure but now since i already executed it, it increased it already. Seems like its a fixed value now. What do I do?
Dropping the procedure has absolutely nothing to do with reversing the effects of running it.
It sounds like there is a trigger on the table preventing you from doing the update that you want to do. One option is to disable the trigger. The error stack from the update should tell you its name. You would execute ALTER TRIGGER <triggername> DISABLE, then run your update, then `ALTER TRIGGER ENABLE'.
Another possible workaround could be to insert the row into a second table, update it there, then delete and re-insert it in the original table. This could also run afoul of triggers if someone has tried to prevent inserts or deletes on the table.
This is all assuming you are in an environment where bypassing logic control implemented by someone else is appropriate. Presumably the trigger exists for a reason.
select trigger_name from user_triggers where table_name='EMP'
Will show you trigger names of table you want to rollback. Then disable them like previous answer show. Dont foget to enable them back.
Begin
For cur in (select trigger_name from user_triggers where tbale_name='EMP') loop
Execute immediate 'alter trigger'||cur.trigger_name||' disable';
Update emp set ....your update DML;
Execute immediate 'alter trigger'||cur.trigger_name||' enable';
end loop;
End;
I hope, it help you.

Resources