What happens when database shut down before triggers executed? - sql

Assume I have table in PostgreSQL as follows:
CREATE TABLE A
(
userid integer,
productid integer,
description citext,
price numeric
)
and some triggers on it:
CREATE TRIGGER afterinsert
AFTER INSERT
ON A
FOR EACH ROW
EXECUTE PROCEDURE DoSomething1();
CREATE TRIGGER beforeinsert
BEFORE INSERT
ON A
FOR EACH ROW
EXECUTE PROCEDURE DoSomething2();
Now, if I do this:
Insert into A values (1,3,'some description',100.5)
What will happen is:
beforeinsert run DoSomething2()
the row is inserted to A
afterinsert run DoSomething1()
My question is what happens if between 2 and 3 the database shuts down?
when it starts again... what will hapen? will it roll back both the inserted row and roll back beforeinsert trigger?
Basically I just don't understand what is considered the Atomic operation in this case. is it the Insert + triggers or just the row?

The triggers are part of the transaction, and it won't commit until they've finished running. If the database shuts down before the transaction commits, it'll be rolled back. The rollback affects all changes that were made in the transaction, including the changes made by the triggers.

If you do a soft shutdown (that is, tell the database to shutdown: pg_ctl -m fast), it will rollback all open transactions. This includes all changes made by any trigger so far.
If you kill the database (like a kill -9), the database has no chance to properly commit or rollback everything. Next time you start the database, it will run a recovery and rollback all the changes to the point of the last successful commit.
All triggers are part of the ongoing transactions, all changes are only committed once all AFTER triggers finish.

Related

Trigger calling stored procedure in Oracle

I have a trigger that calls a stored proc (after insert on a table) to do a number of DML statements. My stored procedure has a commit so my trigger fails. After a bit of researching, I added pragma autonomous_transaction to the trigger and now it doesn't complain. But I'm not sure how this will affect the behaviour of my trigger. Is this the right way to do it or just a "hack" to get it to work?
This is the pseudo code of what I'm trying to do
proc1 (input) - program logic then insert into table X, commit.
proc2 (input) - program logic then insert into table Y, commit.
trigger on table A AFTER INSERT
declare pragma autonomous_transaction
if :new value = 1 then
proc1 (:new value)
else
proc2 (:new value)
end if
Let us consider the program flow:
You start a transaction.
You perform a DML operation on your table.
Your trigger fires and is an autonomous transaction so creates a separate transaction.
Your original transaction continues.
Maybe you perform additional DML actions in that original trasnsaction.
One of those additional DML actions raises an exception.
Your entire transaction (but not the separate autonomous transaction) is rolled back.
However:
You started a separate autonomous transaction.
That calls the stored procedure.
Which does a number of DML statements.
Then the trigger commits them.
You are left in a state where you have two transactions, the original one has been rolled back and the autonomous transaction has been committed.
If the autonomous transaction is used for logging and you want it to always document the actions whether the original transaction failed or was successful then this behaviour is fine.
If the autonomonous transaction is part of the normal business logic then you are potentially left in an inconsistent state where half the transaction was rolled back and half was committed; this is not what you want.
In the latter case, the normal solution would to be to remove the COMMIT from the trigger and make it a non-autonomous transaction and then the DML resulting from the trigger will be part of the original transaction and will be committed (or rolled back) with that transaction.
This is the pseudo code of what I'm trying to do
proc1 (input) - program logic then insert into table X, commit.
proc2 (input) - program logic then insert into table Y, commit.
See: What is the effect of placing the commit after DML in procedure?
In general, you should NOT put a COMMIT in a procedure so your pseudo-code should be:
proc1 (input) - program logic then insert into table X.
proc2 (input) - program logic then insert into table Y.
and your trigger should not be an autonomous transaction.
Then your program flow is:
Insert into table A
Trigger fires
Depending on value, call proc1 or proc2.
Complete the procedure.
Complete the trigger.
Complete the transaction and COMMIT (either from the code that performed the insert or automatically as the transaction closes).

If the table has more than one FOR INSERT trigger and one of them writes to an audit table and the other does a rollback is the audit rolled back

Let's say a table has a validation trigger that enforces some business logic:
TRG_MYTABLE_INSERT_UPDATE_VALIDATION
FOR INSERT, UPDATE on MYTABLE
and an audit trigger that writes all inserts and updates to another table.
TRG_MYTABLE_INSERT_UPDATE_AUDIT
FOR INSERT, UPDATE on MYTABLE
and there's no guarantee that they will be executed in a particular order, will a rollback in the VALIDATION trigger rollback the write to the audit table?
Are all of the triggers enlisted in the same transaction "behind the scenes"?
To answer the question about triggers and transactions: yes triggers are enlisted in the same explicit, or implicit, transaction as the code that executes the statement which makes the trigger fire is enlisted in.
Furthermore, in SQL Server triggers runs be default under XACT_ABORT ON which means that if an error happens in the trigger, the WHOLE transaction is rolled back immediately.
So the answer to your question is that if an error happens in either of the triggers, the whole transaction is rolled back.
You can however do a SET XACT_ABORT OFF in your transaction code, in which case, a rollback would only impact whatever you do in the trigger. That is UNLESS your calling code starts a transaction, and you explicitly do a ROLLBACK in your trigger.
The above is why you should be very careful with using triggers in the first place.

