Bug #15058 | CONTINUE HANDLER not working correctly | ||
---|---|---|---|
Submitted: | 18 Nov 2005 22:06 | Modified: | 23 Nov 2005 22:19 |
Reporter: | Matthew Lord | Email Updates: | |
Status: | Not a Bug | Impact on me: | |
Category: | MySQL Server: Stored Routines | Severity: | S1 (Critical) |
Version: | 5.0.15, 5.0.17-20051115snapshot | OS: | Linux (linux) |
Assigned to: | Konstantin Osipov | CPU Architecture: | Any |
[18 Nov 2005 22:06]
Matthew Lord
[23 Nov 2005 19:46]
Konstantin Osipov
I was able to repeat the described behaviour.
[23 Nov 2005 20:03]
Konstantin Osipov
I was able to repeat the bug with this small test case: drop table if exists t1; create table t1 (a int); insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); drop table if exists t2; create table t2 select * from t1; drop procedure if exists bug15058; delimiter | create procedure bug15058() begin declare done int default 0; declare p_a int; declare p_b int; declare c cursor for select a from t1; declare continue handler for not found set done=1; open c; c_loop: loop fetch c into p_a; if (done = 1) then leave c_loop; end if; select p_a; select a into p_b from t2 where a=p_a; if (p_b is null) then set p_b=5; end if; select p_b; end loop; end| delimiter ; call bug15058(); update t2 set a=0 where a=5; call bug15058(); drop procedure bug15058; drop table t1,t2; Here is the output: mysql> drop table if exists t1; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> create table t1 (a int); Query OK, 0 rows affected (0.01 sec) mysql> insert into t1 (a) values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10); Query OK, 10 rows affected (0.00 sec) Records: 10 Duplicates: 0 Warnings: 0 mysql> drop table if exists t2; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> create table t2 select * from t1; Query OK, 10 rows affected (0.01 sec) Records: 10 Duplicates: 0 Warnings: 0 mysql> drop procedure if exists bug15058; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> delimiter | mysql> create procedure bug15058() -> begin -> declare done int default 0; -> declare p_a int; -> declare p_b int; -> declare c cursor for select a from t1; -> declare continue handler for not found set done=1; -> open c; -> -> c_loop: loop -> -> fetch c into p_a; -> if (done = 1) then -> leave c_loop; -> end if; -> select p_a; -> select a into p_b from t2 where a=p_a; -> if (p_b is null) then -> set p_b=5; -> end if; -> select p_b; -> end loop; -> end| Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> call bug15058(); +-----+ | p_a | +-----+ | 1 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 1 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 2 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 2 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 3 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 3 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 4 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 4 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 5 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 5 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 6 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 6 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 7 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 7 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 8 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 8 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 9 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 9 | +-----+ 1 row in set (0.01 sec) +-----+ | p_a | +-----+ | 10 | +-----+ 1 row in set (0.01 sec) +-----+ | p_b | +-----+ | 10 | +-----+ 1 row in set (0.02 sec) Query OK, 0 rows affected (0.02 sec) mysql> update t2 set a=0 where a=5; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> call bug15058(); +-----+ | p_a | +-----+ | 1 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 1 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 2 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 2 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 3 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 3 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 4 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 4 | +-----+ 1 row in set (0.00 sec) +-----+ | p_a | +-----+ | 5 | +-----+ 1 row in set (0.00 sec) +-----+ | p_b | +-----+ | 4 | +-----+ 1 row in set (0.01 sec) Query OK, 0 rows affected (0.01 sec) mysql> drop procedure bug15058; Query OK, 0 rows affected (0.00 sec) mysql> drop table t1,t2; Query OK, 0 rows affected (0.00 sec)
[23 Nov 2005 22:19]
Peter Gulutzan
The difficulty is that the SELECT ... INTO statement is activating the NOT FOUND handler unexpectedly. This in fact is correct behaviour. I quote the SQL:2003 standard, Part 2 SQL/Foundation, Section 14.5 <select statement: single row>, General Rules: " 1) Let Q be the result of <query specification> S. 2) Case: ... b) If Q is empty, then no SQL-data values are assigned to any targets identified by the <select target list>, and a completion condition is raised: no data. " Later we see that "no data" (SQLSTATE class '02') is category 'N' (not found), and we're supposed to catch not-found situations with NOT FOUND handlers. Here is an example of one way to get around this: create procedure p () begin declare v1,v2 int; declare c cursor for select * from t1; declare continue handler for not found select 'a'; begin declare continue handler for not found begin end; select s1 into v1 from t2; end; open c; fetch c into v2; end// In this case, we put a BEGIN/END within a BEGIN/END. If the SELECT returns "no data", the inner handler catches it, and nothing stops. I believe MySQL has done one thing wrong, though: the "not found" message is "No data to FETCH". I have submitted a separate bug report about that, bug#15206 Misleading message "No data to FETCH"