how programming a business rule on a trigger in mysql - mysql

I'm trying to programm a business rule in a trigger using MySQL, but it doesn't work.
What I want to do is that if the condition is false then the insert must fail and rollback it. The problem is that MySQL doesn't permit using rollback inside a trigger.
I've also tried to use SIGNAL and text message but it fails.
The code is:
CREATE TRIGGER tr_NewTeacher
AFTER INSERT ON teachers FOR EACH ROW
BEGIN
IF (salary > 1300 AND budget < 15000)
BEGIN
ROLLBACK TRANSACTION;
END
END

In MySQL trigger will rollback transaction if it fails.
So, you must raise exception within trigger body:
...
if [Test]
then
SIGNAL sqlstate '45001' set message_text = "No way ! You cannot do this !";
end if ;
...

You should be using before insert and for getting the column values you need to use new
So the trigger would be as
delimiter //
create trigger tr_NewTeacher before insert on teacher
for each row
begin
if new.salaRY>1300 AND new.budget<15000 then
signal sqlstate '45000' set message_text = 'Your error message';
end if;
end;//
delimiter ;
Here is a test case in mysql
mysql> create table teacher (id int auto_increment primary key,salaRY int , budget int);
Query OK, 0 rows affected (0.17 sec)
mysql> delimiter //
mysql> create trigger tr_NewTeacher before insert on teacher
-> for each row
-> begin
-> if new.salaRY>1300 AND new.budget<15000 then
-> signal sqlstate '45000' set message_text = 'Your error message';
-> end if;
-> end;//
Query OK, 0 rows affected (0.14 sec)
mysql> insert into teacher (salaRY,budget) values (1200,16000);
Query OK, 1 row affected (0.04 sec)
mysql> insert into teacher (salaRY,budget) values (1400,13000);
ERROR 1644 (45000): Your error message
mysql> select * from teacher ;
+----+--------+--------+
| id | salaRY | budget |
+----+--------+--------+
| 1 | 1200 | 16000 |
+----+--------+--------+
1 row in set (0.00 sec)

Related

setting variable in case of exception