Does stopping query with a rollback guarantee a rollback

Say I have a query like this:
BEGIN Transaction
UPDATE Person SET Field=1
Rollback
There are one hundred million people. I stopped the query after twenty minutes. Will SQL Server rollback the records updated?
A single update will not update some rows. It will either update all or 0.
So, if you cancel the query, nothing will be updated.
This is atomicity database systems which SQL Server follows.
In other words, you don't have to do that rollback at the end, nothing was committed anyway.
When you cancel a query, it will still hold locks until everything is rolled back so no need to panic.
You could test it yourself, execute the long query, cancel it and you will notice that it takes a while before the process really end.
While the update statement will not complete, the transaction will still be open, so make sure you rollback manually or close out the window to kill the transaction. You can test this by including two statements in your transaction, where the first one finishes and you cancel while it's running the second - you can still commit the transaction after stopping it, and then get the first half of your results.
BEGIN Transaction
UPDATE Person SET Field=1 WHERE Id = 1
UPDATE Person SET Field=1
Rollback
If you start this, give it enough time for the first line to finish, hit the Stop button in SSMS, then execute commit transaction, you'll see that the first change did get applied. Since you obviously don't want part of a transaction to succeed, I'd just kill the whole window after you've stopped it so you can be sure everything's rolled back.
Since you have opened the Transaction, Stoping the Query manually does not completes the transaction, This transaction will still be open and all the subsequent requests to this table will be blocked.
You can do any one of following options
Kill the Connection using the command KILL SPID (SPID is the process ID of your connection)
Note: This will auto rollback the changes you made, you can monitor the rollback status with command KILL SPID WITH STATUSONLY (After killing)
run the ROLLBACK command manually
** SPID is your request id, you can find it from sys.sysprocesses table/ you can also find it on Management Studio query Window the number which is within brackets / also you can find it at bottom right corner of your management studio beside the login name.
Example SQLQuery2.sql... (161) -- 161 is your spid.

When exactly is an AFTER DELETE trigger fired

I hope that you can help me on some SQL theory, as I am not 100% sure how this works.
If I have a trigger and I define it as
AFTER DELETE ON xxxx
I was wondering when exactly this would fire, in terms of transaction management?
So if I delete a record from my table I assume that the trigger will not fire until I type commit and finish the transaction. Is this correct?
If so, then I assume that if the commit on my delete statement works but the trigger fails for some reason then only the trigger would be rolled back, and the original executed delete statement that I performed would still be committed (because I have it defined as AFTER DELETE).
Can somebody please confirm this?
Thanks.
1. You delete a row on TABLE1 no COMMIT;
2. TRIGGER performs an action (This takes place before COMMIT or ROLLBACK for step1, but trigger will not have any commit or rollback in it)
3a. You apply commit - Both step1 and step2 gets completed .
3b. You apply rollback- Both step1 and step2 rolled back.
Either you give 3a or 3b
The purpose of SQL triggers is to ensure referential consistency. But when they would be exectued in a separate transaction commit, there would be the possibility that they leave data in an inconsistent state.
So the delete trigger is executed the moment you do the delete command. When this happens as a transaction and you roll it back, the triggered delete is also rolled back.
An AFTER DELETE trigger is fired after the delete statement is executed, and before the control is returned to the user - i.e., he perceives the delete statement and the code executed after it in a trigger as a single action (assuming the trigger just does DMLs and nothing funky like calling UTL_TCP :-)).
This has nothing to do with transaction management - once the DELETE and the AFTER DELETE trigger execute, you can choose to commit, to rollback, or to continue performing DML statements in the same transaction.

waitfor problem in SQL Server

while 1 = 1
begin
waitfor time #timeToRun
begin
/*delete some records from table X*/
end
end
In the codes above, will the SQL server lock the table X during the wait? I would
like to insert records into table X during this wait time. Is it possible?
All write operations acquire X locks on the rows being updated (deleted) and all these X locks will be hold until the transaction commits. Every statement creates an implicit transaction that commits automatically at the end of the statement, if no transaction is explicitly specified.
So the answer to your question depends whether you call this in a context of an existing transaction or not. If not, then (assuming you do not start a transaction in the inner begin... end block and leave the transaction open) then no lock will be held. If the code is run in a the context of an existing transaction (eg. a TransactionScope in the client started automatically by the WCF service behavior) then any lock placed by the delete will be hold while you wait, until the transaction is committed..
Two part question:
1) In the codes above, will the SQL server lock the table X during the wait?
No. It may lock the rows, but not the table.
2) I would like to insert records into table X during this wait time. Is it possible?
Yes, but they will be locked until your commit
Note: you will want to wrap any work you are doing in a transaction with a BEGIN/COMMIT block. This will avoid the locking issue entirely.