MySql trigger not doing any action - php

It's probably something stupid but I'm unable to figure it out.The issue is that when i want to perform a check on email_token table,It's not passing.I have a simple query to update the needed data.But every time i get a false on my query.
Here is it:
DELIMITER $$
CREATE TRIGGER email_token_used
BEFORE INSERT
ON email_token FOR EACH ROW
BEGIN
IF(EXISTS(SELECT * FROM email_token WHERE used = 'N' AND usable = 'Y' AND user_id = NEW.user_id)) THEN
UPDATE email_token SET usable='N' WHERE user_id = NEW.user_id;
END IF;
END$$
Here is my table:
Here is the php if needed.It's working without the trigger ,the mail can't be sent since it's not updated in the table:
if(!password_verify($password,$output['password'])){
self::$status = 401;
echo "Pass 1";
}else{
$mail_result = self::$db->query("INSERT INTO email_token(user_id,token,created,expires,used) VALUES('{$output['user_id']}','{$token}',NOW(),DATE_ADD(NOW(), INTERVAL 30 MINUTE),'N')");
if($mail_result){
echo "Pass 2";
$mailer = new Mailer();
$mailer -> send_mail("Token authentication","Token authentication <a href='http://localhost:8888/scripts/functions/collector/login/login_step_2.php?user_id={$token}'>Link</a>",$output['email']);
}else{
self::$status = 409;
echo "Error 1";
}
}
Explanation of the final goal here.
The user logs in,he/she gets a token that is being sent to their email(like a 2 stop verification system).After they get the token,they finish with the login in process.I want to prevent people from having multiple tokens,and if they login several times ,they will get the token on their email.The tokens last for 2min,and i want to update the tokens before to be not valid.
Thanks...

You have run afoul of
A stored function or trigger cannot modify a table that is already
being used (for reading or writing) by the statement that invoked the
function or trigger.
https://dev.mysql.com/doc/refman/5.7/en/stored-program-restrictions.html
I am not sure why you need a trigger here in the first place. It seems to be that you have a situation where INSERT ON DUPLICATE KEY UDPATE can be used. Something like
INSERT INTO email_token(user_id,token,created,expires,used)
VALUES(?,?,NOW(),DATE_ADD(NOW(), INTERVAL 30 MINUTE),'N')
ON DUPLICATE KEY UPDATE usable = 'Y'
Note that you are using string concatenation in your queries. You should be using prepared statements instead.
Based on your comments. It sounds like all you need is a simple INSERT statement (The record needs to be inserted every time). Preceded by an UPDATE statement
UPDATE email_token SET usable='N' WHERE user_id = ?
INSERT INTO email_token(user_id,token,created,expires,used)
VALUES(?,?,NOW(),DATE_ADD(NOW(), INTERVAL 30 MINUTE),'N')
First one invalidates existing records. Second one creates a new record. Still no need for a trigger.

Try it like this:
DELIMITER $$
CREATE TRIGGER email_token_used
BEFORE INSERT
ON email_token FOR EACH ROW
BEGIN
IF(EXISTS(SELECT * FROM email_token WHERE used = 'N' AND usable = 'Y' AND user_id = NEW.user_id)) THEN
BEGIN
UPDATE email_token SET usable='N' WHERE user_id = NEW.user_id;
END;
END IF;
END$$

Related

Lock a Select on the Postgres Database and updating the Column of "value+1" when necessary

I am updating this question, please do not mind the comments below as, instead of deleting this question, I reworked it to give it a sense.
A form on a php page let me create a csv file, to name this file I need to run a SELECT on the database, it the name does not exists, my query must create it; if the name exist, it must update it.
The problem is, there is a chance where 2 or more users can push the submit button at the same time.
This will cause the query to reurn the same value to all of them, therefore creating or updating the file in a non-controlled way.
I need to create a system, that will LOCK the table for INSERT/UPDATE and, if in the meantime another connection appear, the column on the database that will name the file must be incremented of +1.
$date = date("Ymd");
$csv = fopen("/tmp/$user_$date_$id_$reference.csv", 'w+');
Where "reference" is a progressive number in the format of "Axxxx". x's are numbers.
The SELECT would be:
$sql = pg_query($conn, " SELECT user, identification, reference, FROM orders WHERE identification = '$_POST[id_order]' ORDER BY date DESC LIMIT 1");
while ($row = pg_fetch_row($sql)) {
$user = $row[0];
$id = $row[1];
$reference = $row[2];
}
I need to create a function, like the one below, where users can both INSERT and UPDATE, and in the case of concurrent connection, the ones that are not the first will have "reference" incremented of 1.
CREATE OR REPLACE FUNCTION upsert_identification( in_identification TEXT, in_user TEXT ) RETURNS void as $$
BEGIN
UPDATE table SET identification=in_identification, user=in_user, reference=in_reference WHERE identification = in_identification;
IF FOUND THEN
RETURN;
END IF;
BEGIN
INSERT INTO table ( identification, user, reference ) VALUES (in_identification, in_user, in_reference );
EXCEPTION WHEN OTHERS THEN
-- Should the increment be here?
END;
RETURN;
END;
$$ language plpgsql;
I hope what I'm asking is clear, re-read and I do understand it. Please comment below for any question you might have.
I really hope someone can help me!
I was looking for some clues in the postgres manual, I found this link about locking but I am not so sure this is what I need: LINK

