Trigger with Autonomous Transaction - sql

I want to know if declaring AUTONOMOUS_TRANSACTION means that the rule action starts after event transaction INSERT on tab has ended (AKA triggering transaction had a successful COMMIT).
I'm using Oracle DB 11.2
Here I have added the trigger:
CREATE OR REPLACE TRIGGER rule1
AFTER INSERT ON tab
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE ('
ALTER TABLE vert_strukt
ADD '||:NEW.name||' VARCHAR2(40) ');
INSERT INTO resulted (name_unique)
VALUES (:NEW.name);
COMMIT;
END;

I recommend you to use a Stored procedure to do so. Also, no transaction control statements are allowed in a trigger. ROLLBACK, COMMIT, and SAVEPOINT cannot be used.

Related

Does anyone know why this trigger is not working?

create or replace NONEDITIONABLE TRIGGER SetNapomena
BEFORE INSERT
ON stavkafakture
FOR EACH ROW
DECLARE
V_napomena VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER zabranjeno DISABLE';
SELECT napomena INTO V_napomena
FROM faktura
WHERE brojfakture =:NEW.brojfakture;
:NEW.napomena := V_napomena;
EXECUTE IMMEDIATE 'ALTER TRIGGER zabranjeno ENABLE';
END;
When I insert into a table (in this case stavkafakture) I get this error:
Cannot commit in a trigger
I did some research and added Pragma Autonomous Transaction and commit, but after adding them I try to insert a row in the table it loads forever and never works.
It stays in load forever.
Does anyone know why?
In Oracle DDL statement like CREATE, ALTER, DROP generate an explicit commit.
IE if you issue this kind of statement this does not requires a COMMIT nor a ROLLBACK :
CREATE TABLE MyTable(MyField NUMBER);
This will create the MyTable table and cannot be ROLLBACK.
This is a limitation of Oracle that other RDBMS like PostreSQL doesn't have.

Postgresql catch rollback when function fails on one of multiple inserts but trigger was executed

I've got plpgsql function that does multiple inserts to multiple tables.
I've also trigger that do some stuff f.g notifies some service.
The process looks like this:
Insert into table A
Trigger executed on table A
Function fails on
insert into table B
And then transaction is rollbacked and I've got no data in table A but trigger has executed.
Furthemore there is no any DELETE on table A.
My trigger and function:
CREATE OR REPLACE FUNCTION some_function() RETURNS trigger AS
$$
BEGIN
PERFORM pg_notify('pgchannel1', row_to_json(new)::text);
RETURN NULL;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER some_trigger
AFTER INSERT
ON some_table
FOR EACH ROW
EXECUTE PROCEDURE some_function();
I would like to have this process like that:
Insert into table A
Insert into table B
Trigger executed on table A / Trigger executed when function is done
How could I catch that transaction was rollbacked and not executing trigger?
Is there any way to trigger function status?
Yes, you can do that with constraint triggers:
CREATE CONSTRAINT TRIGGER some_trigger
AFTER INSERT ON some_table
DEFERRABLE INITIALLY DEFERRED FOR EACH ROW
EXECUTE PROCEDURE some_function();
Such a trigger will fire at the end of the transaction rather than immediately.

Why inside of a trigger the code before raise_application_error isn't executed?

IF I create this trigger, then the error is raised when drop or truncate is used on tables, but there is nothing inserted into logTable, but if I delete RAISE_APPLICATION_ERROR... then the values are inserted into logTable, but the drop/truncate are executed too. Why? How can I avoid drop/truncate on Schema (If I use instead of trigger, it is fired only if owner of the schema is dropping/truncating something).
CREATE OR REPLACE TRIGGER trigger_name
BEFORE DROP OR TRUNCATE ON DATABASE
DECLARE
username varchar2(100);
BEGIN
IF ora_dict_obj_owner = 'MySchema' THEN
select user INTO username from dual;
INSERT INTO logTable VALUES(username , SYSDATE);
RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
END IF;
END;
According to the documentation:
Statement-Level Atomicity
Oracle Database supports statement-level atomicity, which means that a SQL statement is an atomic unit of work
and either completely succeeds or completely fails.
A successful statement is different from a committed transaction. A
single SQL statement executes successfully if the database parses and
runs it without error as an atomic unit, as when all rows are changed
in a multirow update.
If a SQL statement causes an error during execution, then it is not
successful and so all effects of the statement are rolled back. This
operation is a statement-level rollback.
The procedure is a PL/SQL statement, it is atomic, if you raise an error within the procedure, then the whole procedure fails and Oracle performs a rollback of all the changes done by this procedure.
But you can create a procedure with AUTONOMOUS_TRANSACTION Pragma in order to bypass this behaviour, in this way:
CREATE TABLE logtable(
username varchar2(200),
log_date date
);
CREATE OR REPLACE PROCEDURE log_message( username varchar2 ) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO logtable( username, log_date ) VALUES ( username, sysdate );
COMMIT;
END;
/
CREATE OR REPLACE TRIGGER trigger_name
BEFORE DROP OR TRUNCATE ON DATABASE
DECLARE
username varchar2(100);
BEGIN
IF ora_dict_obj_owner = 'TEST' THEN
log_message( user );
RAISE_APPLICATION_ERROR (-20001,'ERROR, YOU CAN NOT DELETE THIS!!');
END IF;
END;
And now:
drop table table1;
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: ERROR, YOU CAN NOT DELETE THIS!!
ORA-06512: at line 6
00604. 00000 - "error occurred at recursive SQL level %s"
*Cause: An error occurred while processing a recursive SQL statement
(a statement applying to internal dictionary tables).
*Action: If the situation described in the next error on the stack
can be corrected, do so; otherwise contact Oracle Support.
select * from logtable;
USERNAME LOG_DATE
-------- -------------------
TEST 2018-04-27 00:16:34

Oracle PL/SQL How to rollback the newly inserted row

create or replace trigger trig_redeem_coffee
before insert
on buycoffee
for each row
declare
CID int;
customerPoint float;
pointNeeded float;
begin
select customer_id into CID
from purchase
where purchase_id = :new.purchase_id;
select total_points into customerPoint
from customer
where customer_id = CID;
pro_get_redeem_point (:new.coffee_ID, :new.redeem_quantity, pointNeeded);
if pointNeeded>customerPoint
then
rollback;
else
pointNeeded := -1*pointNeeded;
pro_update_point(CID, pointNeeded);
end if;
commit;
end;
/
The trigger can be successfully created, but when I insert into buycoffee table(it will meet the condition that pointNeeded>customerPoint), it returns an error that it cannot rollback in a trigger. Is this a proper way to rollback a newly inserted row? Or is there any better way to do it. (all procedures are built properly)
You cannot COMMIT or ROLLBACK inside of a TRIGGER, unless it's an autonomous transaction.
Inside your TRIGGER, you should do whatever logic you wish to apply, but if you reach an error condition, you should raise an application error, rather than ROLLBACK. That should cause the INSERT statement that fired the TRIGGER to error, doing a statement level rollback, and return your transaction to the state it was just before you executed the INSERT. At that point, you can evaluate the error, decide whether to rollback the entire transaction, or re-try the INSERT, or something else.
More on autonomous transactions:
https://docs.oracle.com/database/121/CNCPT/transact.htm#GUID-C0C61571-5175-400D-AEFC-FDBFE4F87188
More on statement-level rollback:
https://docs.oracle.com/cd/B19306_01/server.102/b14220/transact.htm#i8072

When does an insert commit with a trigger on the table?

I have a very simple trigger I wrote for a table at work. The problem is when I use 'AFTER INSERT', the trigger locks the table and nothing else can write to it. When I use 'BEFORE INSERT', the trigger works just fine. Both versions of the trigger compile just fine, but only the 'AFTER INSERT' gives me the error when I use a simple insert statement into the 'EVENT_MESSAGE' table. Below is the trigger and error message:
create or replace TRIGGER TWCC_MHE_RERELEASE
AFTER INSERT
ON EVENT_MESSAGE
FOR EACH ROW
DECLARE
m_count number(3);
BEGIN
select count(*) into m_count from event_message where event_id = 7010 and ek_wave_nbr = :new.ek_wave_nbr;
IF :new.event_id = 7010
THEN
insert into TEST_COUNT
(count, wave_nbr, create_date_time)
values
(m_count,:new.ek_wave_nbr, sysdate);
END IF;
END;
A mutating table occurs when a statement causes a trigger to fire and that trigger references the table that caused the trigger. The best way to avoid such problems is to not use triggers :P
Changed the trigger to an after trigger.
Changed it from a row level trigger to a statement level trigger.
Convert to a Compound Trigger.
Modified the structure of the triggers to use a combination of row and statement level triggers.
Made the trigger autonomous with a commit in it.