How are database triggers implemented inside a SQL database engine? - mysql

How are triggers implemented inside a SQL database engine? I am not referring to the SQL language-level trigger definitions but rather their underlying implementations inside Oracle, SQL Server, MySQL, etc. How can the database engine scalably manage hundreds or thousands of triggers? Do they use a publish-subscribe model like with an observer/listener pattern? Any pointers to relevant literature on the subject would also be appreciated.
I did google for "database trigger implementation" but all I found was information on SQL trigger definitions, which again is not want I'm looking for.

Triggers are callbacks, so the implementation can be as simple as function pointers in C. Normally, a user is not expected writing user-defined procedural code in the RDBMS in C, though. You would need to support some other "higher-level" language. So the relevant programming pattern is DSL. The number of triggers (scalability) itself is not a problem because there is usually only one, max two per table and DML event triggers only these. The implementation challenge is elsewhere: in the areas of consistency, concurrency semantics.

You can explore source codes of open source databases.
For example PostreSql's trigger.

First off, triggers are pieces of code that are run when a particular event (e.g. INSERT/UPDATE/DELETE on a particular table) occurs in the database. Triggers are executed implicitly BEFORE or AFER the DML statement and triggers cannot be executed explicitly like stored procedures.
There are also two types of triggers - STATEMENT LEVEL triggers and ROW LEVEL triggers.
The STATEMENT LEVEL triggers are fired BEFORE or AFTER a statement is executed.
The ROW LEVEL triggers are fired BEFORE or AFTER an operation is performed on each individual row affected by the operation.
So we have 12 types of triggers:
1. BEFORE INSERT STATEMENT
2. BEFORE INSERT ROW
3. AFTER INSERT STATEMENT
4. AFTER INSERT ROW
5. BEFORE UPDATE STATEMENT
6. BEFORE UPDATE ROW
7. AFTER UPDATE STATEMENT
8. AFTER UPDATE ROW
9. BEFORE DELETE STATEMENT
10. BEFORE DELETE ROW
11. AFTER DELETE STATEMENT
12. AFTER DELETE ROW
Multiple triggers can be coded for an event with their order of precedence of execution mentioned.
Whenever we run a DML query (INSERT/UPDATE/DELETE) on a database, that query is run in a transaction. Hence when a query runs -
The table is locked
The DBMS checks for triggers that run BEFORE the statement is to be executed
Execute the actual SQL statement row-by-row.
The BEFORE trigger for EACH ROW is looked for. If found, executed.
Check for errors. If any, rollback the changes made by the statement or its triggers.
Any AFTER EACH ROW triggers are found and executed.
Any AFTER STATEMENT triggers are found and executed.
Different DBMS manage transactions differently. Refer to their documentation for details.
Many DBMS keep the triggers in text format only, not like stored procedures that are compiled.
It is best practice to call stored procedures from inside a trigger body as stored procedures are much faster performers than triggers.

Related

Does MySQL handle queries within a procedure synchronously?

Perhaps the title is a little misleading, so I'll explain my question in further detail. Obviously the queries inside of the procedure are executed synchronously and in order, but are procedures themselves executed synchronously?
Lets say I have a procedure called "Register" which handles a couple of queries, for example it looks like this:
BEGIN
DECLARE account_inserted INT(11);
INSERT INTO accounts (...) VALUES (...);
SET account_inserted = LAST_INSERTED_ID(); # <------
QUERY using account_inserted...
QUERY using account_inserted...
QUERY using account_inserted...
...
END
Now lets say that there were numerous requests to register coming in at the same time (For example purposes let us say around 200-300 requests) would it execute all of the procedures in order? Or is it possible would the LAST_INSERTED_ID() variable to conflict with a row inserted from another procedure that is being executed in parallel?
You're muddling three things:
Whether MySQL executes procedures synchronously
This could be interpreted to mean either "does MySQL wait for each command within a procedure to complete before moving on to the next?" or "does MySQL wait for the entire procedure to complete before accepting further instructions from the connection that invoked the CALL command?". In both cases, the answer is "yes, it does".
Whether invocations of MySQL procedures are executed atomically
As with any other series of commands, commands within procedures are only atomic if performed within a transaction on tables that use a transactional storage engine. Thus a different connection may well execute another INSERT between the INSERT in your procedure and the command that follows.
Whether LAST_INSERTED_ID() is guaranteed to return the value generated by the immediately preceding INSERT command in the procedure?
Yes, it is. The most recent insertion ID is maintained on a per-connection basis, and as described above the connection waits for CALL to complete before further commands are accepted.
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
https://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id

Which tables were affected during single query run by triggers cascade