Insert ID field generated from trigger, but not passed along

In MySQL, I have a trigger:
BEGIN
IF (EXISTS(SELECT * FROM devices WHERE device_id = NEW.device_id)) THEN
SET NEW.id = NULL;
ELSE
INSERT INTO objects (object_type) VALUES ('3');
SET NEW.id = LAST_INSERT_ID();
END IF;
END
When this trigger gets a new id (from the objects table) it inserts the id into the id column of the devices table.
When I refer to it (for example with mysql_insert_id(); in PHP), its empty.
How can I return the insert id from the trigger (LAST_INSERT_ID();) to the function in PHP as the mysql_insert_id(); ?
Personally I use stored procedures.
Here is a basic example with PDO:
Code to create the Stored Procedures:
CREATE DEFINER=`user`#`localhost` PROCEDURE `InsertUser`(IN `Input_username` INT, OUT `Out_ID` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
INSERT INTO users(
username)
VALUES (
Input_username);
SET Out_ID = LAST_INSERT_ID();
SELECT Out_ID;
END
And PHP code:
$insert = "CALL InsertUser(:Input_username,
#Out_ID)";
$bdd = new PDO('mysql:host=localhost;dbname=db-name', 'user', 'password');
$stmt = $bdd->prepare($insert);
$stmt->bindParam(':Input_username', rand(), PDO::PARAM_STR); // to create random name
$stmt->execute();
$tabResultat = $stmt->fetch();
$id_user = $tabResultat['Out_ID'];
var_dump($id_user);
I hope I have helped. :)
This behaviour is by design:
If a stored procedure executes statements that change the value of LAST_INSERT_ID(), the changed value is seen by statements that follow the procedure call.
For stored functions and triggers that change the value, the value is restored when the function or trigger ends, so following statements will not see a changed value.
Workaround 1: Stored Procedures
Unfortunately this introduces a risk of inconsistencies between your table and objects, as insertions could still happen outside of this procedure (this problem could be adressed with convoluted access restrictions on the table)
Workaround 2:
Save the value in a user variable:
CREATE TRIGGER
....
BEGIN
INSERT INTO objects (object_type) VALUES ('3');
SET NEW.id = LAST_INSERT_ID();
SET #myLastInsertID = LAST_INSERT_ID();
END //
INSERT INTO your_table... -- trigger the above
SELECT #myLastInsertID; -- here is your value
Workaround 3:
Simply get the value from object ;)
INSERT INTO your_table... -- trigger the above
SELECT MAX(autoinc_column) FROM objects; -- here is your value!
Workarounds 2 and 3 should be wrapped in a transaction to ensure no-one interferes with #myLastInsertID or object during the process.

mysql default value NULL "unchageble" - is it possible?