I have a stored procedure which will set my customized error as well as set an out variable to some value . I tried the following procedure .
***delimiter //
drop procedure if exists test_dalpu //
create procedure test_dalpu(out out_value int)
begin
DECLARE EXIT HANDLER FOR SQLSTATE '22001'
BEGIN
set out_value = 1;
SIGNAL SQLSTATE '22001' SET
MYSQL_ERRNO = 2,
MESSAGE_TEXT = 'Too long ';
END;
insert into abc_test values ( 5,"eerrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr");***
end//
delimiter ;
Then after executing
**call test_dalpu(#out_value);**
I got the correct output as Error code 2 Too long as expected, But after that If I do
select #out_value;
Am getting the value as null instead of 1 .
Is it the correct way?
Try:
mysql> DROP PROCEDURE IF EXISTS `test_dalpu`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `abc_test`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `abc_test` (
-> `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-> `description` CHAR(10)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER //
mysql> CREATE PROCEDURE `test_dalpu`(`out_value` VARCHAR(64))
-> BEGIN
-> DECLARE EXIT HANDLER FOR SQLSTATE '22001'
-> BEGIN
-> ROLLBACK;
-> SET #`set_variable` := CONCAT('SET #`', `out_value`, '` := 1');
-> PREPARE `stmt` FROM #`set_variable`;
-> EXECUTE `stmt`;
-> DEALLOCATE PREPARE `stmt`;
-> SIGNAL SQLSTATE '22001' SET
-> MYSQL_ERRNO = 2,
-> MESSAGE_TEXT = 'Too long';
-> END;
-> START TRANSACTION;
-> INSERT INTO `abc_test`
-> (`description`)
-> VALUES
-> ('ABCDEFGHIJK');
-> COMMIT;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> SET #`out_value` := NULL;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL `test_dalpu`('out_value');
ERROR 2 (22001): Too long
mysql> SHOW WARNINGS;
+-------+------+----------+
| Level | Code | Message |
+-------+------+----------+
| Error | 2 | Too long |
+-------+------+----------+
1 row in set (0.00 sec)
mysql> SELECT #`out_value`;
+--------------+
| #`out_value` |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)

sql if-else procedure prob, cant create procedure

delimiter//
create procedure exercise(out stat char(10),out fee double)
begin
if stat=='AC executive' then set fee=100;
else if stat=='AC Economi' then set fee=50;
else set saldo=20;
end if;
end//
delimiter;
Your if-else if is not correct it should be
if condition then do something
elseif condition then do something
else do something
There is no space between elseif. Also I suppose you mean fee not saldo in the else part. And out stat char(10) should be in stat varchar(100), since you are using the param as input
delimiter //
create procedure exercise(in stat varchar(100),out fee double)
begin
if stat ='AC executive' then set fee=100;
elseif stat = 'AC Economi' then set fee=50;
else set fee=20;
end if;
end;//
delimiter;
Here is a test on mysql
mysql> delimiter //
mysql> create procedure exercise(in stat varchar(100),out fee double)
-> begin
-> if stat ='AC executive' then set fee=100;
-> elseif stat = 'AC Economi' then set fee=50;
-> else set fee=20;
-> end if;
-> end;//
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call exercise('AC executive',#o);
Query OK, 0 rows affected (0.00 sec)
mysql> select #o ;
+------+
| #o |
+------+
| 100 |
+------+
1 row in set (0.00 sec)

printing message in trigger Mysql 5.0

WELL I NEED TO CREATE A TRIGGER ON TABLE RESERVATION IT DOES NOT ALLOW DATE LESS THAN CURRENT DATE.
MY TRIGGER IS :
mysql> create trigger reserve_trigger before insert on reservation
-> for each row
-> begin
-> if(new.Journey_Day<curdate())
-> then
-> set NEW="Jouney Date Should not be less than current date";
-> end if;
-> end //
Query OK, 0 rows affected (0.00 sec)
mysql> insert into reservation values ("ppno00005","FSA03","2012-02-09","ECONOMY","MC001","SC003")//
ERROR 1231 (42000): Variable 'new' can't be set to the value of 'Jouney Date Should not be less than current date'
I TRIED TO PRINT MESSAGE USING
signal sqlstate '45000' set message_text ="invalid entry";
BUT I REALIZED IT DOESN'T WORK FOR MY VERSION OF SQL. IS THERE A BETTER WAY OF DOING IT.
Yes, there is. You can call nonexistent stored procedure. For example -
...
BEGIN
IF NEW.Journey_Day < CURDATE() THEN
CALL proc_error_date();
END IF;
END
...

Is there a way to query mySql if my connection has started a transaction?

I have found many anwsers of how frameworks handle transaction nesting, but I couldn't found if there is an executable query against mySql RDMS that returns if a transaction has started or not. Is it posible? thanks
It is a bit tricky, but you can find an example how to do this at http://forge.mysql.com/tools/tool.php?id=145
I am copying here #wonk0 link as it is no more aviable.
DELIMITER $$
CREATE PROCEDURE `myexample` (
OUT errno INT,
OUT error VARCHAR(255)
)
BEGIN
DECLARE need_to_commit BOOL DEFAULT FALSE;
main:BEGIN
-- for example, catch duplicate key errors and roll back
DECLARE EXIT HANDLER FOR 1062
BEGIN
ROLLBACK TO SAVEPOINT myexample;
SET errno = 1060,
error = 'Duplicate key.';
END;
-- catch any other errors that should cause automatic rollbacks
-- ------
-- set up the savepoint / trx handler
--
DECLARE CONTINUE HANDLER FOR 1305
BEGIN
START TRANSACTION;
SET need_to_commit = TRUE;
END;
-- this will have no effect if we are not in a trx
SAVEPOINT myexample;
-- this will error if we are not in a trx, be caught above, and start a trx
-- it will do nothing if we are already in a trx
RELEASE SAVEPOINT myexample;
-- this will always set a savepoint
-- because we are now guaranteed to be in a trx
SAVEPOINT myexample;
--
-- done setting up savepoint / trx
-- ------
-- initialize the OUT parameters
SET errno = 0,
error = '';
-- do some stuff
INSERT INTO mytable VALUES (1);
INSERT INTO yourtable VALUES (2);
-- you can even handle your own errors (without handlers!)
IF ( 0 != 1 ) THEN
ROLLBACK TO SAVEPOINT myexample;
SET errno = 1234,
error = 'Zero is not one!';
LEAVE main;
END IF;
END; -- main
-- if we were not in a transaction to start with
-- we should not leave one dangling, so commit here
IF need_to_commit THEN
COMMIT;
END IF;
END $$
DELIMITER ;
/*
EXAMPLE
mysql> create table mytable (a int primary key) engine=innodb;
Query OK, 0 rows affected (0.07 sec)
mysql> create table yourtable (b int primary key) engine=innodb;
Query OK, 0 rows affected (0.03 sec)
mysql> call myexample(#e,#r); select #e,#r;
Query OK, 0 rows affected (0.00 sec)
+------+------------------+
| #e | #r |
+------+------------------+
| 1234 | Zero is not one! |
+------+------------------+
1 row in set (0.00 sec)
mysql> select * from mytable union select * from yourtable;
Empty set (0.00 sec)
mysql> insert into yourtable values (2);
Query OK, 1 row affected (0.00 sec)
mysql> call myexample(#e,#r); select #e,#r;
Query OK, 0 rows affected (0.00 sec)
+------+----------------+
| #e | #r |
+------+----------------+
| 1060 | Duplicate key. |
+------+----------------+
1 row in set (0.00 sec)
mysql> select * from mytable union select * from yourtable;
+---+
| a |
+---+
| 2 |
+---+
1 row in set (0.00 sec)
*/

In a MySQL Trigger, how to get informations on the user sending the request?

I'm on MySQL 5.5, with a trigger, and I want to check if the user can do its request. It's just an exemple, how can I do with a code like this?
-- Trigger DDL Statements
DELIMITER $$
USE `database`$$
CREATE TRIGGER TBI_TEST BEFORE INSERT
ON tb_test FOR EACH ROW
BEGIN
DECLARE ER_BAD_USER CONDITION FOR SQLSTATE '45000';
IF NEW.host != {{HOW TO KNOW THE HOST PART OF THE CURRENT USER?}} THEN
SIGNAL ER_BAD_USER
SET MESSAGE_TEXT = 'forbidden', MYSQL_ERRNO = 401;
END IF;
END$$
Ok, i've found the solution:
USER()
[EDIT]
Warning:
MySQL store user and host values in with UTF8-BIN collation, after lowering them and USER() return without lowering.
For example, USER() return gqyy#MyTinnyHost-PC.local when MySQL have stored gqyy and mytinnyhost-pc.local
a problem described here (Bug #60166) arises when you use a SUBSTRING_INDEX() inside a LOWER() with the return of USER() stored in an user-defined variable
For example:
mysql> SET #user_at_host = 'gqyy#mytinyhost-PC.local';
Query OK, 0 rows affected (0,00 sec)
mysql> SELECT LOWER(SUBSTRING_INDEX(#user_at_host, '#', -1));
+------------------------------------------------+
| LOWER(SUBSTRING_INDEX(#user_at_host, '#', -1)) |
+------------------------------------------------+
| mytinyhost-pc. ocal |
+------------------------------------------------+
1 row in set (0,00 sec)