Mysql trigger inserts persist after original transaction is rolled back - mysql

I have implemented a typical "audit log" trigger in Mysql version 5.5. I use AFTER INSERT to copy inserted rows from table user into my 'audit_log' table.
So for this sequence:
BEGIN;
insert into user (name) values ('joe');
<--trigger fires, adds new row to audit_log table-->
COMMIT;
Then I get a new row in 'audit_log' with 'joe'. Great.
However, it appears that the results of my trigger are applied even if the insert which fired the trigger is aborted by its enclosing transaction.
So for this sequence:
BEGIN;
insert into user (name) values ('mary');
<--trigger fires, adds new row to audit_log table-->
ROLLBACK;
I STILL end up with a new row 'mary' in audit_log, which refers to data that was never committed to my user table. This seems pretty clearly wrong.
It appears that trigger updates performed in Postgres execute within the original transaction, which is what I would expect to happen. Anyone have experience with this in MySQL? Perhaps there is a setting I have wrong?
I note that this question implies that all updates happen in the original transaction. However, the answer refers to the Mysql manual page on triggers, which in fact has no mention of "transation" at all.

Turns out my audit table was using the MyISAM engine which of course prevented it from obeying the transaction properly.
My colleague says I owe Larry Ellison an apology.
Larry, I'm sorry I doubted the transactional semantics of Mysql.

Related

MySql After Update Trigger execution order

I am calling an API of my Nodejs app to update a record in my MySQL database.
I defined an "After Update trigger" on it. the trigger calls a post restful API using sys_exec,to pass the updated record's ID to another API. Then, the other API fetch the record and based on the updated values, will insert a new record in the other table of the same database.
But what actually happens is: first the second API insert new record based on the old values of the record and then the old value update new value.
As far as I know, "after update trigger" guarantees to start executing trigger after updating current record.
any suggestion or help, please.
The after update trigger runs after the record is updated, but before the committing of the transaction.
By calling another api from the trigger, the 2nd insert is most likely runs in a different transaction. Unless you change the isolation level to read uncommitted, the 2nd transaction can only read the committed, therefore unchanged values of the record.
I would do the 2nd insertion from the trigger, not from another api because the trigger can obviously see the updated values. The 2nd api can still take care of whatever else it is doing at the moment.
I would not recommend changing the isolation level to read uncommitted - unless you really know what you are doing. It can have unintended side effects.

MySQL Trigger with transaction insert or update

i'm using mysql db & java, so in my application i need to use trigger, but i need to make it save while inserting or updating.
when insering new record in table A, trigger will do the work to insert some information in another table B.
also when doing update for records in table A, trigger will update records for table B.
if transaction during update or insrt rolledback, does this rolled back any changes done by the trigger.??!!
Yes, if the transaction is rolled back, the work done by the trigger will also be rolled back (unless you do some monkeying with transaction scoping inside the trigger to specifically prevent this)

SQL Server - After Insert/ For Insert - Rollback

