I'm creating a trigger in MySQL to let me know WHO (which mysql user) is doing an update on a specific table.
I know MySQL has a function CURRENT_USER(), but is that going to insert the username at the time the trigger is CREATED, or at the time the trigger is CALLED?
This is my trigger so far. I want to insert the username in the 'content' column.
delimiter |
CREATE TRIGGER update_product_procedure
BEFORE UPDATE ON product_procedure
FOR EACH ROW BEGIN
INSERT INTO trigger_logs SET
content = 'This is a test', postDate=NOW();
END;
|
I would have put lots of money on it being the invoker, but from http://bugs.mysql.com/bug.php?id=5861:
SQL standard says that: "A triggered action is always executed under
the authorization of the owner of the schema that includes the trigger." This means that
in MySQL we should execute trigger body under authorization of user who
created trigger and not the one who issued statement which invoked this trigger.
Apologies, I assumed it was an obvious question :-(
Regards
EDIT: user() gives the invoker
Related
I'm writing a query that creates a trigger to soft delete a row in the table customer using the a flag called "IsDelete" when the flag is '0' it is not deleted and when the flag becomes 1 the row has been marked as deleted.
When the query is run the error code 1442c is generated. can anyone explain why??
DELIMITER $$
CREATE TRIGGER SOFT_DELETE_TRIGGER
BEFORE DELETE ON customer
FOR EACH ROW
BEGIN
IF OLD.IsDelete = 0 THEN
CALL cannot_delete_error;
UPDATE customer
SET IsDelete = 1;
END IF;
END
$$
Deleting a row in the table to test the trigger.
DELETE FROM customer
WHERE C_username = 'testuser'
Yes, triggers don't allow you to INSERT/UPDATE/DELETE against the same table that spawned the trigger, because that could run invoke the trigger again, and then that trigger might do another INSERT/UPDATE/DELETE, and so on. You have too high a chance of causing an infinite loop.
I'm afraid MySQL doesn't support an "instead of" trigger like some other brands of SQL database do. You can't make a trigger on DELETE that does an update instead.
You can use SIGNAL to make it throw an error, and that blocks the DELETE, but it doesn't do an UPDATE instead.
To implement soft deletes as you are doing, you'll just have to make the client use UPDATE instead of DELETE.
In Oracle, when writing a unique trigger that handles INSERT, UPDATE OR DELETE operations, when can detect which operation is being executed using this technique, called 'Conditional Predicates'
create trigger sample_trigger
before insert or update
on sample_table
for each row
begin
case
when inserting then
--do something
when updating then
--do something
end case;
end;
Does MySQL 5.6 provides any technique that allows me to do the same? I wouldn't like to write three distinct triggers only to differentiate which operating is being executed. Thanks!
No. In MySQL (even in 8.0) triggers can only be called for one type of operation, INSERT, UPDATE or DELETE. From the manual, the syntax for CREATE TRIGGER is:
CREATE
[DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
...
and trigger_event may only be one of INSERT, UPDATE or DELETE:
trigger_event: { INSERT | UPDATE | DELETE }
So given that there is no scope to call a trigger for different operations, there is no need to have the conditional predicates you describe.
What you could do instead in put your trigger code into a stored procedure, and then call that with a parameter which specifies whether the type of operation e.g. for an INSERT trigger you might use
CREATE TRIGGER db_insert
BEFORE INSERT ON db
FOR EACH ROW
BEGIN
CALL db_trigger_proc('INSERT');
END
Unfortunately you can't.
You can see that the documentation states that you can use only one event to fire the trigger.
Syntax:
trigger_event: { INSERT | UPDATE | DELETE }
that means you can choose only one event as a trigger_event.
another argument on the same doc :
There cannot be multiple triggers for a given table that have the same trigger event and action time. For example, you cannot have two BEFORE UPDATE triggers for a table. But you can have a BEFORE UPDATE and a BEFORE INSERT trigger, or a BEFORE UPDATE and an AFTER UPDATE trigger.
I am optimizing my login system in which I am maintain previous passwords of a user, schema of pwd table for maintaining passwords is as follows:
here userId refers to primary key of user table.
If status attribute is 1 that means row has marking current password if it is 0 previous password.
How can I write a trigger so that if a new entry for any user is made in pwd
table, all previous status of that user gets set to 0 and new value will remain 1 as the default value of status attribute. Currently I am doing so at application level.
It won't be possible because it would require to have a mutating (changing the same table it's being fired upon) trigger which is prohibited in MySQL.
You can create a stored procedure though, but you would still need to call it from the client code explicitly.
Now a better schema design would be to keep all current passwords in one table (presumably in the users table) and all previous in the other (i.e. pwd_history). Not only it makes queries for current passwords faster it also allows you to use a trigger if you choose to.
DELIMITER //
CREATE TRIGGER tg_pwd_history
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
IF NOT NEW.pwd <=> OLD.pwd THEN
INSERT INTO pwd_history (`userId`, `pwd`, `ts`)
VALUES (NEW.id, OLD.pwd, NOW());
END IF;
END//
DELIMITER ;
Here is a SQLFiddle demo
Now I very much hope you're not storing them in plain text.
I have to limit table record to 25. after I just delete everything (in the future will modify it to delete just oldest rows)
DELIMITER //
CREATE PROCEDURE p1 () delete FROM tests;//
CREATE TRIGGER trigger1
BEFORE INSERT
ON tests
FOR EACH ROW
BEGIN
SELECT COUNT(*) INTO #cnt FROM tests;
IF #cnt >= 25 THEN
CALL p1();
END IF;
END
//
DELIMITER ;
, but I am getting error:
Can't update table 'tests' in stored function/trigger because it is already used by statement which invoked this stored function/trigger
So I can not add any more fields.
The MySQL trigger FAQ sais that you cannot modify the table that calls the trigger.
But you can set up a cron job, or CREATE EVENT in MySQL that cleans the table at regular intervals. (CREATE EVENT needs the PROCESS privilege, and a running event_scheduler. The event_scheduler is turned off by default: it can be turned on from SQL console, but the MySQL config must be modified to ensure that it starts when MySQL restarts.)
It seems that you can't update or delete from the same table which invoked the trigger.
But you can work around this by creating another table, for example tests2 and instead of inserting into tests just insert into tests2, and have the trigger insert the NEW. values into tests, then you can count from tests and delete from tests like how you want it.
see this sqlFiddle
The problem with this is then tests2 gets filled up with Inserted data So you might have to manually delete from tests2.
I am trying to create a MySQL Trigger to disable someone's account if they have logged in to the site 3 times. I have tried to create this trigger using the following code, but it is not setting is_active to 0 no matter what times_logged_in is. Any help would be appreciated.
CREATE TRIGGER updateTrigger AFTER UPDATE ON users
FOR EACH ROW
BEGIN
UPDATE users SET is_active=0 WHERE NEW.is_code=1
AND NEW.times_logged_in>=3
AND NEW.user_id=user_id;
END;
You've hit a limitation of MySQL. Your table users invokes the trigger (AFTER UPDATE ON users), therefore the triggered code cannot modify it. See the MySQL-Manual:
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.
I'm not sure where user_id comes from in your trigger, looks like an extraneous check, try this:
CREATE TRIGGER updateTrigger AFTER UPDATE ON users
FOR EACH ROW
BEGIN
UPDATE users SET is_active=0 WHERE NEW.is_code=1
AND NEW.times_logged_in>=3
END;
Also, be sure that is_code = 1 is actually matching on the users you're updating, if it's not, it won't update any rows.
I would use a BEFORE UPDATE trigger instead:
CREATE TRIGGER updateTrigger BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF NEW.is_code=1 AND NEW.times_logged_in>=3 THEN
SET NEW.is_active=0;
END IF;
END;