Protect column, disallow update, only allow insert if NULL in MySQL - mysql

I want to protect existing dates in a date column from being overwritten. So disallow updates to the date column and only allow inserts if the existing field value is NULL (date column default is NULL). Are triggers the only way to accomplish this in MySQL? If so, would the trigger below work?
create trigger date_check
before insert, update on date
for each row
begin
if(date IS NOT NULL) then
SIGNAL 'date already set'
end if ;
end ;
Background: I have a table with critical dates that was accidentally changed due to user error. I put some checks in the user interface to prevent this from happening again but want another layer of safety directly with the database if possible.

Yes, in MySQL triggers are the only way to do this. MySQL does not support constraints.
Your trigger is not exactly right. First, you have update on date, but this should be update on <table name>. Second, you are checking the date value used for the update. Perhaps you mean:
create trigger date_check_update
before update on <the table name goes here>
for each row
begin
if (old.date IS NOT NULL) then
SIGNAL 'date already set'
end if ;
end;
An insert trigger on this condition doesn't make sense.

If anyone like me stumble upon this thread and is getting syntax error, it's because "When you try to raise errors via SIGNAL you need to specify the SQLSTATE which is the error code and for the user defined generic error codes its 45000 along with the message text MESSAGE_TEXT"
So the SIGNAL line should look like this.
signal SQLSTATE VALUE '45000' SET MESSAGE_TEXT = 'Your custom error message';
See this answer for more details.
https://stackoverflow.com/a/42827275/4164651

Just combining the above two answers, however, if you are writing triggers directly at the terminal, you'll have to change the delimiter before writing the trigger and then change it back once done.
delimiter $$
create trigger date_check_update
before update on <the table name goes here>
for each row
begin
if (old.date IS NOT NULL) then
signal SQLSTATE VALUE '45000' SET MESSAGE_TEXT = 'Your custom error message';
end if ;
end $$
delimiter ;

Related

MYSQL Trigger, is it possible to loop once and then disable field update?

I have a table called char and a column on that table called guild_id
I want to make it so that when a field in that column is updated once (default is 0, so from 0 to another number) it never allows it to be updated again either through a query_sql() or whatever way the application I am using is doing it in the source. Is this possible?
I have some logic here but I do not know the syntax -
CREATE TRIGGER guildcheck
BEFORE UPDATE ON guild_id
FOR EACH ROW
BEGIN
if(guild_id != 0) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Player is already in a guild';
END IF;
END;
Also, am I saving the trigger in the right location for MYSQL Workbench? When I run the lightning it saves somewhere?

MySQL Trigger - Don't accept record update if outside in range

I'm trying to create (what I think) should be a basic trigger.
Essentially if someone tries putting a value in for a record that is outside the range of the trigger, then it would refuse the update.
Table is called: People
Field concerned is: age
CREATE TRIGGER max_age_trigger
BEFORE UPDATE
ON People
FOR EACH ROW
BEGIN
IF People.age <0 OR People.age>150 THEN
CALL 'Error: The age is out of range (0 > 150)';
END IF;
END
MySQL is throwing an error at line 7. However i'm not understanding where i'm going wrong. I'm new to triggers and still getting my head around it.
If you could help/assist me in my code, that would be of great help :)
If you want to throw an error and stop processing, use SIGNAL:
CREATE TRIGGER max_age_trigger BEFORE UPDATE
ON People
FOR EACH ROW
BEGIN
IF People.age <0 OR People.age>150 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Error: The age is out of range (0 > 150)';
END IF;
END

INSERT INTO new MYSQL row only if all values are present

I'm trying to work out a MySQL query that only adds a new row to an existing table if all fields have a value present, otherwise it should be dropped/ignored.
The query I have at the moment is as follows:
INSERT INTO markers (`name`, `address`, `details`, `date`, `link`, `lat`, `lng`, `type`)
VALUES ("{1}", "{2}", "{3}", "{15}", "{4}", "{11}", "{12}", "{5}")
If {15} is left blank by my form then I don't want any of the other values added to the table.
Hope that makes sense!
Thanks
I would suggest you do a couple of different solutions.
You could setup your database to not allow null values, and then when you go to insert, handle your null errors (probably not the best solution out there though). If you need to have null values, then you will have to handle this in code.
The other thing is to implement form validation. You can do this with JavaScript, code behind, or even both. Both is suggested as some people may have JavaScript disabled on their browser.
I'd say check the inputs on the front-end with Java, C#, Swift, whatever. Then if an entry is blank, just make a pop-up message letting your user know to fill out every field, or for your application to just skip that record.
If you want to do this directly in MySQL, you can create a trigger that checks the values before they are inserted, and throw an error if some validation rule is not fulfilled.
Example:
delimiter $$
create trigger no_emtpy_values before insert into markers
for each row
begin
declare msg varchar(255);
declare condition_fail int default 0;
-- Write the appropriate validations and set condition_fail to 1
-- if the validations are not fulfilled. For example:
if NEW.`date` IS NULL then
set condition_fail = 1;
end if;
-- That "NEW" refers to the row about to be inserted
-- If the validation rules are not fulfilled, throw an error:
if condition_fail then
set msg = "You can't insert the data!";
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg;
end if;
end $$
delimiter ;
References:
"How to abort INSERT operation in MySql trigger?"
MySQL reference manual: CREATE TRIGGER Syntax
Hope this helps

