Handling sql errors (if exists, if constraint), Perl - mysql

I am using perl and mysql on my site.
Is there a mechanism to restrict deleting if data connected to 'deleting' exists? Yes - foreign key constraint. Is it possible to return some code if row wasn't deleted because of foreign key constraint? Somethink like this:
$id = $cgi->param("id");
$query="delete from `Class` where `id` = '$id'";
$sth = $dbh->prepare($query);
$sth->execute or die(print $sth->errstr);
if ($sth->errcode eq '777')
{
print 'error! there are 1 or more rows, connected with row you want to delete';
}
else
{
print 'ok! deleted';
}
Same for inserting row and there are row with same data existing. Sure It is possible to create trigger on insert and try to return some code. Give me example of such trigger, please? And again : how to handle this error?
Some help please! Sorry for my not very good english :)

If you want to return your own success/failure code then you can use procedures. create a procedure
delimiter $$
CREATE PROCEDURE `deleteProcedure`(in Id INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
select 0; -- number that you want to return on failure
ROLLBACK;
END;
START TRANSACTION;
-- put any number of statement, including delete, insert, update
delete from `Class` where `id` = Id;
COMMIT;
select 1; -- number that you want to return on success
END$$
call this procedure from your perl script, and it will return the values you put in your procedure.

Related

PLsql equivalent query from PLpgsql

I am new to mysql and used to work in postgresql. I have been trying to make a function in mysql workbench using plsql. I have written a code in plpgsql to describe what I want to do (basically data entry in a table avoiding the redundant entries)
I tried searching relevant answers and founds ways to delete duplicate entries from table. But, what I want to do is avoid duplicate data entry in the table. Following is the workable code but I don't understand how to get out of the loop if the update query runs successfully, if not, then insert query needs to be run. I know I can add IF-then-ELSE here. but what to write in the conditions.
DELIMITER //
CREATE PROCEDURE merge_abc
(IN data CHAR(20))
BEGIN
LOOP
UPDATE abc SET node_name = data WHERE node_name = data;
END LOOP;
END //
DELIMITER ;
*Aim is to avoid duplicate data entry rather than deleting the duplicate entries in the end just like this;
CREATE FUNCTION merge_abc(data TEXT) RETURNS VOID AS
BEGIN
LOOP
-- first try to update the key
UPDATE abc SET node_name = data WHERE node_name = data;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO abc(node_name) VALUES (data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END LOOP;
END;

Transaction and stored procedure. Why log table is empty in this case?

I am very new in Mysql, probably don't know or don't understand something essential.
Could you please advise me why 'begin !!!' message is not inserted in this
case?
DELIMITER $$
CREATE TABLE `_debugLogTable` (
`Message` varchar(255) DEFAULT NULL
) ENGINE=InnoDB $$
CREATE PROCEDURE `debug_msg`(msg VARCHAR(255))
BEGIN
insert into _debugLogTable select msg;
END$$
CREATE FUNCTION `ValueMeetsCondition`(value varchar(20)) RETURNS tinyint(1)
BEGIN
DECLARE ConditionValue INTEGER;
call debug_msg('begin !!!');
SET ConditionValue = CAST(`value` AS UNSIGNED);
call debug_msg('end !!!');
RETURN TRUE;
END$$
DELIMITER ;
I am aware that CAST function fails, but why call debug_msg('begin !!!'); does not insert new record into table?! There are not any transactions there!
Just want post an answer, maybe it will help somebody in the future.
From this we have -
If autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error
I call function in this way - select ValueMeetsCondition('>10').
So actually it is wrapped into transaction by MySQL, that's why if something inside my procedure fails - the whole changes are roll backed.
If i remake my query in this way the message begin !! will be inserted, while end !! does not
call debug_msg('begin !!!');
SET ConditionValue = CAST(`>10` AS UNSIGNED);
select ConditionValue;
call debug_msg('end !!!');

How to write a stored procedure in MySQL that displays two messages when called

The goal here is to write a script attempts to insert a new category into categories table and if the insert was successful the message:
1 row was inserted
If the update is unsuccessful, the procedure should display this message:
Row was not inserted - duplicate entry
Whenever I run this script, I keep getting the second message no matter how many times I run it, when really it should display the first message, followed by the second. Here is my script. Can someone please help me spot what I'm overlooking? Thank you.
use my_guitar_shop;
/*drop procedure if exists add_category;*/
DELIMITER //
CREATE PROCEDURE add_category(
in categories VARCHAR(100)
)
begin
declare duplicate_entry_for_key TinyINT DEFAULT FALSE;
declare continue handler for 1062
SET duplicate_entry_for_key = TRUE;
insert into categories values (5, 'Electric');
select '1 row was inserted.' as message;
if duplicate_entry_for_key = true then
select 'Row was not inserted - duplicate entry.' as message;
end if;
end //
DELIMITER ;
/* call the stored procedure with 'Gibson' */
call add_category('Gibson');
call add_category('Gibson');
You should set duplicate_entry_for_key to True. If data insert is successful.
Do something like below after insert operation.
SET duplicate_entry_for_key = last_insert_id();
OR see the below example for bit check.
For example:
START TRANSACTION; -- Begin a transaction
INSERT INTO categories
VALUES
(
5
,'Electric'
);
IF ROW_COUNT() > 0 THEN -- ROW_COUNT() returns the number of rows updated/inserted/deleted
SET duplicate_entry_for_key = TRUE;
COMMIT; -- Finalize the transaction
ELSE
SET duplicate_entry_for_key = False;
--You can ROLLBACK the transaction also - Revert all changes made before the transaction began
END IF

How to translate PostgreSQL "merge_db" (aka upsert) function into MySQL

Straight from the manual, here's the canonical example of merge_db in PostgreSQL:
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE db SET b = data WHERE a = key;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO db(a,b) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- Do nothing, and loop to try the UPDATE again.
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');
Can this be expressed as a user-defined function in MySQL, and if so, how? Would there be any advantage over MySQL's standard INSERT...ON DUPLICATE KEY UPDATE?
Note: I'm specifically looking for a user-defined function, not INSERT...ON DUPLICATE KEY UPDATE.
Tested on MySQL 5.5.14.
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
DELIMITER //
CREATE PROCEDURE merge_db(k INT, data TEXT)
BEGIN
DECLARE done BOOLEAN;
REPEAT
BEGIN
-- If there is a unique key constraint error then
-- someone made a concurrent insert. Reset the sentinel
-- and try again.
DECLARE ER_DUP_UNIQUE CONDITION FOR 23000;
DECLARE CONTINUE HANDLER FOR ER_DUP_UNIQUE BEGIN
SET done = FALSE;
END;
SET done = TRUE;
SELECT COUNT(*) INTO #count FROM db WHERE a = k;
-- Race condition here. If a concurrent INSERT is made after
-- the SELECT but before the INSERT below we'll get a duplicate
-- key error. But the handler above will take care of that.
IF #count > 0 THEN
UPDATE db SET b = data WHERE a = k;
ELSE
INSERT INTO db (a, b) VALUES (k, data);
END IF;
END;
UNTIL done END REPEAT;
END//
DELIMITER ;
CALL merge_db(1, 'david');
CALL merge_db(1, 'dennis');
Some thoughts:
You can't do an update first and then check #ROW_COUNT() because it returns the number of rows actually changed. This could be 0 if the row already has the value you are trying to update.
Also, #ROW_COUNT() is not replication safe.
You could use REPLACE...INTO.
If using InnoDB or a table with transaction support you might be able to use SELECT...FOR UPDATE (untested).
I see no advantage to this solution over just using INSERT...ON DUPLICATE KEY UPDATE.

MySQL EXIT HANDLER is not catching all the errors?

My MySQL procedure looks like:
create procedure create_feed (_id int)
begin
declare exit handler for sqlexception
begin
rollback;
select false;
end;
start transaction;
insert into t1(id)
values (_id);
insert into wrong_table_name (id, createdtime)
values (
_id,
CURRENT_TIMESTAMP
);
commit;
select true;
end//
After I called this procedure, the t1 table is updated, and the value 'true' is returned. The wrong_table_name does not exist at all. Why?
I recommend that you use a function instead of a procedure if you want it to return a value. Another option is to use out parameters if you want your procedure to return one or more values.
Nonetheless, I am surprised by your results. If wrong_table_name does not exist, then that proc should return true. As for t1 being updated, that would happen if you are using a non-transactional storage engine like MyISAM, which ignores the rollback.
I tested your code in MySQL 5.5.8, and it worked properly for me. Namely, it always entered the exit handler when wrong_table_name did not exist.