I have the below trigger:
CREATE Trigger instructor_expertise on CourseSections
After Insert
As Begin
......
If (Not Exists(Select AreaName From AreasOfInstructor Where (InstructorNo = #InstructorNo AND AreaName = #AreaName)))
Begin
RAISERROR('Course not in instructors expertise', 16, 1)
rollback transaction
End
GO
My question is, does 'rollback transaction' remove the row?
What if it's 'For Insert' instead, does 'rollback transaction' remove the row in that case?
Thanks!!!
Your INSERT statement always runs in a transaction - either you've explicitly defined one, or if not, then SQL Server will use an implicit transaction.
You're inserting one (or multiple) row into your table. Then - still inside the transaction - the AFTER INSERT trigger runs and checks certain conditions - typically using the Inserted pseudo table available inside the trigger, which contains the rows that have been inserted.
If you call ROLLBACK TRANSACTION in your trigger, then yes - your transaction, with everything it's been doing, is rolled back and it's as if that INSERT never happened - nothing shows up in your database table.
Also: FOR INSERT is the same as AFTER INSERT in SQL Server - the trigger is executed after the INSERT statement has done its job.
One thing to keep in mind (which a lot of programmers get wrong): the trigger is fired once per statement - NOT once per row! So if you insert 20 rows at once, the trigger is fired once and the Inserted pseudo table inside the trigger contains 20 rows. You need to take that into account when writing the trigger - you're not always dealing with just a single row being inserted!
no it is not possible because when their is no row exist then it will go in begin block ...

Sqlserver standard trigger order

I have database partially created and edited by an external CRM where certain tables have multiple (at most 2) after triggers on them. This is due to 1 trigger being auto generated by the CRM (over which I have limited control) and the other containing my code.
The CRM trigger generates the primary key for the datarow inserted. My trigger needs to access that primary key in order to write it to another table as foreign key. I use
Select #id=max(id) from mytable
since Scope_Identity did not produce the desired result somehow.
This worked until I let the CRM recreate the table and its own trigger. The maximum id of that table selected by my trigger seemed to always be actual_id - 1.
When I altered my trigger using the same code it has always had the procedure worked again.
My question is:
Does SQL server (I am using SQL Server 2008) set its trigger order by creation time?
And:
Is
sp_settriggerorder #triggername='mycustomtrigger', #order='Last', #stmttype='INSERT'
going to change this permanently or do I have to call that procedure again, every time the CRM recreates its trigger? (using DROP and CREATE, not ALTER)
Hope the answers to that will help someone looking at the same issue.
Regards
It's not documented, but I believe that the LAST setting will stay with a trigger, provided it is not modified. (Contrariwise, it is documented that a trigger will lose this setting if it is modified). However, it seems to work:
create table T (ID int not null)
go
create trigger T_T1 on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
go
create trigger T_T2 on T
after insert
as
RAISERROR('T2',10,1) WITH NOWAIT
go
create trigger T_T3 on T
after insert
as
RAISERROR('T3',10,1) WITH NOWAIT
go
insert into T(ID) values (1)
go
sp_settriggerorder 'T_T2','Last','INSERT'
go
insert into T(ID) values (2)
go
drop trigger T_T1
go
create trigger T_T1 on T
after insert
as
RAISERROR('T1',10,1) WITH NOWAIT
go
insert into T(ID) values (3)
Results:
T1
T2
T3
(1 row(s) affected)
T1
T3
T2
(1 row(s) affected)
T3
T1
T2
(1 row(s) affected)
As to your first question, however:
Does SQL server (I am using SQL Server 2008) set its trigger order by creation time?
It also appears to, but I would not rely on that. sp_settriggerorder is the only place where any ordering is documented.
Finally, as mentioned in my comment, I wouldn't rely on your current Select #id=max(id) from mytable method - it could be broken for a number of reasons, but the most important is that a trigger is fired once per method, and may fire in response to multiple rows, so you ought to write triggers to use the inserted pseudo-table instead (and expect it to contain 0, 1 or multiple rows).

SQL trigger to delete rows from database

I have an industrial system that logs alarms to a remotely hosted MySQL database. The industrial system inserts a new row whenever a property of the alarm changes (such as the time the alarm was activated, acknowledged or switched off) into a table named 'alarms'.
I don't want multiple records for each alarm, so I have set up two database triggers. The first trigger mirrors each new record to a second table, creating/updating rows as required. The second table ('alarm_display') has the 'Tag' column set as the primary key. The 'alarm' table has no primary key. The code for this trigger is:
CREATE TRIGGER `mirror_alarms` BEFORE INSERT ON `alarms`
FOR EACH ROW
INSERT INTO `alarm_display` (Tag,...,OffTime)
VALUES (new.Tag,...,new.OffTime)
ON DUPLICATE KEY UPDATE OnDate=new.OnDate,...,OffTime=new.OffTime
The second trigger should execute after the first and (ideally) delete all rows from the alarms table. (I used the Tag property of the alarm because the Tag property never changes, although I suspect I could just use a 'DELETE FROM alarms WHERE 1' statement to the same effect).
CREATE TRIGGER `remove_alarms` AFTER INSERT ON `alarms`
FOR EACH ROW DELETE FROM alarms WHERE Tag=new.Tag
My problem is that the second trigger doesn't appear to run, or if it does, the second trigger doesn't delete any rows from the database.
So here's the question: why does my second trigger not do what I expect it to do?
The explanation can be read here: http://dev.mysql.com/doc/refman/5.0/en/stored-program-restrictions.html
Within a stored function or trigger,
it is not permitted to modify a table
that is already being used (for
reading or writing) by the statement
that invoked the function or trigger.
This is your problem and your trigger ends with error #1442.
The table alarms is already being used by the statement that invoked your trigger (the insert). This essentially means you cannot modify alarms with a delete trigger.
Cheers!