Protecting database data - mysql

Currently I'm using a MySQL database. Let's say that I have some data regarding last month. When the data has been verified I want to "lock" it, so when my boss asks me if the data is correct, I can answer yes, because I'm certain that the data couldn't have been altered in the mean time.
Is there a way to "lock" data rows or cells from being modified? How could I do that?

Mysql innoDB lock may works, but is not permanent I think (related to transactions). Check yourself here
I would suggest you add an extra column in your table, name it for eg. validated with default value 0. Then when every rows are validated, you change validated value to 1. If so, you can add a trigger to check update on this current table.
delimiter $$
CREATE TRIGGER updateRestrictionOnMyTable
BEFORE UPDATE ON my_table
FOR EACH ROW
BEGIN
IF OLD.validated > 0 THEN
SIGNAL SQLSTATE VALUE '99999'
SET MESSAGE_TEXT = 'You cannot update validated rows';
END IF;
END$$
delimiter ;

Related

MySql cannot update a DATETIME field - no record found

I have a table in MySql 5.6.10 defined as this:
When I do a select query (from HeidiSQL client) for a particular record filtering on the id_failed_delivery_log column, the record is found successfully, but when I use the same filter for the UPDATE command, then no record is found and no error reported:
When I update a different column using the same filter, the update works and I can see the updated value. Then there is an issue with an update to this particular column.
I've also tried updating with a time function instead of hard-coded date value, for example with the now() function but I still get the same result and the record is not found.
Could it be caused by the 'Default' value is set to CURRENT_TIMESTAMP?
After further investigation I found the reason why I couldn't update that field. I was not totally familiarized with the database definition and I found that there was a trigger in the same schema that forced to keep the date_created column with the same value:
SET #OLDTMP_SQL_MODE=##SQL_MODE, SQL_MODE='STRICT_ALL_TABLES';
DELIMITER //
CREATE TRIGGER `failed_delivery_log_before_update` BEFORE UPDATE ON
`failed_delivery_log` FOR EACH ROW BEGIN
SET NEW.date_created = OLD.date_created ;
END//
DELIMITER ;
SET SQL_MODE=#OLDTMP_SQL_MODE;
I removed this trigger temporarily in order to test. Once removed, the updated worked fine. The trigger execution was not reported in the SQL client, so it was difficult to find out its execution.

How to create MySQL Table with no Insert allowed

In MySQL i need a table that stores only one static value, which the application can access and change if needed. For this purpose i have to make sure there can't be another row inserted.
So i need to create a table that allows update operations, but no insert and no delete operations.
Is there another way than checking the size of the table in a trigger and if > 1 cancel the operation?
This can be controlled by specifying the desired privileges for the mysql user used to interogate the table .
For example you can create the table and insert the values (for one time) with one user then use another user that has only update privileges.
You can read more here
As mentioned by Stephan a possible Solution is by creating another User and specifying such privileges that rows from that table can't be inserted or deleted. I think it's a good solution but since I'm accessing multiple tables via the existing user and i didn't wanted to create another one, i solved the problem with two triggers.
So here's my solution:
Trigger: On_Insert_Check_Count
BEGIN
IF ((SELECT COUNT(*) FROM oe_last_tabkey) >= 1) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "DIE: Only Update allowed on this table.";
END IF;
END
Trigger: On_Delete
BEGIN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "DIE: Only Update allowed on this table.";
END
With this solution, everytime any User tries to insert (as long as there is min 1 row existant) or to delete, MySQL will cancel the Operation with the Message: "DIE: Only Update allowed on this table".

How to add many constrains to a mysql database