According to MySQL Performance Blog, the new Percona Servers, announced yesterday (May 6), both include the open source version of the MySQL Audit Plugin.
The task I want to accomplish is: log the tables affected by cascade trigger execution during one single update query run. E. g. when UPDATE MY_TABLE … is executed, the triggers {BEFORE,AFTER}_UPDATE may update other tables, on which there might be their own triggers, etc.
Currently I use the domestic solution; inside all triggers I put smth like:
IF (
SELECT count(*)
FROM `information_schema`.`ROUTINES`
WHERE specific_name = 'my_own_log'
AND routine_schema = 'my_schema'
) > 0 THEN
CALL my_own_log ('FOO_TRIGGER', 'Hi, I’m to update MY_TABLE') ;
END IF ;
In production I don’t have the my_own_log procedure defined and since the information_schema table is well-optimized, I don’t yield any performance penalties.
The question is if I could switch to enterprise solution (aforementioned audit plugin) to harvest an information about which tables were affected by cascade trigger execution. JFYI: the only similar question I have found here is not supplied with an applicable answer.
Thanks for any suggestions.
Plugin auditing is designed to register outside interactions with the server, being used to track invasion and other related activities, not interactions of the server with itself (like triggers and procedures).
These internal activities will not generate actions on any audit plugin by design. From the dev blog:
http://dev.mysql.com/doc/refman/5.6/en/audit-log-plugin-logging-control.html
The MySQL server calls the audit log plugin to write an element whenever an auditable event occurs, such as when it completes execution of an SQL statement received from a client. Typically the first element written after server startup has the server description and startup options. Elements following that one represent events such as client connect and disconnect events, executed SQL statements, and so forth. Only top-level statements are logged, not statements within stored programs such as triggers or stored procedures. Contents of files referenced by statements such as LOAD DATA INFILE are not logged.
For now, you are better with your homegrown solution. You could try to improve its performance so you can turn it on in the production environment.

Is there a way to run the trigger in separate thread in MySQL?

I have a PHP web application and when inserted in a table, I have written a trigger to calculate some scores and this takes time. My PHP application waits till the trigger completes.
Can this trigger run in a separate thread such that my application will not wait for the completion of the trigger?
No.
But as Bill Clinton would have said, "and I'll tell you why."
One of the big advantages of a trigger is that the actions of the trigger are contained within the transaction of the original statement that fired the trigger. This is a Good Thing from the Relational mindset because we like ACID compliance.
Now if you do not need that ACID compliance, and it is ok for the triggered actions to take effect "eventually", then remove the trigger and make an 'actions' table. Then either the trigger can drop something into the actions table or your PHP code might do it. You can have a job querying this table once/second or so, and if it finds anything it executes it. Don't worry about performance on the daemon, a once/second query of a table that is usually empty is nothing.

MySQL history or transaction log table updated by triggers

my question is about a database history or transaction log table which is currently updated by mysql procedure. The procedure is called by mysql trigger every time when we keep a history of an appropriate table in during insert, update or delete actions. As far as we have lots of tables for each of them we need to create a separate trigger e.g. for "accounts table" we need to create "accounts_insert, accounts_update and accounts_delete" triggers.
The problem is every time when we alter "accounts table" we have to modify appropriate triggers as well.
Is there any way to avoid that manual work? Would it be better to implement it in application layer/code?
There are no 'global' triggers if that's what you're thinking about.
Application side logging is one possible solution. You'll want to do this within transactions whenever possible.
Other possible approaches:
Create a script that will update your triggers for you. Can be fairly easy, if your triggers are generally similar to each other. Using information_schema database can be helpful here.
Parse general query log (careful, enabling this log can have large negative impact on server's performance)

BEFORE or AFTER trigger for maintaining audit log

I've been reading the MySql 5.0 comment stream on the create trigger page
and I would like to ask the community if the recommendations are good & whether they still apply to 5.1. What I've noticed playing with triggers today is that it is impossible to update a field in the old table using a AFTER UPDATE.
Be careful with BEFORE triggers. Constraints may occur, specifically if you are using InnoDB engine, where an insert will fail, but actions from your BEFORE trigger will succeed.
Use BEFORE triggers primarily for constraints or rules, not transactions, tweaking the NEW.* columns should be fine.
Stick with AFTER triggers for most other operations, such as inserting into a history table or updating a denormalization.
Yes. AFAIK, MySQL 5.1 did not make any changes to the semantics of how triggers work. MySQL tries to support the ANSI/ISO SQL specification for trigger semantics.
You can imagine there's a sequence of operations that runs as a row is written to the database:
Run BEFORE triggers
Evaluate constraints, enforce NOT NULL, apply DEFAULT values
Write the row to the database
Update indexes
Run AFTER triggers
Once you've reached the AFTER trigger, it's too late to change values in the row. In some databases, you can set NEW.somecolumn = 1234 but this change is silently discarded as the AFTER trigger finishes. In other databases, it helps you understand your mistake by giving you an error either when you define the trigger or when you run the trigger.
AFTER triggers are best used for extra actions to take as a result of INSERT/UPDATE of the row, such as the audit logging you mentioned. For one thing, MySQL only permits one trigger per action per table, so if you are also using a BEFORE trigger to change values and enforce business rules, now you can at least keep the extra actions in a separate trigger. That makes it easier to update one or the other.
The other consideration is that you should probably do extra actions only after you know the row has been saved successfully. E.g. it wouldn't be right to log a change in a BEFORE trigger, and then have the change abort because of a NOT NULL constraint.
For DELETE actions where you need to remove dependent rows in other tables, you may still have to do that in a BEFORE trigger.