I'm looking for a way to prevent DELETE statement on a MySQL replication Master/Slave.
In my case, the Master is a live database, with fresh entries (not older than a week), and the Slave is an archive database which must contains all entries.
I have several problems with my test:
If I raise an exception in a Slave BEFORE DELETE trigger, like SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'DELETE canceled';
The SQL exception raise an error and stop my Slave.
If I raise a warning, the Slave keep running but the delete is not canceled
I can't modify the my.cnf of MySQL.
I can't use a boolean attribute for hide on master, show on slave (Master database must be as little as possible).
I rack my brain on this since a few days, and I 'm running out of ideas ...
You would be better off writing the deletes to an audit table.
Problem with preventing deletes in slaves is: if you try to insert a row with a pk which is already deleted in the master and if you have somehow prevented the delete in slave, the insert will fail in the slave.
You can track deleted rows in a different table with same structure.
http://www.techonthenet.com/mysql/triggers/before_delete.php
CREATE TRIGGER audit_before_delete
BEFORE DELETE
ON yourtable FOR EACH ROW
BEGIN
-- Find the deleted row and insert record into audit table
-- insert into yourtable_audit values (old.id, old.name, old.description);
END;
Related
I have an Aurora cross region read replica set up in AWS. It all works perfect at the moment but when I create a trigger on the master instance it causes a replication error on the slave instance. The code for the trigger is below.
CREATE TRIGGER `mydb`.`user_AFTER_UPDATE` AFTER UPDATE ON `user` FOR EACH ROW
BEGIN
END
The error I get is:
Error 'Access denied; you need (at least one of) the SUPER privilege(s) for this operation' on query. Default database: 'mydb'. Query: 'CREATE DEFINER=`myuser`#`%` TRIGGER `mydb`.`user_AFTER_UPDATE` AFTER UPDATE ON `user` FOR EACH ROW
BEGIN
END'
What's strange is that when I check the slave, the trigger is actually created. But because the error blocks up the replication I have to run the following to get it skip the error.
CALL mysql.rds_skip_repl_error;
I've tried setting log_bin_trust_function_creators to 1 but it doesn't seem to make any difference. When I run the following on the slave instance it says it's value is ON
SHOW GLOBAL VARIABLES LIKE 'log_bin_trust_function_creators';
Do I need to set that variable to 1 on the master instance as well?
Any help would be really appreciated. I've been Googling for hours but most of the stuff I find is talking about MySQL dumps which can have the definer removed but obviously that isn't possible in this case.
How can I detect into a trigger in my slave that it has been invoked by a replicated statement in MySQL?
I've tried with USER() function, but it returns null when the trigger is activated by a replicated statement. Shouldn't it returns replication user (repl)?
Example:
CREATE TRIGGER `test`.`t1_BEFORE_INSERT` BEFORE INSERT ON `t1` FOR EACH ROW
BEGIN
IF USER() LIKE 'repl#%'
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Custom error';
END IF;
END
I want the behaviour of the trigger to be different depending of it's activated by a replicated statement or not. I've configured statement based replication and I've tested that triggers run in my slave.
Try checking ##server_id. This is a global variable that must be unique for each MySQL instance in the replica-set.
Another option might be to change the trigger code on the replica. I don't usually like to do this, because I'd prefer that all schema objects are identical between the master and replica.
PS: You already know this, but for the benefit of other readers: note that replicated events do not execute triggers if you use row-based replication. This means you get mixed results if you used mixed-mode replication, because events will be logged in row-based format if the SQL is not safe for replication.
Re your comment:
Regarding testing the user, there is no user session in the replication thread. The replication thread is effectively runs without any limits with respect to the SQL privilege system (it can execute any change in the binary log stream), so there's no need for that thread to be associated with a user.
So it's not surprising that USER() returns NULL.
You could try testing for that:
IF USER() IS NULL THEN ...
By the way, there's actually no information in your question above that says that the trigger exists only on the replica.
I'm trying to issue the following in MySQL via node.js (using the https://github.com/mysqljs/mysql library).
I'm using the transaction API to rollback when an error happens but it doesn't appear to roll back. I next tried to simplify the problem and put it directly in PHPMyAdmin SQL box to do the following....
START TRANSACTION;
UPDATE users SET balance=balance + 1 WHERE id_user='someuserid'
ROLLBACK WORK;
I was expecting the user balance to remain at it's previous value (124) but instead it keeps adding one and shows an updated balance of 125 at the end of this.
Any idea what I'm doing wrong? Could it be the MySQL Db isn't supporting of transactions and/or is UPDATE like this allowed to be rolled back in transactions?
Ok, problem solved.
For reference for anyone else encountering this it was because of the table engine being used.
My table was using MyISASM which DOES NOT support transactions and fails silently. It autocommits on every transaction hence ROLLBACK never did anything.
The fix was to change the table engine from MyISAM to InnoDB (did via phpmyadmin but could also do it via
ALTER TABLE table_name ENGINE=InnoDB; sql command.
Now the following works perfectly.
START TRANSACTION;
UPDATE users SET balance = 8 WHERE id_user='s10';
UPDATE users SET balance = 9 WHERE id_user='s12';
ROLLBACK;
I am creating a php script for a MySQL database whereby I call a MySQL trigger..
The trigger should affect a table which is effectively an invoice:
So when I update a field called 'date_invoiced' from its NULL default to a valid date it then locks the whole record from being updated unless you have permission via your MySQL logon to change it back to its default NULL, (effectively 're-opening' the invoice)
No idea how to do this, any help would be great
You can't put a lock on a row. I suggest you use a TRIGGER on update, which makes the update fail if date_invoiced is NOT NULL. Unless username is 'superman'.
I think that you can code what you want following this example.
DELIMITER ||
CREATE TRIGGER upd_lock
BEFORE UPDATE
ON table_name
FOR EACH ROW
BEGIN
IF OLD.date_invoiced IS NOT NULL AND USER() NOT LIKE '\'superman\'#%' THEN
SIGNAL SQLSTATE VALUE '45000' SET MESSAGE_TEXT = '[upd_lock] - Record is locked';
END IF;
END;
||
DELIMITER ;
Adding triggers is essential for the development of complex MySQL databases that retain enforced referential integrity. Foreign keys cannot handle complex cases that perhaps involve more than one column (such as an item_id and item_type_id scenario).
SUPER is required when creating or dropping trigger only when binary logging is turned on.
The reason appears to be related to replication issues (MySQL 5.0 documentation).
RTM.and RTM
Read this link to ... & this threads Applying column permissions for a table over a trigger , Can't create MySQL trigger with TRIGGER privilege on 5.1.32
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.