creating a trigger to lock and unlock a record - mysql

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

Related

MYSQL trigger before delete (instead of deleting rather update

I have two tables, one containing candidate information and the other containing the personal information of everyone considered as a person in the database. However, a candidate should not be deleted from the database, their non-personal data should be kept for record. The deletion of a candidate must result in the deletion of his personal data only. So i am trying to write a before delete trigger on the candidate table that takes the id of the candidate we are trying to delete and sets all their personal info to null. When i run the trigger, it only returns a msg saying candidate was deleted however when i check the personal info table, the row for that candidate still has all the information. what might i be doing wrong?
here is the code for the trigger:
USE `agence_interim`;
DELIMITER $$
DROP TRIGGER IF EXISTS agence_interim.candidat_BEFORE_DELETE$$
USE `agence_interim`$$
CREATE DEFINER = CURRENT_USER TRIGGER `agence_interim`.`candidat_BEFORE_DELETE` BEFORE DELETE ON `candidat` FOR EACH ROW
BEGIN
DECLARE errorMessage VARCHAR(255);
Update personne SET personne.id_personne = null,
personne.nom = null,
personne.prenom = null,
personne.email = null,
personne.telephone =null,
personne.date_naissance =null,
personne.description = null
WHERE personne.id_personne = OLD.id_personne;
SET errorMessage = CONCAT('The personal information for candidate number ',
OLD.id_personne,
' has been deleted');
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = errorMessage;
END$$
DELIMITER ;
I'm afraid this is not possible using a trigger in MySQL.
Using a SIGNAL statement in the trigger body aborts the DELETE action that spawned the trigger, but when the trigger is aborted, this also cancels all subordinate changes executed within the trigger body (and also any actions performed by triggers spawned by those changes, etc.).
There is no support for "instead of" triggers in MySQL. Either all changes succeed, or none of them succeed.
To do what you want, you can't use DELETE from the client.
You must use UPDATE.

Protecting database data

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 ;

How to rollback all statements inside a MySQL Transaction?

I need to update a specific column of a table (bigtable) containing ids of another table (FK constraint to oldsmalltable) to point to ids on another table (FK constraint to newsmalltable). Basically this is what I am doing:
DELIMITER //
CREATE PROCEDURE updatebigtable ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING ROLLBACK;
START TRANSACTION;
ALTER TABLE bigtable DROP FOREIGN KEY bigtable_ibfk_1,
MODIFY smalltable_id SMALLINT ;
UPDATE bigtable SET smalltable_id=CASE smalltable_id
WHEN 1 THEN 1592
WHEN 2 THEN 1593
WHEN 3 THEN 1602
...
ELSE 0
END;
ALTER TABLE bigtable ADD CONSTRAINT bigtable_ibfk_1
FOREIGN KEY(smalltable_id) REFERENCES newsmalltable(id);
COMMIT;
END//
DELIMITER ;
CALL updatebigtable();
DROP PROCEDURE updatebigtable;
I need to ensure that if by some reason the new Foreign Key constraint fails (e.g. with columns with different types, the error would occur on the last alter table statement), the UPDATE and the first ALTER TABLE should be rolled back as well, i.e. they should remain as they were initially.
According to MySQL documentation, by using START TRANSACTION the autocommit mode is disabled for that transaction, which will not allow:
that as soon as you execute a statement that updates (modifies) a table, MySQL stores the update on disk to make it permanent.
I only found this question as minimally related to mine:
How can I use transactions in my MySQL stored procedure?
If that error I mentioned occurs inside the transaction, the previous statements were already executed and the updates were "permanently done on disk"...
I also tried to place SET autocommit=0; before creating the procedure but the behavior is still the same... Am I missing something? Or is this the expected behavior of a MySQL transaction rollback?
If it makes any difference, I am using MySQL v.5.6.17.
ALTER TABLE statements always cause an implicit commit (section 13.3.3 from MySQL docs, thanks wchiquito), which means that even if they're inside a START TRANSACTION; ... COMMIT; block, there will be as many commits as the number of alters done inside that block.
Locking the table is not an option as well since (from problems with ALTER TABLE):
If you use ALTER TABLE on a transactional table or if you are using Windows, ALTER TABLE unlocks the table if you had done a LOCK TABLE on it. This is done because InnoDB and these operating systems cannot drop a table that is in use.
The only option left for avoiding unwanted reads/writes while the alter and update statements are being executed is emulating all the steps of an ALTER TABLE:
Create a new table named A-xxx with the requested structural changes.
Copy all rows from the original table to A-xxx.
Rename the original table to B-xxx.
Rename A-xxx to your original table name.
Delete B-xxx.
This way the updates can be done in the new table (after step 2) and the only time the bigtable is unavailable is while doing step 3 and 4 (renaming).
Use a TRY CATCH block
BEGIN TRAN before BEGIN TRY and ROLLBACK TRAN inside CATCH block

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".

Syntax exception on trigger with multiple statements with MySQL and JDBC

I am trying to create a trigger that performs multiple operations in MySQL 5.5.28 with InnoDB.
I have two tables, "test" and "test_watcher": changes to the first are recorded in the watcher table with the help of triggers. The last trigger needs to perform 2 operations on DELETE, it works in MySQL Workbench (with DELIMITER) but doesn't if I create it with JDBC.
CREATE TRIGGER `AD_test_FER` AFTER DELETE
ON `test`
FOR EACH ROW
BEGIN
-- if it's been inserted, modified and deleted but never synced,
-- the revision is NULL: no one needs to know about it
DELETE FROM test_watcher WHERE pk = OLD.id AND revision IS NULL;
-- if it has been synced already, we just update the flag
UPDATE test_watcher SET flag = -1 WHERE pk = OLD.id;
END;
I keep getting com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax.
I know it works with DELIMITER $$ in Workbench, but JDBC doesn't support it.
I've achieved it in PostgreSQL and would post the code if necessary.
This behavior might be caused by the connection property allowMultiQueries=true. My guess is this property will make MySQL break up queries on the ; as a query separator and then execute those as separate queries, essentially breaking your trigger creation code.
As you said in a - now deleted - answer that adding allowMultiQueries=true actually solved the problem (contrary to my expectiation), the problem might actually be the last ; in your query. So another thing to check is if the problem goes away by removing the last ; (in END;) in your script (and not using allowMultiQueries=true). Some database don't consider ; to be valid at the end of a statement (as it is actually a delimiter to separate statements).
(this answer is based on my comment above)
If JDBC does not support delimiters (DELIMITER is a client command), then execute these two statements separately - one by one.