I am using a licensed software for my website, and of course the engine is encrypted. i have no access and modify to any php code...
It has a feature that i want to change, and i can't modify the php code, since the vendor doesn't allow me to do it...
The only option is to build a separate custom script and run it separately to update fields in database (which is not suitable for my project)
OR to change the values manual in mysql database (again it's not suitable)
There is a third option, and i don't know if it's really possible....
here's the scenario:
The script ads a new row in database whenever the user click on a particular link. That row has 10 fields. last 4 fields are NULL by default and they should stay NULL.
The script insert values in that last 4 fields, and it's unusual, and of course the script is not working properly anymore.
So my question is: Is there any way for me to prevent the insertion of the values for that 4 fields in database ? Can it be locked to NULL ? Can "SET / UPDATE" function be ignored for that fields?
Options:
During insert do not use those columns to insert into.
Write before insert trigger to reset to null those new column values.
Writer before update trigger to reset to null those new column values, based on a where condition.
Update:
If you do not have access to your php code to modify the insert statement, you can only achieve this by defining triggers in database. For this to happen, you should at the least have various privileges like remote connect, create, execute triggers, etc. Unless which you can't do this.
If you have such privileges, you can try on your data table something similar to the following:
before insert trigger as below:
delimiter $$
drop trigger if exists bfimt_omit_colum_data $$
create trigger bfimt_omit_colum_data before insert on my_table
for each row begin
set NEW.col_name_4_to_set_null = NULL,
NEW.col_name_5_to_set_null = NULL,
NEW.col_name_6_to_set_null = NULL;
end;
$$
delimiter ;
Similarly the before update trigger as below:
delimiter $$
drop trigger if exists bfumt_omit_colum_data $$
create trigger bfumt_omit_colum_data before update on my_table
for each row begin
set NEW.col_name_4_to_set_null = NULL,
NEW.col_name_5_to_set_null = NULL,
NEW.col_name_6_to_set_null = NULL;
end;
$$
delimiter ;
You could define a trigger like this:
delimiter //
CREATE TRIGGER set_to_null_bi BEFORE INSERT ON tablename
FOR EACH ROW
BEGIN
SET new.col4 = NULL;
SET new.col5 = NULL;
SET new.col6 = NULL;
END//
delimiter ;
and you could also create a BEFORE UPDATE trigger.
Thanks all for your help, i can't say you codes are not correct, but hey didn't work for me. I manage to solve this by myself, here's the code:
DROP TRIGGER IF EXISTS `trigger_null`;
CREATE DEFINER=`root`#`localhost` TRIGGER `trigger_null` BEFORE INSERT ON `mytable` FOR EACH ROW
SET new.create_action = NULL,
new.col4 = NULL,
new.col5 = NULL,
new.col6 = NULL,
new.col6 = NULL,
new.col7 = NULL,
new.col8 = NULL;
That's all...
Thanks for your reply's !!!

mysql_affected_rows(); does not work for checking if row exists

i am using mysql_affected_rows() to check if i have to enter new record or update existing, but the problem is if the user tries to enter exactly same data as record which already exists it runs insert into.
$result = mysql_query("update Data set Score='$score',Comment='$_POST[Comments]' where Date='$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]' AND User='$_POST[UserID]';");
$last = mysql_affected_rows();
if ($last==0) {
$result1 = mysql_query("INSERT INTO Data (User,Date,Score,Comment) VALUES ('$_POST[UserID]','$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]','$score','$_POST[Comments]')");
what should i do to avoid redundant entries
You could parse mysql_info() output (but the solution itself will be affected by race condition issue)
You could create unique key User + Date and end up with a single query using ON DUPLICATE KEY UPDATE syntax:
INSERT INTO `Data` (User,Date,Score,Comment)
('$_POST[UserID]','$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]','$score','$_POST[Comments]')
ON DUPLICATE KEY UPDATE Score='$score',Comment='$_POST[Comments]'
some solutions:
add another query to see if data exists, and then decide if you want to do some action (update/delete) or nothing.
add a 'modified' column with type "TIMESTAMP" and make it on update - CURRENT_TIMESTAMP
i'd go with first option.
btw, you should escape your post data (mysql_real_escape_string) to prevent injects or malformed query string
You may get the number of affected rows with FOUND_ROWS() instead of mysql_affected_rows(). The latter counts the not modified rows as well.
$result = mysql_query("update Data set Score='$score',Comment='$_POST[Comments]' where Date='$_POST[forDay_3]-$_POST[forDay_1]-$_POST[forDay_2]' AND User='$_POST[UserID]';");
$last = mysql_query("SELECT ROW_COUNT();");
$last = mysql_fetch_array($last);
...
Reference: http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_row-count

mysql Trigger On insert

whats wrong with my syntax?
CREATE
TRIGGER db_dhruniversity.trigger1
AFTER INSERT
ON jos_dhruprofile
FOR EACH ROW
BEGIN
UPDATE jos_users
SET jos_users.department = jos_dhruprofile.department
WHERE jos_users.id = jos_dhruprofile.uid
END
The syntax should be as follows:
DELIMITER $$ /* if you're not using an editor, you must change the delimiter*/
CREATE
TRIGGER ai_jos_dhruprofile_each
AFTER INSERT
ON jos_dhruprofile
FOR EACH ROW
BEGIN
UPDATE jos_users
SET jos_users.department = NEW.department
WHERE jos_users.id = NEW.uid; /*<<<--- ; after every stament */
END $$ /* changed delimiter after the end */
DELIMITER ; /*make sure you set the delimiter back to the default*/
Note on the naming scheme for triggers
I'd recommend naming your trigger ai (meaning after insert) so you know when it fires on which table, rather than a meaningless name like: db_dhruniversity.trigger1.
I always use [a/b]+[d/i/u]_tablename_each as the triggername, that way I always know when the triggers fires (before/after) for which event (insert/delete/update) and on which table.
It's also good practise to document that the trigger fires on each row, hence the each on the end of the trigger name.
Note that MySQL does not support triggers that fire once per statement yet (But that might change in future).
There are no delimiters in it:
DELIMITER ||
CREATE
TRIGGER db_dhruniversity.trigger1
AFTER INSERT
ON jos_dhruprofile
FOR EACH ROW
BEGIN
UPDATE jos_users
SET jos_users.department = NEW.department
WHERE jos_users.id = NEW.uid;
END ||
DELIMITER;

Categories