Error in mysql syntax: Befor Update Trigger

I am setting up a master master replication and trying to do some tests by setting up a before update trigger.
I am getting an error when I run the code below
CREATE TRIGGER update_blogs
BEFORE UPDATE ON blogs
FOR EACH ROW
BEGIN
IF (NEW.updated_replication < OLD.updated_replication) THEN
SET NEW= OLD ;
END IF;
END$$
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near '' at
line 6
What I am trying to do is only allow the row to be updated if the new row has a greater updated_replication(timestamp) value.
I am using mysql.
Can any one please tell me where I am wrong. How can I debug such errors? Is this any kind of syntax error?
Two problems:
First problem: you can't SET NEW = OLD. You can only assign individual columns, not the whole row. So you could make sure the new value does not decrease:
IF (NEW.updated_replication < OLD.updated_replication) THEN
SET NEW.updated_replication = OLD.updated_replication;
END IF;
But that will set one column and let any other columns change according to the UPDATE that spawned this trigger. That might leave you with data that doesn't agree with itself.
If you want the whole row to revert to the old column values, you'd have to write a series of assignments in the SET statement, one for each column of the row.
If you instead want to abort the whole update, then you need to learn the SIGNAL feature.
IF (NEW.updated_replication < OLD.updated_replication) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'You can\'t travel backwards in time!';
END IF;
This doesn't roll back the transaction, just the single UPDATE that spawned the trigger. Any other changes made in the same transaction are still pending.
Second problem: you haven't set the DELIMITER when defining a trigger with a compound statement. See my answer here: Create function through MySQLdb
I don't think assignment of a database record with tableA = tableB is legal which is essentially what NEW=OLD is doing.
It would be better to do some other operation if the update is not desired:
CREATE TRIGGER update_blogs
BEFORE UPDATE ON blogs
FOR EACH ROW
BEGIN
IF (NEW.updated_replication >= OLD.updated_replication) THEN
SET NEW.invalid = True;
END IF;
END;$$
Where invalid is a column added just for this purpose. A periodically run stored procedure could then deal with such records, perhaps by deleting them.

mysql: Insert only if certain conditions are respected

I'm trying to find a way to check ,before adding a new tuple in a table, if the tuple respect some condition and in case of one of the conditions is not respected do not allow the insert.
I've thought of something like
DELIMITER //
CREATE TRIGGER t BEFORE INSERT ON Table
FOR EACH ROW
CALL CHECK1(…);
CALL CHECK2(…);
CALL CHECK3(…);
//
DELIMITER;
Where check1,check2,check3 are procedures that raise an exception if the NEW.(attributes) that I pass do not respect condition in the inserting table and/or with other tables.
Is this a correct and/or good way to make what I'm trying to do?
What is the best way to do that?
The best way to do it, is to do the data validation using stored procedures, instead of triggers. The trigger strategy is useful if you only want to filter incoming data. If the objective is to cancel an operation entirely when data values are unsuitable, you cannot do this in MySQL using a trigger.
I'm answering to reply(with a comment my answer would be incomprehensible) and to give more details:
I've used 2 strategies to make my goal, here 2 examples
1)if the check is easy
DELIMITER $$
create trigger RV5_1 before insert on Customer
for each row begin
IF(DATEDIFF(CURDATE(),NEW.birthdate)/365<18)
THEN
SIGNAL sqlstate '45006' set message_text = "too young to be a customer";
END IF;
END;
$$
DELIMITER ;
2) if the check is not easy and need cursors, variables etc
DELIMITER $$
create trigger T2 before insert on Table
for each row begin
IF (check1(NEW.[_some_attribute/s_]) or
check2(NEW.[_some_attribute/s_]))
THEN
SIGNAL sqlstate '45002' set message_text = "invalid insert";
END IF;
END;
$$;
DELIMITER ;
where check1 and check2 are stored functions that returns 0 if it's ok or 1 if there are problem with the new tuple.
Maybe someone with the same problem will found this helpful.