I am trying to set up a MySQL database. The final database will consist of approx. 200 columns distributed over 7 tables. Now I’ve got the problem that I’d like to add check constraints to most of the columns. Some columns will only have one constraint, others will have many and/or are affected by columns of different tables.
E.g. rows can only be added to a table if the age is older than 20 years or if the zip code consists of 5 characters and starts with 1 or if the admission date (in table admission) is before the discharge date (in table discharge).
Just three examples. I can think of 1000 more, which is my problem.
I’d like to add these constraints in a more structured way. A trigger with more than 10 constraints will be complex and no one, except for the author will be able to reconstruct all constraints. But if the database will be used for years, many administrators will have to work with the database and maybe add new or remove unnecessary constraints.
DELIMITER $$
CREATE TRIGGER `test``_before_insert` BEFORE INSERT ON `zip`
FOR EACH ROW
BEGIN
IF NEW.zip_id < 5 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint 1';
END IF;
IF NEW.zip_id = 5 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint 2';
END IF;
END $$
DELIMITER ;
That's the way I don't want to handle the problem. 
My question is, if there is a way to declare constraints in a normal-human-readable-way (e.g. one constrain with a nice name per textfile) and add these to the database.
Unfortunately, the check constraint in MySQL does not work. You can learn more at this SO question. Even the accepted answer for this question suggests the use of triggers.
You are on the right track when you decided to use triggers. However you have a few misconceptions.
A trigger is basically defined on an event on a table. As you have 7 tables, you will have 7 separate triggers per table, at least. Read more about triggers in MySQL at MySQL Reference Manual.
It is you and you only that has to find ways to keep the code well organized so that your teammates and even you can understand it well. Everything that you need to do is follow the best practices followed across the software industry. For a few hints you can refer here and here on dba.stackexchange.com.
I have thought about my problem over, and have another argument and solution about the checking of constrains in a trigger.
First, the argument: I probably need at least a BEFORE INSERT and a BEFORE UPDATE trigger to check my data. Both triggers are more or less identical. If a constrain changes over time, I have to submit the change to both triggers or my data can be come inconsistent.
I thought of stored procedures to implement one constrain, which can be called in the INSERT and UPDATE trigger. Now If I change a procedure, it affects both triggers.
DROP TRIGGER IF EXISTS test_before_insert;
DROP PROCEDURE IF EXISTS `procedureLess5`;
DROP PROCEDURE IF EXISTS `procedureEquals5`;
DELIMITER $$
CREATE PROCEDURE `procedureLess5` (IN myInput INT)
BEGIN
IF myInput < 5 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint 1';
END IF;
END $$
CREATE PROCEDURE `procedureEquals5` (IN myInput INT)
BEGIN
IF myInput = 5 THEN
SIGNAL SQLSTATE '12345'
SET MESSAGE_TEXT = 'check constraint 2';
END IF;
END $$
CREATE TRIGGER `test_before_insert` BEFORE INSERT ON `zip`
FOR EACH ROW
BEGIN
CALL procedureLess5(NEW.zip_id);
CALL procedureEquals5(NEW.zip_id);
END $$
DELIMITER ;
Is this an opportunity?
How about the performance of my database if I have more than 20 called procedures?
Or do you think of another way? I’m not fixed to MySQL, PostgreSQL is also an opportunity. Is it better/faster/more standard to use CHECK CONSTRAINS in PostgreSQL?

creating a trigger to lock and unlock a record

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

MySQL Trigger: compare record with previous record before insert

When I insert data in db I have to compare the current record with the previous one. If neccassary, some values of the current record needs to be modified.
I've tried some pieces of SQL like below, but all give SQL errors. This one gives me an error says that I select more than 1 records.
DELIMITER $$
CREATE
TRIGGER set_moment_display
BEFORE INSERT ON data
FOR EACH ROW
BEGIN
DECLARE moment DATETIME;
SELECT press_moment_1 INTO moment FROM data LIMIT 1;
IF moment > NEW.press_moment_1 THEN SET NEW.press_moment_1 = moment;
END IF;
END$$
DELIMITER ;
How do I achieve what I've described above.
The problem here is that, since a SQL database has no implicit concept of row ordering (you supply the ordering criteria on every query), there is no "previous" row for the trigger to look at. The "previously inserted row" has no meaning in the context of an insert trigger.
Suppose for a moment that it did and there were several processes inserting rows in the table. When the trigger fired for process #1's insert, which row is the "previous" row? The one previously inserted by process #1? Suppose the chronologically "most recent" row was actually inserted by process #3?
If you need to do this it cannot be done in a trigger unless you can use a know key value to identify the row you understand as "most recent". Otherwise it must be handled in the application that is doing the inserts.
You can use the alias "OLD."
You can refer to columns in the subject table
(the table associated with the trigger) by using the aliases OLD and NEW.
OLD.col_name refers to a column of an existing row before
it is updated or deleted. NEW.col_name refers to the column
of a new row to be inserted or an existing row after it is updated
UPDATE
Jim Garrison properly pointed up to me the mistake, "BEFORE INSERT" doesn't have "OLD." values, this alias works only for UPDATE and DELETE.