3497 Jon Olav Hauglid 2011-10-14 [merge]
WL#5986 Stored programs: Make condition handlers closer to the standard
Merged from mysql-trunk-alik.
modified:
mysql-test/r/signal.result
mysql-test/r/sp-code.result
mysql-test/r/sp-error.result
mysql-test/r/sp.result
mysql-test/suite/funcs_1/r/innodb_storedproc_02.result
mysql-test/suite/funcs_1/r/memory_storedproc_02.result
mysql-test/suite/funcs_1/r/myisam_storedproc_02.result
mysql-test/suite/funcs_1/r/storedproc.result
mysql-test/t/sp-error.test
mysql-test/t/sp.test
sql/item_xmlfunc.cc
sql/sp_head.cc
sql/sp_head.h
sql/sp_pcontext.cc
sql/sp_pcontext.h
sql/sp_rcontext.cc
sql/sp_rcontext.h
sql/sql_array.h
sql/sql_class.cc
sql/sql_class.h
sql/sql_error.cc
sql/sql_error.h
sql/sql_signal.cc
sql/sql_yacc.yy
3496 Jon Olav Hauglid 2011-10-14
This patch fixes a compiler error reported by gcc 4.6.1.
pfs_instr.cc:1476:47: error: cast to pointer from
integer of different size
modified:
storage/perfschema/pfs_instr.cc
=== modified file 'mysql-test/r/signal.result'
--- a/mysql-test/r/signal.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/r/signal.result 2011-09-20 12:13:07 +0000
@@ -2163,15 +2163,18 @@ CALL peter_p2() $$
1
1
Level Code Message
+Error 1231 Variable 'sql_mode' can't be set to the value of 'NULL'
2
2
Level Code Message
Error 1231 Variable 'sql_mode' can't be set to the value of 'NULL'
+Error 1232 Variable 'sql_mode' can't be set to the value of 'NULL'
3
3
Level Code Message
Error 1231 Variable 'sql_mode' can't be set to the value of 'NULL'
Error 1232 Variable 'sql_mode' can't be set to the value of 'NULL'
+Error 9999 Variable 'sql_mode' can't be set to the value of 'NULL'
ERROR 42000: Hi, I am a useless error message
show warnings $$
Level Code Message
=== modified file 'mysql-test/r/sp-code.result'
--- a/mysql-test/r/sp-code.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/r/sp-code.result 2011-09-20 12:13:07 +0000
@@ -924,16 +924,20 @@ CALL p1();
Warning found!
Warning found!
Level Code Message
+Warning 1105 Unknown error
Warning found!
Warning found!
Level Code Message
+Warning 1105 Unknown error
Warning found!
Warning found!
Level Code Message
+Warning 1105 Unknown error
End of Result Set found!
End of Result Set found!
Level Code Message
Warning 1105 Unknown error
+Error 1329 No data - zero rows fetched, selected, or processed
SET SESSION debug="-d,bug23032_emit_warning";
DROP PROCEDURE p1;
DROP TABLE t1;
=== modified file 'mysql-test/r/sp-error.result'
--- a/mysql-test/r/sp-error.result 2011-09-21 11:01:41 +0000
+++ b/mysql-test/r/sp-error.result 2011-10-14 15:04:41 +0000
@@ -1901,8 +1901,6 @@ END|
CALL p1();
exception
exception
-Warnings:
-Warning 1292 Truncated incorrect INTEGER value: '10 '
DROP TABLE t1;
DROP PROCEDURE p1;
#
@@ -2020,3 +2018,805 @@ Error 1048 Column 'c' cannot be null
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
+
+###################################################################
+# Tests for the following bugs:
+# - Bug#11763171: 55852 - Possibly inappropriate handler activation.
+# - Bug#11749343: 38806 - Wrong scope for SQL HANDLERS in SP.
+###################################################################
+
+
+# -- Check that SQL-conditions thrown by Statement-blocks are
+# -- handled by Handler-decl blocks properly.
+
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H2' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+END|
+
+CALL p1()|
+HandlerId
+H2
+
+# -- Check that SQL-conditions thrown by Statement-blocks are
+# -- handled by Handler-decl blocks properly in case of nested
+# -- SQL-blocks.
+
+CREATE PROCEDURE p2()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H2' AS HandlerId;
+BEGIN
+SELECT 'B1' AS BlockId;
+BEGIN
+SELECT 'B2' AS BlockId;
+BEGIN
+SELECT 'B3' AS BlockId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+END;
+END;
+END;
+END|
+
+CALL p2()|
+BlockId
+B1
+BlockId
+B2
+BlockId
+B3
+HandlerId
+H2
+
+# -- Check SQL-handler resolution rules.
+
+CREATE PROCEDURE p3()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H3' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H3.
+END|
+
+CALL p3()|
+HandlerId
+H3
+
+CREATE PROCEDURE p4()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H3' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+END|
+
+CALL p4()|
+HandlerId
+H2
+
+CREATE PROCEDURE p5()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H2' AS HandlerId;
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H3' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H3.
+END;
+END|
+
+CALL p5()|
+HandlerId
+H3
+
+# -- Check that handlers don't handle its own exceptions.
+
+CREATE PROCEDURE p6()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+SELECT 'H1' AS HandlerId;
+SIGNAL SQLSTATE 'HY000'; # Should *not* be handled by H1.
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # Should be handled by H1.
+END|
+
+CALL p6()|
+SignalId
+S1
+HandlerId
+H1
+ERROR HY000: Unhandled user-defined exception condition
+
+# -- Check that handlers don't handle its own warnings.
+
+CREATE PROCEDURE p7()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+SELECT 'H1' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H1.
+END|
+
+CALL p7()|
+SignalId
+S1
+HandlerId
+H1
+Warnings:
+Warning 1642 Unhandled user-defined warning condition
+
+# -- Check that conditions for handlers are not handled by the handlers
+# -- from the same block.
+
+CREATE PROCEDURE p8()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+SELECT 'H2' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+END|
+
+CALL p8()|
+SignalId
+S1
+HandlerId
+H2
+Warnings:
+Warning 1642 Unhandled user-defined warning condition
+
+# -- Check that conditions for handlers are not handled by the handlers
+# -- from the same block even if they are thrown deep down the stack.
+
+CREATE PROCEDURE p9()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H1:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H1:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H2:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H2:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H3:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H3:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H4:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H4:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H5:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H5:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H6:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H6:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+SELECT 'H2' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+END;
+SELECT 'S6' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S5' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S4' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S3' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S2' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+END|
+
+CALL p9()|
+SignalId
+S1
+SignalId
+S2
+SignalId
+S3
+SignalId
+S4
+SignalId
+S5
+SignalId
+S6
+HandlerId
+H2
+Warnings:
+Warning 1642 Unhandled user-defined warning condition
+
+# -- Check that handlers are choosen properly in case of deep stack and
+# -- nested SQL-blocks.
+
+CREATE PROCEDURE p10()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H2' AS HandlerId;
+BEGIN
+BEGIN
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H1:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H1:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H2:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H2:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H3:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H3:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H4:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H4:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H5:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H5:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H6:1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'Wrong:H6:2' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+BEGIN
+SELECT 'H2' AS HandlerId;
+SIGNAL SQLSTATE '01000'; # Should be handled by H1.
+END;
+SELECT 'S6' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S5' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S4' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S3' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S2' AS SignalId;
+SIGNAL SQLSTATE 'HY000';
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+END;
+END;
+END;
+END|
+
+CALL p10()|
+SignalId
+S1
+SignalId
+S2
+SignalId
+S3
+SignalId
+S4
+SignalId
+S5
+SignalId
+S6
+HandlerId
+H2
+HandlerId
+H1
+
+# -- Test stored procedure from Peter's mail.
+
+CREATE PROCEDURE p11()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H1' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H2' AS HandlerId;
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01000', 1249
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+SELECT 'H3' AS HandlerId;
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+SELECT 'H4' AS HandlerId;
+BEGIN
+SELECT 'H5' AS HandlerId;
+SELECT 'S3' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # H3
+SELECT 'S4' AS SignalId;
+SIGNAL SQLSTATE '22003'; # H3
+SELECT 'S5' AS SignalId;
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H4
+END;
+END;
+SELECT 'S6' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # H1
+SELECT 'S7' AS SignalId;
+SIGNAL SQLSTATE '22003'; # H1
+SELECT 'S8' AS SignalId;
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H5
+END;
+SELECT 'S1' AS SignalId;
+SIGNAL SQLSTATE 'HY000'; # H1
+SELECT 'S2' AS SignalId;
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H2
+END|
+
+CALL p11()|
+SignalId
+S6
+HandlerId
+H1
+SignalId
+S7
+HandlerId
+H1
+SignalId
+S8
+HandlerId
+H5
+SignalId
+S3
+HandlerId
+H3
+SignalId
+S4
+HandlerId
+H3
+SignalId
+S5
+HandlerId
+H4
+SignalId
+S1
+HandlerId
+H1
+SignalId
+S2
+HandlerId
+H2
+
+# -- Check that runtime stack-trace can be deeper than parsing-time one.
+
+CREATE PROCEDURE p12()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+SELECT 'H1:5' AS HandlerId;
+SIGNAL SQLSTATE '01002';
+END;
+SELECT 'H1:4' AS HandlerId;
+SIGNAL SQLSTATE '01001';
+END;
+SELECT 'H1:3' AS HandlerId;
+SIGNAL SQLSTATE '01001';
+END;
+SELECT 'H1:2' AS HandlerId;
+SIGNAL SQLSTATE '01001';
+END;
+SELECT 'H1:1' AS HandlerId;
+SIGNAL SQLSTATE '01001';
+END;
+#########################################################
+DECLARE CONTINUE HANDLER FOR SQLSTATE '01002'
+ SELECT 'OK' AS Msg;
+#########################################################
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+SELECT 'H2:5' AS HandlerId;
+SIGNAL SQLSTATE '01001';
+END;
+SELECT 'H2:4' AS HandlerId;
+SIGNAL SQLSTATE '01000';
+END;
+SELECT 'H2:3' AS HandlerId;
+SIGNAL SQLSTATE '01000';
+END;
+SELECT 'H2:2' AS HandlerId;
+SIGNAL SQLSTATE '01000';
+END;
+SELECT 'H2:1' AS HandlerId;
+SIGNAL SQLSTATE '01000';
+END;
+#######################################################
+SELECT 'Throw 01000' AS Msg;
+SIGNAL SQLSTATE '01000';
+END;
+END|
+
+CALL p12()|
+Msg
+Throw 01000
+HandlerId
+H2:1
+HandlerId
+H2:2
+HandlerId
+H2:3
+HandlerId
+H2:4
+HandlerId
+H2:5
+HandlerId
+H1:1
+HandlerId
+H1:2
+HandlerId
+H1:3
+HandlerId
+H1:4
+HandlerId
+H1:5
+Warnings:
+Warning 1642 Unhandled user-defined warning condition
+
+# -- Check that handler-call-frames are removed properly for EXIT
+# -- handlers.
+
+CREATE PROCEDURE p13()
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING
+BEGIN
+DECLARE EXIT HANDLER FOR SQLWARNING
+BEGIN
+SELECT 'EXIT handler 3' AS Msg;
+END;
+SELECT 'CONTINUE handler 2: 1' AS Msg;
+SIGNAL SQLSTATE '01000';
+SELECT 'CONTINUE handler 2: 2' AS Msg;
+END;
+SELECT 'CONTINUE handler 1: 1' AS Msg;
+SIGNAL SQLSTATE '01000';
+SELECT 'CONTINUE handler 1: 2' AS Msg;
+END;
+SELECT 'Throw 01000' AS Msg;
+SIGNAL SQLSTATE '01000';
+END|
+
+CALL p13()|
+Msg
+Throw 01000
+Msg
+CONTINUE handler 1: 1
+Msg
+CONTINUE handler 2: 1
+Msg
+EXIT handler 3
+Msg
+CONTINUE handler 1: 2
+
+# That's it. Cleanup.
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+DROP PROCEDURE p7;
+DROP PROCEDURE p8;
+DROP PROCEDURE p9;
+DROP PROCEDURE p10;
+DROP PROCEDURE p11;
+DROP PROCEDURE p12;
+DROP PROCEDURE p13;
+
+# Bug#12731619: NESTED SP HANDLERS CAN TRIGGER ASSERTION
+
+DROP FUNCTION IF EXISTS f1;
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1(msg VARCHAR(255));
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 1
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 2
+BEGIN
+INSERT INTO t1 VALUE('WRONG: Inside H2');
+RETURN 2;
+END;
+INSERT INTO t1 VALUE('CORRECT: Inside H1');
+RETURN 1;
+END;
+BEGIN
+DECLARE CONTINUE HANDLER FOR SQLWARNING # handler 3
+BEGIN
+INSERT INTO t1 VALUE('WRONG: Inside H3');
+RETURN 3;
+END;
+INSERT INTO t1 VALUE('CORRECT: Calling f1()');
+RETURN f1(); # -- exception here
+END;
+INSERT INTO t1 VALUE('WRONG: Returning 10');
+RETURN 10;
+END|
+
+SELECT f1();
+f1()
+1
+
+SELECT * FROM t1;
+msg
+CORRECT: Calling f1()
+CORRECT: Inside H1
+
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+# Check that handled SQL-conditions are properly cleared from DA.
+
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+DROP PROCEDURE IF EXISTS p3;
+DROP PROCEDURE IF EXISTS p4;
+DROP PROCEDURE IF EXISTS p5;
+CREATE TABLE t1(a CHAR, b CHAR, c CHAR);
+CREATE TABLE t2(a SMALLINT, b SMALLINT, c SMALLINT);
+
+# Check that SQL-conditions for which SQL-handler has been invoked,
+# are cleared from the Diagnostics Area. Note, there might be several
+# SQL-conditions, but SQL-handler must be invoked only once.
+
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE EXIT HANDLER FOR SQLWARNING
+SELECT 'Warning caught' AS msg;
+# The INSERT below raises 3 SQL-conditions (warnings). The EXIT HANDLER
+# above must be invoked once (for one condition), but all three conditions
+# must be cleared from the Diagnostics Area.
+INSERT INTO t1 VALUES('qqqq', 'ww', 'eee');
+# The following INSERT will not be executed, because of the EXIT HANDLER.
+INSERT INTO t1 VALUES('zzz', 'xx', 'yyyy');
+END|
+
+CALL p1()|
+msg
+Warning caught
+
+SELECT * FROM t1|
+a b c
+q w e
+
+# Check that SQL-conditions for which SQL-handler has *not* been
+# invoked, are *still* cleared from the Diagnostics Area.
+
+CREATE PROCEDURE p2()
+BEGIN
+DECLARE CONTINUE HANDLER FOR 1292
+SELECT 'Warning 1292 caught' AS msg;
+# The following INSERT raises 6 SQL-warnings with code 1292,
+# and 3 SQL-warnings with code 1264. The CONTINUE HANDLER above must be
+# invoked once, and all nine SQL-warnings must be cleared from
+# the Diagnostics Area.
+INSERT INTO t2
+SELECT
+CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+CALL p2()|
+msg
+Warning 1292 caught
+
+# Check that if there are two equally ranked SQL-handlers to handle
+# SQL-conditions from SQL-statement, only one of them will be invoked.
+
+CREATE PROCEDURE p3()
+BEGIN
+DECLARE CONTINUE HANDLER FOR 1292
+SELECT 'Warning 1292 caught' AS msg;
+DECLARE CONTINUE HANDLER FOR 1264
+SELECT 'Warning 1264 caught' AS msg;
+# The following INSERT raises 6 SQL-warnings with code 1292,
+# and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above
+# must be called, and only once. The SQL Standard does not define, which one
+# should be invoked.
+INSERT INTO t2
+SELECT
+CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+CALL p3()|
+msg
+Warning 1264 caught
+
+# The same as p3, but 1264 comes first.
+
+CREATE PROCEDURE p4()
+BEGIN
+DECLARE CONTINUE HANDLER FOR 1292
+SELECT 'Warning 1292 caught' AS msg;
+DECLARE CONTINUE HANDLER FOR 1264
+SELECT 'Warning 1264 caught' AS msg;
+# The following INSERT raises 4 SQL-warnings with code 1292,
+# and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above
+# must be called, and only once. The SQL Standard does not define, which one
+# should be invoked.
+INSERT INTO t2
+SELECT
+CAST(999999 AS SIGNED INTEGER),
+CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+CALL p4()|
+msg
+Warning 1264 caught
+
+# Check that if a SQL-handler raised its own SQL-conditions, there are
+# preserved after handler exit.
+
+CREATE PROCEDURE p5()
+BEGIN
+DECLARE EXIT HANDLER FOR 1292
+BEGIN
+SELECT 'Handler for 1292 (1)' AS Msg;
+SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1234;
+SHOW WARNINGS;
+SELECT 'Handler for 1292 (2)' AS Msg;
+END;
+INSERT INTO t2
+SELECT
+CAST(999999 AS SIGNED INTEGER),
+CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+CALL p5()|
+Msg
+Handler for 1292 (1)
+Level Code Message
+Warning 1234 Unhandled user-defined warning condition
+Msg
+Handler for 1292 (2)
+Warnings:
+Warning 1234 Unhandled user-defined warning condition
+
+# Check that SQL-conditions are available inside the handler, but
+# cleared after the handler exits.
+
+CREATE PROCEDURE p6()
+BEGIN
+DECLARE CONTINUE HANDLER FOR 1292
+BEGIN
+SHOW WARNINGS;
+SELECT 'Handler for 1292' Msg;
+END;
+INSERT INTO t2
+SELECT
+CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+CALL p6()|
+Level Code Message
+Warning 1292 Truncated incorrect INTEGER value: '1 '
+Warning 1292 Truncated incorrect INTEGER value: '1999999 '
+Warning 1264 Out of range value for column 'a' at row 1
+Warning 1292 Truncated incorrect INTEGER value: '2 '
+Warning 1292 Truncated incorrect INTEGER value: '2999999 '
+Warning 1264 Out of range value for column 'b' at row 1
+Warning 1292 Truncated incorrect INTEGER value: '3 '
+Warning 1292 Truncated incorrect INTEGER value: '3999999 '
+Warning 1264 Out of range value for column 'c' at row 1
+Msg
+Handler for 1292
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+DROP TABLE t1;
+DROP TABLE t2;
+
+# Bug#13059316: ASSERTION FAILURE IN SP_RCONTEXT.CC
+# Check DECLARE statements that raise conditions before handlers
+# are declared.
+
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+CREATE PROCEDURE p1()
+BEGIN
+DECLARE var1 INTEGER DEFAULT 'string';
+DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H1';
+END|
+
+CALL p1()|
+Warnings:
+Warning 1366 Incorrect integer value: 'string' for column 'var1' at row 1
+
+CREATE PROCEDURE p2()
+BEGIN
+DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H2';
+CALL p1();
+END|
+
+CALL p2()|
+H2
+H2
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
=== modified file 'mysql-test/r/sp.result'
--- a/mysql-test/r/sp.result 2011-09-21 11:01:41 +0000
+++ b/mysql-test/r/sp.result 2011-10-14 15:04:41 +0000
@@ -1460,11 +1460,11 @@ create procedure h_es()
deterministic
begin
declare continue handler for 1062 -- ER_DUP_ENTRY
-select 'Outer (good)' as 'h_es';
+select 'Outer (bad)' as 'h_es';
begin
-- integrity constraint violation
declare continue handler for sqlstate '23000'
- select 'Inner (bad)' as 'h_es';
+ select 'Inner (good)' as 'h_es';
insert into t3 values (1);
end;
end|
@@ -1472,11 +1472,11 @@ create procedure h_en()
deterministic
begin
declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA
-select 'Outer (good)' as 'h_en';
+select 'Outer (bad)' as 'h_en';
begin
declare x int;
declare continue handler for sqlstate '02000' -- no data
-select 'Inner (bad)' as 'h_en';
+select 'Inner (good)' as 'h_en';
select a into x from t3 where a = 42;
end;
end|
@@ -1484,10 +1484,10 @@ create procedure h_ew()
deterministic
begin
declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE
-select 'Outer (good)' as 'h_ew';
+select 'Outer (bad)' as 'h_ew';
begin
declare continue handler for sqlwarning
-select 'Inner (bad)' as 'h_ew';
+select 'Inner (good)' as 'h_ew';
insert into t3 values (123456789012);
end;
delete from t3;
@@ -1497,10 +1497,10 @@ create procedure h_ex()
deterministic
begin
declare continue handler for 1062 -- ER_DUP_ENTRY
-select 'Outer (good)' as 'h_ex';
+select 'Outer (bad)' as 'h_ex';
begin
declare continue handler for sqlexception
-select 'Inner (bad)' as 'h_ex';
+select 'Inner (good)' as 'h_ex';
insert into t3 values (1);
end;
end|
@@ -1535,11 +1535,11 @@ begin
-- Note: '02000' is more specific than NOT FOUND ;
-- there might be other not found states
declare continue handler for sqlstate '02000' -- no data
-select 'Outer (good)' as 'h_sn';
+select 'Outer (bad)' as 'h_sn';
begin
declare x int;
declare continue handler for not found
-select 'Inner (bad)' as 'h_sn';
+select 'Inner (good)' as 'h_sn';
select a into x from t3 where a = 42;
end;
end|
@@ -1548,10 +1548,10 @@ deterministic
begin
-- data exception - numeric value out of range
declare continue handler for sqlstate '22003'
- select 'Outer (good)' as 'h_sw';
+ select 'Outer (bad)' as 'h_sw';
begin
declare continue handler for sqlwarning
-select 'Inner (bad)' as 'h_sw';
+select 'Inner (good)' as 'h_sw';
insert into t3 values (123456789012);
end;
delete from t3;
@@ -1562,10 +1562,10 @@ deterministic
begin
-- integrity constraint violation
declare continue handler for sqlstate '23000'
-select 'Outer (good)' as 'h_sx';
+select 'Outer (bad)' as 'h_sx';
begin
declare continue handler for sqlexception
-select 'Inner (bad)' as 'h_sx';
+select 'Inner (good)' as 'h_sx';
insert into t3 values (1);
end;
end|
@@ -1684,16 +1684,16 @@ h_ee
Inner (good)
call h_es()|
h_es
-Outer (good)
+Inner (good)
call h_en()|
h_en
-Outer (good)
+Inner (good)
call h_ew()|
h_ew
-Outer (good)
+Inner (good)
call h_ex()|
h_ex
-Outer (good)
+Inner (good)
call h_se()|
h_se
Inner (good)
@@ -1702,13 +1702,13 @@ h_ss
Inner (good)
call h_sn()|
h_sn
-Outer (good)
+Inner (good)
call h_sw()|
h_sw
-Outer (good)
+Inner (good)
call h_sx()|
h_sx
-Outer (good)
+Inner (good)
call h_ne()|
h_ne
Inner (good)
@@ -2917,8 +2917,8 @@ call bug6900_9074(0)|
sqlexception
sqlexception
call bug6900_9074(1)|
-23000
-23000
+sqlexception
+sqlexception
drop procedure bug6900|
drop procedure bug9074|
drop procedure bug6900_9074|
@@ -7490,6 +7490,4 @@ END $
SELECT f1();
f1()
1
-Warnings:
-Error 1305 FUNCTION test.f1 does not exist
DROP FUNCTION f1;
=== modified file 'mysql-test/suite/funcs_1/r/innodb_storedproc_02.result'
--- a/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result 2011-09-20 12:13:07 +0000
@@ -762,29 +762,21 @@ SELECT @done, @x;
0 1
INSERT INTO temp VALUES('1', NULL);
CALL sp1();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('2', NULL);
CALL sp2();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('3', NULL);
CALL sp3();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
INSERT INTO temp VALUES('4', NULL);
CALL sp4();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
=== modified file 'mysql-test/suite/funcs_1/r/memory_storedproc_02.result'
--- a/mysql-test/suite/funcs_1/r/memory_storedproc_02.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/suite/funcs_1/r/memory_storedproc_02.result 2011-09-20 12:13:07 +0000
@@ -763,29 +763,21 @@ SELECT @done, @x;
0 1
INSERT INTO temp VALUES('1', NULL);
CALL sp1();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('2', NULL);
CALL sp2();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('3', NULL);
CALL sp3();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
INSERT INTO temp VALUES('4', NULL);
CALL sp4();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
=== modified file 'mysql-test/suite/funcs_1/r/myisam_storedproc_02.result'
--- a/mysql-test/suite/funcs_1/r/myisam_storedproc_02.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/suite/funcs_1/r/myisam_storedproc_02.result 2011-09-20 12:13:07 +0000
@@ -763,29 +763,21 @@ SELECT @done, @x;
0 1
INSERT INTO temp VALUES('1', NULL);
CALL sp1();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('2', NULL);
CALL sp2();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 1
INSERT INTO temp VALUES('3', NULL);
CALL sp3();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
INSERT INTO temp VALUES('4', NULL);
CALL sp4();
-Warnings:
-Warning 1265 Data truncated for column 'f2' at row 1
SELECT @done, @x;
@done @x
1 0
=== modified file 'mysql-test/suite/funcs_1/r/storedproc.result'
--- a/mysql-test/suite/funcs_1/r/storedproc.result 2011-07-28 08:31:36 +0000
+++ b/mysql-test/suite/funcs_1/r/storedproc.result 2011-09-20 12:13:07 +0000
@@ -23422,8 +23422,6 @@ CREATE TABLE res_t1(w CHAR, x CHAR);
INSERT INTO res_t1 VALUES('a', 'b');
INSERT INTO res_t1 VALUES('c', 'd');
CALL h1();
-Warnings:
-Warning 1265 Data truncated for column 'x' at row 1
SELECT @done, @x;
@done @x
1 1
@@ -23446,8 +23444,6 @@ CREATE TABLE res_t1(w CHAR, x CHAR);
INSERT INTO res_t1 VALUES('a', 'b');
INSERT INTO res_t1 VALUES('c', 'd');
CALL h1();
-Warnings:
-Warning 1265 Data truncated for column 'x' at row 1
SELECT @done, @x;
@done @x
1 1
=== modified file 'mysql-test/t/sp-error.test'
--- a/mysql-test/t/sp-error.test 2011-09-21 11:01:41 +0000
+++ b/mysql-test/t/sp-error.test 2011-10-14 15:04:41 +0000
@@ -2904,3 +2904,875 @@ SHOW WARNINGS;
DROP TABLE t1;
DROP TABLE t2;
DROP PROCEDURE p1;
+
+--echo
+--echo ###################################################################
+--echo # Tests for the following bugs:
+--echo # - Bug#11763171: 55852 - Possibly inappropriate handler activation.
+--echo # - Bug#11749343: 38806 - Wrong scope for SQL HANDLERS in SP.
+--echo ###################################################################
+--echo
+
+#
+# Structure of SQL-block:
+# BEGIN
+# <Handler declaration block>
+# <Statement block>
+# END
+#
+# Scope of Handler-decl-block is Statement-block.
+# I.e. SQL-conditions thrown in the Handler-decl-block can not be handled by
+# the same block, only by outer SQL-blocks.
+#
+# This rule is recursive, i.e. if a Handler-decl-block has nested SQL-blocks,
+# the SQL-conditions from those nested blocks can not be handled by the this
+# Handler-decl-block, only by outer SQL-blocks.
+#
+
+delimiter |;
+
+--echo
+--echo # -- Check that SQL-conditions thrown by Statement-blocks are
+--echo # -- handled by Handler-decl blocks properly.
+--echo
+
+CREATE PROCEDURE p1()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H2' AS HandlerId;
+
+ SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+END|
+
+--echo
+CALL p1()|
+
+--echo
+--echo # -- Check that SQL-conditions thrown by Statement-blocks are
+--echo # -- handled by Handler-decl blocks properly in case of nested
+--echo # -- SQL-blocks.
+--echo
+
+CREATE PROCEDURE p2()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H2' AS HandlerId;
+
+ BEGIN
+
+ SELECT 'B1' AS BlockId;
+ BEGIN
+
+ SELECT 'B2' AS BlockId;
+ BEGIN
+ SELECT 'B3' AS BlockId;
+ SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+ END;
+
+ END;
+
+ END;
+
+END|
+
+--echo
+CALL p2()|
+
+--echo
+--echo # -- Check SQL-handler resolution rules.
+--echo
+
+CREATE PROCEDURE p3()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H3' AS HandlerId;
+
+ SIGNAL SQLSTATE '01000'; # Should be handled by H3.
+END|
+
+--echo
+CALL p3()|
+--echo
+
+CREATE PROCEDURE p4()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H3' AS HandlerId;
+
+ SIGNAL SQLSTATE '01000'; # Should be handled by H2.
+END|
+
+--echo
+CALL p4()|
+--echo
+
+CREATE PROCEDURE p5()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H2' AS HandlerId;
+
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H3' AS HandlerId;
+
+ SIGNAL SQLSTATE '01000'; # Should be handled by H3.
+ END;
+END|
+
+--echo
+CALL p5()|
+
+--echo
+--echo # -- Check that handlers don't handle its own exceptions.
+--echo
+
+CREATE PROCEDURE p6()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ SELECT 'H1' AS HandlerId;
+ SIGNAL SQLSTATE 'HY000'; # Should *not* be handled by H1.
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # Should be handled by H1.
+END|
+
+--echo
+--error ER_SIGNAL_EXCEPTION
+CALL p6()|
+
+--echo
+--echo # -- Check that handlers don't handle its own warnings.
+--echo
+
+CREATE PROCEDURE p7()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ SELECT 'H1' AS HandlerId;
+ SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE '01000'; # Should be handled by H1.
+END|
+
+--echo
+CALL p7()|
+
+--echo
+--echo # -- Check that conditions for handlers are not handled by the handlers
+--echo # -- from the same block.
+--echo
+
+CREATE PROCEDURE p8()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ SELECT 'H2' AS HandlerId;
+ SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+END|
+
+--echo
+CALL p8()|
+
+--echo
+--echo # -- Check that conditions for handlers are not handled by the handlers
+--echo # -- from the same block even if they are thrown deep down the stack.
+--echo
+
+CREATE PROCEDURE p9()
+BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H1:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H1:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H2:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H2:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H3:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H3:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H4:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H4:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H5:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H5:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H6:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H6:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ SELECT 'H2' AS HandlerId;
+ SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1.
+ END;
+
+ SELECT 'S6' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+ END;
+
+ SELECT 'S5' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S4' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S3' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S2' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+
+END|
+
+--echo
+CALL p9()|
+
+--echo
+--echo # -- Check that handlers are choosen properly in case of deep stack and
+--echo # -- nested SQL-blocks.
+--echo
+
+CREATE PROCEDURE p10()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H2' AS HandlerId;
+
+ BEGIN
+ BEGIN
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H1:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H1:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H2:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H2:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H3:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H3:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H4:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H4:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H5:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H5:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000'
+ SELECT 'Wrong:H6:1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'Wrong:H6:2' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ SELECT 'H2' AS HandlerId;
+ SIGNAL SQLSTATE '01000'; # Should be handled by H1.
+ END;
+
+ SELECT 'S6' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+ END;
+
+ SELECT 'S5' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S4' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S3' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S2' AS SignalId;
+ SIGNAL SQLSTATE 'HY000';
+
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # Should be handled by H2.
+
+ END;
+ END;
+ END;
+END|
+
+--echo
+CALL p10()|
+
+--echo
+--echo # -- Test stored procedure from Peter's mail.
+--echo
+
+CREATE PROCEDURE p11()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H1' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H2' AS HandlerId;
+
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01000', 1249
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ SELECT 'H3' AS HandlerId;
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ SELECT 'H4' AS HandlerId;
+
+ BEGIN
+ SELECT 'H5' AS HandlerId;
+
+ SELECT 'S3' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # H3
+
+ SELECT 'S4' AS SignalId;
+ SIGNAL SQLSTATE '22003'; # H3
+
+ SELECT 'S5' AS SignalId;
+ SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H4
+ END;
+ END;
+
+ SELECT 'S6' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # H1
+
+ SELECT 'S7' AS SignalId;
+ SIGNAL SQLSTATE '22003'; # H1
+
+ SELECT 'S8' AS SignalId;
+ SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H5
+ END;
+
+ SELECT 'S1' AS SignalId;
+ SIGNAL SQLSTATE 'HY000'; # H1
+
+ SELECT 'S2' AS SignalId;
+ SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H2
+END|
+
+--echo
+CALL p11()|
+
+--echo
+--echo # -- Check that runtime stack-trace can be deeper than parsing-time one.
+--echo
+
+CREATE PROCEDURE p12()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01001'
+ BEGIN
+ SELECT 'H1:5' AS HandlerId;
+ SIGNAL SQLSTATE '01002';
+ END;
+ SELECT 'H1:4' AS HandlerId;
+ SIGNAL SQLSTATE '01001';
+ END;
+ SELECT 'H1:3' AS HandlerId;
+ SIGNAL SQLSTATE '01001';
+ END;
+ SELECT 'H1:2' AS HandlerId;
+ SIGNAL SQLSTATE '01001';
+ END;
+ SELECT 'H1:1' AS HandlerId;
+ SIGNAL SQLSTATE '01001';
+ END;
+
+ #########################################################
+
+ DECLARE CONTINUE HANDLER FOR SQLSTATE '01002'
+ SELECT 'OK' AS Msg;
+
+ #########################################################
+
+ BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ SELECT 'H2:5' AS HandlerId;
+ SIGNAL SQLSTATE '01001';
+ END;
+ SELECT 'H2:4' AS HandlerId;
+ SIGNAL SQLSTATE '01000';
+ END;
+ SELECT 'H2:3' AS HandlerId;
+ SIGNAL SQLSTATE '01000';
+ END;
+ SELECT 'H2:2' AS HandlerId;
+ SIGNAL SQLSTATE '01000';
+ END;
+ SELECT 'H2:1' AS HandlerId;
+ SIGNAL SQLSTATE '01000';
+ END;
+
+ #######################################################
+
+ SELECT 'Throw 01000' AS Msg;
+ SIGNAL SQLSTATE '01000';
+ END;
+
+END|
+
+--echo
+CALL p12()|
+
+--echo
+--echo # -- Check that handler-call-frames are removed properly for EXIT
+--echo # -- handlers.
+--echo
+
+CREATE PROCEDURE p13()
+BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING
+ BEGIN
+ DECLARE EXIT HANDLER FOR SQLWARNING
+ BEGIN
+ SELECT 'EXIT handler 3' AS Msg;
+ END;
+
+ SELECT 'CONTINUE handler 2: 1' AS Msg;
+ SIGNAL SQLSTATE '01000';
+ SELECT 'CONTINUE handler 2: 2' AS Msg;
+ END;
+
+ SELECT 'CONTINUE handler 1: 1' AS Msg;
+ SIGNAL SQLSTATE '01000';
+ SELECT 'CONTINUE handler 1: 2' AS Msg;
+ END;
+
+ SELECT 'Throw 01000' AS Msg;
+ SIGNAL SQLSTATE '01000';
+END|
+
+--echo
+CALL p13()|
+
+delimiter ;|
+
+--echo
+--echo # That's it. Cleanup.
+--echo
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+DROP PROCEDURE p7;
+DROP PROCEDURE p8;
+DROP PROCEDURE p9;
+DROP PROCEDURE p10;
+DROP PROCEDURE p11;
+DROP PROCEDURE p12;
+DROP PROCEDURE p13;
+
+--echo
+--echo # Bug#12731619: NESTED SP HANDLERS CAN TRIGGER ASSERTION
+--echo
+
+--disable_warnings
+DROP FUNCTION IF EXISTS f1;
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1(msg VARCHAR(255));
+
+delimiter |;
+CREATE FUNCTION f1() RETURNS INT
+BEGIN
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 1
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 2
+ BEGIN
+ INSERT INTO t1 VALUE('WRONG: Inside H2');
+ RETURN 2;
+ END;
+
+ INSERT INTO t1 VALUE('CORRECT: Inside H1');
+ RETURN 1;
+ END;
+
+ BEGIN
+ DECLARE CONTINUE HANDLER FOR SQLWARNING # handler 3
+ BEGIN
+ INSERT INTO t1 VALUE('WRONG: Inside H3');
+ RETURN 3;
+ END;
+
+ INSERT INTO t1 VALUE('CORRECT: Calling f1()');
+ RETURN f1(); # -- exception here
+ END;
+
+ INSERT INTO t1 VALUE('WRONG: Returning 10');
+ RETURN 10;
+
+END|
+
+delimiter ;|
+
+--echo
+SELECT f1();
+--echo
+SELECT * FROM t1;
+--echo
+
+DROP FUNCTION f1;
+DROP TABLE t1;
+
+
+--echo
+--echo # Check that handled SQL-conditions are properly cleared from DA.
+--echo
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+DROP PROCEDURE IF EXISTS p3;
+DROP PROCEDURE IF EXISTS p4;
+DROP PROCEDURE IF EXISTS p5;
+--enable_warnings
+
+CREATE TABLE t1(a CHAR, b CHAR, c CHAR);
+CREATE TABLE t2(a SMALLINT, b SMALLINT, c SMALLINT);
+
+delimiter |;
+
+--echo
+--echo # Check that SQL-conditions for which SQL-handler has been invoked,
+--echo # are cleared from the Diagnostics Area. Note, there might be several
+--echo # SQL-conditions, but SQL-handler must be invoked only once.
+--echo
+
+CREATE PROCEDURE p1()
+BEGIN
+ DECLARE EXIT HANDLER FOR SQLWARNING
+ SELECT 'Warning caught' AS msg;
+
+ # The INSERT below raises 3 SQL-conditions (warnings). The EXIT HANDLER
+ # above must be invoked once (for one condition), but all three conditions
+ # must be cleared from the Diagnostics Area.
+
+ INSERT INTO t1 VALUES('qqqq', 'ww', 'eee');
+
+ # The following INSERT will not be executed, because of the EXIT HANDLER.
+
+ INSERT INTO t1 VALUES('zzz', 'xx', 'yyyy');
+END|
+
+--echo
+CALL p1()|
+--echo
+SELECT * FROM t1|
+
+--echo
+--echo # Check that SQL-conditions for which SQL-handler has *not* been
+--echo # invoked, are *still* cleared from the Diagnostics Area.
+--echo
+
+CREATE PROCEDURE p2()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR 1292
+ SELECT 'Warning 1292 caught' AS msg;
+
+ # The following INSERT raises 6 SQL-warnings with code 1292,
+ # and 3 SQL-warnings with code 1264. The CONTINUE HANDLER above must be
+ # invoked once, and all nine SQL-warnings must be cleared from
+ # the Diagnostics Area.
+
+ INSERT INTO t2
+ SELECT
+ CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+--echo
+CALL p2()|
+
+--echo
+--echo # Check that if there are two equally ranked SQL-handlers to handle
+--echo # SQL-conditions from SQL-statement, only one of them will be invoked.
+--echo
+
+CREATE PROCEDURE p3()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR 1292
+ SELECT 'Warning 1292 caught' AS msg;
+
+ DECLARE CONTINUE HANDLER FOR 1264
+ SELECT 'Warning 1264 caught' AS msg;
+
+ # The following INSERT raises 6 SQL-warnings with code 1292,
+ # and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above
+ # must be called, and only once. The SQL Standard does not define, which one
+ # should be invoked.
+
+ INSERT INTO t2
+ SELECT
+ CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+--echo
+CALL p3()|
+
+--echo
+--echo # The same as p3, but 1264 comes first.
+--echo
+
+CREATE PROCEDURE p4()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR 1292
+ SELECT 'Warning 1292 caught' AS msg;
+
+ DECLARE CONTINUE HANDLER FOR 1264
+ SELECT 'Warning 1264 caught' AS msg;
+
+ # The following INSERT raises 4 SQL-warnings with code 1292,
+ # and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above
+ # must be called, and only once. The SQL Standard does not define, which one
+ # should be invoked.
+
+ INSERT INTO t2
+ SELECT
+ CAST(999999 AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+--echo
+CALL p4()|
+
+--echo
+--echo # Check that if a SQL-handler raised its own SQL-conditions, there are
+--echo # preserved after handler exit.
+--echo
+
+CREATE PROCEDURE p5()
+BEGIN
+ DECLARE EXIT HANDLER FOR 1292
+ BEGIN
+ SELECT 'Handler for 1292 (1)' AS Msg;
+ SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1234;
+ SHOW WARNINGS;
+ SELECT 'Handler for 1292 (2)' AS Msg;
+ END;
+
+ INSERT INTO t2
+ SELECT
+ CAST(999999 AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+--echo
+CALL p5()|
+
+--echo
+--echo # Check that SQL-conditions are available inside the handler, but
+--echo # cleared after the handler exits.
+--echo
+
+CREATE PROCEDURE p6()
+BEGIN
+ DECLARE CONTINUE HANDLER FOR 1292
+ BEGIN
+ SHOW WARNINGS;
+ SELECT 'Handler for 1292' Msg;
+ END;
+
+ INSERT INTO t2
+ SELECT
+ CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER),
+ CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER);
+END|
+
+--echo
+CALL p6()|
+
+delimiter ;|
+
+--echo
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+DROP PROCEDURE p3;
+DROP PROCEDURE p4;
+DROP PROCEDURE p5;
+DROP PROCEDURE p6;
+DROP TABLE t1;
+DROP TABLE t2;
+
+--echo
+--echo # Bug#13059316: ASSERTION FAILURE IN SP_RCONTEXT.CC
+--echo # Check DECLARE statements that raise conditions before handlers
+--echo # are declared.
+--echo
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+--enable_warnings
+
+delimiter |;
+
+CREATE PROCEDURE p1()
+BEGIN
+ DECLARE var1 INTEGER DEFAULT 'string';
+ DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H1';
+END|
+
+--echo
+CALL p1()|
+--echo
+
+CREATE PROCEDURE p2()
+BEGIN
+ DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H2';
+ CALL p1();
+END|
+
+--echo
+CALL p2()|
+
+delimiter ;|
+
+--echo
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
=== modified file 'mysql-test/t/sp.test'
--- a/mysql-test/t/sp.test 2011-09-21 11:01:41 +0000
+++ b/mysql-test/t/sp.test 2011-10-14 15:04:41 +0000
@@ -1661,31 +1661,27 @@ drop function getcount|
# Test cases for different combinations of condition handlers in nested
# begin-end blocks in stored procedures.
#
-# Note that the standard specifies that the most specific handler should
-# be triggered even if it's an outer handler masked by a less specific
-# handler in an inner block.
+# The SQL standard document says:
+# "8) If there is a general <handler declaration> and a specific
+# <handler declaration> for the same <condition value> in the same scope, then
+# only the specific <handler declaration> is associated with that
+# <condition value>."
+
+# A general handler declaration has SQLWARNING + SQLEXCEPTION + NOT FOUND.
+# A specific handler declaration has SQLSTATE, condition name, or (for MySQL)
+# an errno.
+#
+# So when there are multiple handlers in the same scope, they're all
+# applicable, but the most specific handler should be activated. Notice the
+# standard's exact words: "in the same scope". A specific handler declaration
+# in an outer scope must not be activated instead of a general handler
+# declaration in the inner scope. Previously that was not the case in MySQL.
+#
# Note also that '02000' is more specific than NOT FOUND; there might be
# other '02xxx' states, even if we currently do not issue them in any
# situation (e.g. '02001').
#
-# The combinations we test are these:
-#
-# Inner
-# errcode sqlstate not found sqlwarning sqlexception
-# Outer +------------+------------+------------+------------+------------+
-#errcode | h_ee (i) | h_es (o) | h_en (o) | h_ew (o) | h_ex (o) |
-#sqlstate | h_se (i) | h_ss (i) | h_sn (o) | h_sw (o) | h_sx (o) |
-#not found | h_ne (i) | h_ns (i) | h_nn (i) | | |
-#sqlwarning | h_we (i) | h_ws (i) | | h_ww (i) | |
-#sqlexception | h_xe (i) | h_xs (i) | | | h_xx (i) |
-# +------------+---------------------------------------------------+
-#
-# (i) means that the inner handler is the one that should be invoked,
-# (o) means that the outer handler should be invoked.
-#
-# ('not found', 'sqlwarning' and 'sqlexception' are mutually exclusive, hence
-# no tests for those combinations.)
-#
+# Thus, in all combinations below an inner handler should be activated.
--disable_warnings
drop table if exists t3|
@@ -1734,12 +1730,12 @@ create procedure h_es()
deterministic
begin
declare continue handler for 1062 -- ER_DUP_ENTRY
- select 'Outer (good)' as 'h_es';
+ select 'Outer (bad)' as 'h_es';
begin
-- integrity constraint violation
declare continue handler for sqlstate '23000'
- select 'Inner (bad)' as 'h_es';
+ select 'Inner (good)' as 'h_es';
insert into t3 values (1);
end;
@@ -1749,12 +1745,12 @@ create procedure h_en()
deterministic
begin
declare continue handler for 1329 -- ER_SP_FETCH_NO_DATA
- select 'Outer (good)' as 'h_en';
+ select 'Outer (bad)' as 'h_en';
begin
declare x int;
declare continue handler for sqlstate '02000' -- no data
- select 'Inner (bad)' as 'h_en';
+ select 'Inner (good)' as 'h_en';
select a into x from t3 where a = 42;
end;
@@ -1764,11 +1760,11 @@ create procedure h_ew()
deterministic
begin
declare continue handler for 1264 -- ER_WARN_DATA_OUT_OF_RANGE
- select 'Outer (good)' as 'h_ew';
+ select 'Outer (bad)' as 'h_ew';
begin
declare continue handler for sqlwarning
- select 'Inner (bad)' as 'h_ew';
+ select 'Inner (good)' as 'h_ew';
insert into t3 values (123456789012);
end;
@@ -1780,11 +1776,11 @@ create procedure h_ex()
deterministic
begin
declare continue handler for 1062 -- ER_DUP_ENTRY
- select 'Outer (good)' as 'h_ex';
+ select 'Outer (bad)' as 'h_ex';
begin
declare continue handler for sqlexception
- select 'Inner (bad)' as 'h_ex';
+ select 'Inner (good)' as 'h_ex';
insert into t3 values (1);
end;
@@ -1827,12 +1823,12 @@ begin
-- Note: '02000' is more specific than NOT FOUND ;
-- there might be other not found states
declare continue handler for sqlstate '02000' -- no data
- select 'Outer (good)' as 'h_sn';
+ select 'Outer (bad)' as 'h_sn';
begin
declare x int;
declare continue handler for not found
- select 'Inner (bad)' as 'h_sn';
+ select 'Inner (good)' as 'h_sn';
select a into x from t3 where a = 42;
end;
@@ -1843,11 +1839,11 @@ create procedure h_sw()
begin
-- data exception - numeric value out of range
declare continue handler for sqlstate '22003'
- select 'Outer (good)' as 'h_sw';
+ select 'Outer (bad)' as 'h_sw';
begin
declare continue handler for sqlwarning
- select 'Inner (bad)' as 'h_sw';
+ select 'Inner (good)' as 'h_sw';
insert into t3 values (123456789012);
end;
@@ -1860,11 +1856,11 @@ create procedure h_sx()
begin
-- integrity constraint violation
declare continue handler for sqlstate '23000'
- select 'Outer (good)' as 'h_sx';
+ select 'Outer (bad)' as 'h_sx';
begin
declare continue handler for sqlexception
- select 'Inner (bad)' as 'h_sx';
+ select 'Inner (good)' as 'h_sx';
insert into t3 values (1);
end;
=== modified file 'sql/item_xmlfunc.cc'
--- a/sql/item_xmlfunc.cc 2011-08-02 08:33:54 +0000
+++ b/sql/item_xmlfunc.cc 2011-08-29 11:06:14 +0000
@@ -2493,7 +2493,7 @@ my_xpath_parse_VariableReference(MY_XPAT
LEX *lex;
if ((lex= current_thd->lex) &&
(spc= lex->spcont) &&
- (spv= spc->find_variable(&name)))
+ (spv= spc->find_variable(name, false)))
{
Item_splocal *splocal= new Item_splocal(name, spv->offset, spv->type, 0);
#ifndef DBUG_OFF
=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc 2011-10-05 13:16:38 +0000
+++ b/sql/sp_head.cc 2011-10-14 15:04:41 +0000
@@ -782,7 +782,7 @@ sp_head::~sp_head()
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
delete i;
delete_dynamic(&m_instr);
- m_pcont->destroy();
+ delete m_pcont;
free_items();
/*
@@ -1074,109 +1074,6 @@ void sp_head::recursion_level_error(THD
/**
- Find an SQL handler for any condition (warning or error) after execution
- of a stored routine instruction. Basically, this function looks for an
- appropriate SQL handler in RT-contexts. If an SQL handler is found, it is
- remembered in the RT-context for future activation (the context can be
- inactive at the moment).
-
- If there is no pending condition, the function just returns.
-
- If there was an error during the execution, an SQL handler for it will be
- searched within the current and outer scopes.
-
- There might be several errors in the Warning Info (that's possible by using
- SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL
- handler for the latest (current) error only.
-
- If there was a warning during the execution, an SQL handler for it will be
- searched within the current scope only.
-
- If several warnings were thrown during the execution and there are different
- SQL handlers for them, it is not determined which SQL handler will be chosen.
- Only one SQL handler will be executed.
-
- If warnings and errors were thrown during the execution, the error takes
- precedence. I.e. error handler will be executed. If there is no handler
- for that error, condition will remain unhandled.
-
- According to The Standard (quoting PeterG):
-
- An SQL procedure statement works like this ...
- SQL/Foundation 13.5 <SQL procedure statement>
- (General Rules) (greatly summarized) says:
- (1) Empty diagnostics area, thus clearing the condition.
- (2) Execute statement.
- During execution, if Exception Condition occurs,
- set Condition Area = Exception Condition and stop
- statement.
- During execution, if No Data occurs,
- set Condition Area = No Data Condition and continue
- statement.
- During execution, if Warning occurs,
- and Condition Area is not already full due to
- an earlier No Data condition, set Condition Area
- = Warning and continue statement.
- (3) Finish statement.
- At end of execution, if Condition Area is not
- already full due to an earlier No Data or Warning,
- set Condition Area = Successful Completion.
- In effect, this system means there is a precedence:
- Exception trumps No Data, No Data trumps Warning,
- Warning trumps Successful Completion.
-
- NB: "Procedure statements" include any DDL or DML or
- control statements. So CREATE and DELETE and WHILE
- and CALL and RETURN are procedure statements. But
- DECLARE and END are not procedure statements.
-
- @param thd thread handle
- @param ctx runtime context of the stored routine
-*/
-
-static void
-find_handler_after_execution(THD *thd, sp_rcontext *ctx)
-{
- Diagnostics_area *da= thd->get_stmt_da();
-
- if (thd->is_error())
- {
- if (ctx->find_handler(thd,
- da->sql_errno(),
- da->get_sqlstate(),
- Sql_condition::WARN_LEVEL_ERROR,
- da->message()))
- {
- da->remove_sql_condition(da->get_error_condition());
- }
- }
- else if (thd->get_stmt_da()->current_statement_warn_count())
- {
- Diagnostics_area::Sql_condition_iterator it=
- thd->get_stmt_da()->sql_conditions();
- const Sql_condition *err;
-
- while ((err= it++))
- {
- if (err->get_level() != Sql_condition::WARN_LEVEL_WARN &&
- err->get_level() != Sql_condition::WARN_LEVEL_NOTE)
- continue;
-
- if (ctx->find_handler(thd,
- err->get_sql_errno(),
- err->get_sqlstate(),
- err->get_level(),
- err->get_message_text()))
- {
- da->remove_sql_condition(err);
- break;
- }
- }
- }
-}
-
-
-/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
@@ -1446,19 +1343,10 @@ sp_head::execute(THD *thd, bool merge_da
errors are not catchable by SQL handlers) or the connection has been
killed during execution.
*/
- if (!thd->is_fatal_error && !thd->killed_errno())
+ if (!thd->is_fatal_error && !thd->killed_errno() &&
+ ctx->handle_sql_condition(thd, &ip, i))
{
- /*
- Find SQL handler in the appropriate RT-contexts:
- - warnings can be handled by SQL handlers within
- the current scope only;
- - errors can be handled by any SQL handler from outer scope.
- */
- find_handler_after_execution(thd, ctx);
-
- /* If found, activate handler for the current scope. */
- if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena))
- err_status= FALSE;
+ err_status= FALSE;
}
/* Reset sp_rcontext::end_partial_result_set flag. */
@@ -1534,6 +1422,7 @@ sp_head::execute(THD *thd, bool merge_da
*/
da->opt_clear_warning_info(thd->query_id);
da->copy_sql_conditions_from_wi(thd, &sp_wi);
+ da->remove_marked_sql_conditions();
}
}
@@ -1750,8 +1639,7 @@ sp_head::execute_trigger(THD *thd,
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
{
err_status= TRUE;
goto err_with_cleanup;
@@ -1867,8 +1755,7 @@ sp_head::execute_function(THD *thd, Item
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
{
thd->restore_active_arena(&call_arena, &backup_arena);
err_status= TRUE;
@@ -2087,7 +1974,7 @@ sp_head::execute_procedure(THD *thd, Lis
if (! octx)
{
/* Create a temporary old context. */
- if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) || octx->init(thd))
+ if (!(octx= sp_rcontext::create(thd, m_pcont, NULL)))
{
delete octx; /* Delete octx if it was init() that failed. */
DBUG_RETURN(TRUE);
@@ -2102,8 +1989,7 @@ sp_head::execute_procedure(THD *thd, Lis
thd->spcont->callers_arena= thd;
}
- if (!(nctx= new sp_rcontext(m_pcont, NULL, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
{
delete nctx; /* Delete nctx if it was init() that failed. */
thd->spcont= save_spcont;
@@ -2436,7 +2322,7 @@ sp_head::backpatch(sp_label *lab)
if (bp->lab == lab)
{
DBUG_PRINT("info", ("backpatch: (m_ip %d, label 0x%lx <%s>) to dest %d",
- bp->instr->m_ip, (ulong) lab, lab->name, dest));
+ bp->instr->m_ip, (ulong) lab, lab->name.str, dest));
bp->instr->backpatch(dest, lab->ctx);
}
}
@@ -3118,7 +3004,7 @@ sp_lex_keeper::reset_lex_and_exec_core(T
sp_instr class functions
*/
-uint sp_instr::get_cont_dest()
+uint sp_instr::get_cont_dest() const
{
return (m_ip+1);
}
@@ -3326,7 +3212,7 @@ sp_instr_set_trigger_field::print(String
sp_instr_opt_meta
*/
-uint sp_instr_opt_meta::get_cont_dest()
+uint sp_instr_opt_meta::get_cont_dest() const
{
return m_cont_dest;
}
@@ -3544,14 +3430,12 @@ int
sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hpush_jump::execute");
- List_iterator_fast<sp_condition_value> li(m_cond);
- sp_condition_value *p;
- while ((p= li++))
- thd->spcont->push_handler(p, m_ip+1, m_type);
+ int ret= thd->spcont->push_handler(m_handler, m_ip + 1);
*nextp= m_dest;
- DBUG_RETURN(0);
+
+ DBUG_RETURN(ret);
}
@@ -3561,27 +3445,22 @@ sp_instr_hpush_jump::print(String *str)
/* hpush_jump dest fsize type */
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21))
return;
+
str->qs_append(STRING_WITH_LEN("hpush_jump "));
str->qs_append(m_dest);
str->qs_append(' ');
str->qs_append(m_frame);
- switch (m_type) {
- case SP_HANDLER_NONE:
- str->qs_append(STRING_WITH_LEN(" NONE")); // This would be a bug
- break;
- case SP_HANDLER_EXIT:
+
+ switch (m_handler->type) {
+ case sp_handler::EXIT:
str->qs_append(STRING_WITH_LEN(" EXIT"));
break;
- case SP_HANDLER_CONTINUE:
+ case sp_handler::CONTINUE:
str->qs_append(STRING_WITH_LEN(" CONTINUE"));
break;
- case SP_HANDLER_UNDO:
- str->qs_append(STRING_WITH_LEN(" UNDO"));
- break;
default:
- // This would be a bug as well
- str->qs_append(STRING_WITH_LEN(" UNKNOWN:"));
- str->qs_append(m_type);
+ // The handler type must be either CONTINUE or EXIT.
+ DBUG_ASSERT(0);
}
}
@@ -3609,7 +3488,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *s
above, so we start on m_dest+1 here.
m_opt_hpop is the hpop marking the end of the handler scope.
*/
- if (m_type == SP_HANDLER_CONTINUE)
+ if (m_handler->type == sp_handler::CONTINUE)
{
for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++)
sp->add_mark_lead(scope_ip, leads);
@@ -3651,13 +3530,11 @@ int
sp_instr_hreturn::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hreturn::execute");
- if (m_dest)
- *nextp= m_dest;
- else
- {
- *nextp= thd->spcont->pop_hstack();
- }
- thd->spcont->exit_handler();
+
+ uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da());
+
+ *nextp= m_dest ? m_dest : continue_ip;
+
DBUG_RETURN(0);
}
@@ -3669,12 +3546,17 @@ sp_instr_hreturn::print(String *str)
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9))
return;
str->qs_append(STRING_WITH_LEN("hreturn "));
- str->qs_append(m_frame);
if (m_dest)
{
- str->qs_append(' ');
+ // NOTE: this is legacy: hreturn instruction for EXIT handler
+ // should print out 0 as frame index.
+ str->qs_append(STRING_WITH_LEN("0 "));
str->qs_append(m_dest);
}
+ else
+ {
+ str->qs_append(m_frame);
+ }
}
@@ -3709,38 +3591,30 @@ sp_instr_cpush::execute(THD *thd, uint *
Query_arena backup_arena;
DBUG_ENTER("sp_instr_cpush::execute");
- /*
- We should create cursors in the callers arena, as
- it could be (and usually is) used in several instructions.
- */
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena);
-
- thd->spcont->push_cursor(&m_lex_keeper, this);
-
- thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena);
+ int ret= thd->spcont->push_cursor(&m_lex_keeper, this);
*nextp= m_ip+1;
- DBUG_RETURN(0);
+ DBUG_RETURN(ret);
}
void
sp_instr_cpush::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cpush name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cpush "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3828,19 +3702,19 @@ sp_instr_copen::exec_core(THD *thd, uint
void
sp_instr_copen::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* copen name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("copen "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3870,19 +3744,19 @@ sp_instr_cclose::execute(THD *thd, uint
void
sp_instr_cclose::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cclose name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cclose "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3913,19 +3787,19 @@ sp_instr_cfetch::print(String *str)
{
List_iterator_fast<sp_variable> li(m_varlist);
sp_variable *pv;
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cfetch name@offset vars... */
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cfetch "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h 2011-08-12 07:18:41 +0000
+++ b/sql/sp_head.h 2011-08-25 06:41:21 +0000
@@ -25,6 +25,7 @@
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_class.h" // THD, set_var.h: THD
#include "set_var.h" // Item
+#include "sp_pcontext.h" // sp_pcontext
#include <stddef.h>
@@ -49,12 +50,9 @@ sp_map_item_type(enum enum_field_types t
uint
sp_get_flags_for_command(LEX *lex);
-struct sp_label;
class sp_instr;
class sp_instr_opt_meta;
class sp_instr_jump_if_not;
-struct sp_condition_value;
-struct sp_variable;
/*************************************************************************/
@@ -216,15 +214,11 @@ private:
uint32 unsafe_flags;
public:
- inline Stored_program_creation_ctx *get_creation_ctx()
- {
- return m_creation_ctx;
- }
+ Stored_program_creation_ctx *get_creation_ctx()
+ { return m_creation_ctx; }
- inline void set_creation_ctx(Stored_program_creation_ctx *creation_ctx)
- {
- m_creation_ctx= creation_ctx->clone(mem_root);
- }
+ void set_creation_ctx(Stored_program_creation_ctx *creation_ctx)
+ { m_creation_ctx= creation_ctx->clone(mem_root); }
longlong m_created;
longlong m_modified;
@@ -320,13 +314,11 @@ public:
int
add_instr(sp_instr *instr);
- inline uint
+ uint
instructions()
- {
- return m_instr.elements;
- }
+ { return m_instr.elements; }
- inline sp_instr *
+ sp_instr *
last_instruction()
{
sp_instr *i;
@@ -415,7 +407,7 @@ public:
void recursion_level_error(THD *thd);
- inline sp_instr *
+ sp_instr *
get_instr(uint i)
{
sp_instr *ip;
@@ -584,7 +576,7 @@ public:
Get the continuation destination of this instruction.
@return the continuation destination
*/
- virtual uint get_cont_dest();
+ virtual uint get_cont_dest() const;
/*
Execute core function of instruction after all preparations (e.g.
@@ -685,17 +677,13 @@ public:
int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables,
sp_instr* instr);
- inline uint sql_command() const
- {
- return (uint)m_lex->sql_command;
- }
+ uint sql_command() const
+ { return (uint)m_lex->sql_command; }
void disable_query_cache()
- {
- m_lex->safe_to_cache_query= 0;
- }
-private:
+ { m_lex->safe_to_cache_query= 0; }
+private:
LEX *m_lex;
/**
Indicates whenever this sp_lex_keeper instance responsible
@@ -856,7 +844,7 @@ public:
virtual void set_destination(uint old_dest, uint new_dest)
= 0;
- virtual uint get_cont_dest();
+ virtual uint get_cont_dest() const;
protected:
@@ -1007,15 +995,21 @@ class sp_instr_hpush_jump : public sp_in
public:
- sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp)
- : sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp), m_opt_hpop(0)
+ sp_instr_hpush_jump(uint ip,
+ sp_pcontext *ctx,
+ sp_handler *handler)
+ :sp_instr_jump(ip, ctx),
+ m_handler(handler),
+ m_opt_hpop(0),
+ m_frame(ctx->current_var_count())
{
- m_cond.empty();
+ DBUG_ASSERT(m_handler->condition_values.elements == 0);
}
virtual ~sp_instr_hpush_jump()
{
- m_cond.empty();
+ m_handler->condition_values.empty();
+ m_handler= NULL;
}
virtual int execute(THD *thd, uint *nextp);
@@ -1039,18 +1033,22 @@ public:
m_opt_hpop= dest;
}
- inline void add_condition(sp_condition_value *cond)
- {
- m_cond.push_front(cond);
- }
+ void add_condition(sp_condition_value *condition_value)
+ { m_handler->condition_values.push_back(condition_value); }
+
+ sp_handler *get_handler()
+ { return m_handler; }
private:
+ /// Handler.
+ sp_handler *m_handler;
- int m_type; ///< Handler type
- uint m_frame;
- uint m_opt_hpop; // hpop marking end of handler scope.
- List<sp_condition_value> m_cond;
+ /// hpop marking end of handler scope.
+ uint m_opt_hpop;
+ // This attribute is needed for SHOW PROCEDURE CODE only (i.e. it's needed in
+ // debug version only). It's used in print().
+ uint m_frame;
}; // class sp_instr_hpush_jump : public sp_instr_jump
@@ -1085,9 +1083,9 @@ class sp_instr_hreturn : public sp_instr
void operator=(sp_instr_hreturn &);
public:
-
- sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp)
- : sp_instr_jump(ip, ctx), m_frame(fp)
+ sp_instr_hreturn(uint ip, sp_pcontext *ctx)
+ :sp_instr_jump(ip, ctx),
+ m_frame(ctx->current_var_count())
{}
virtual ~sp_instr_hreturn()
@@ -1106,9 +1104,9 @@ public:
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
private:
-
+ // This attribute is needed for SHOW PROCEDURE CODE only (i.e. it's needed in
+ // debug version only). It's used in print().
uint m_frame;
-
}; // class sp_instr_hreturn : public sp_instr_jump
=== modified file 'sql/sp_pcontext.cc'
--- a/sql/sp_pcontext.cc 2011-08-02 17:47:00 +0000
+++ b/sql/sp_pcontext.cc 2011-09-20 12:54:31 +0000
@@ -18,133 +18,87 @@
#include "sp_pcontext.h"
#include "sp_head.h"
-/* Initial size for the dynamic arrays in sp_pcontext */
-#define PCONTEXT_ARRAY_INIT_ALLOC 16
-/* Increment size for the dynamic arrays in sp_pcontext */
-#define PCONTEXT_ARRAY_INCREMENT_ALLOC 8
-
-/*
- Sanity check for SQLSTATEs. Will not check if it's really an existing
- state (there are just too many), but will check length and bad characters.
- Returns TRUE if it's ok, FALSE if it's bad.
-*/
-bool
-sp_cond_check(LEX_STRING *sqlstate)
-{
- int i;
- const char *p;
-
- if (sqlstate->length != 5)
- return FALSE;
- for (p= sqlstate->str, i= 0 ; i < 5 ; i++)
- {
- char c = p[i];
-
- if ((c < '0' || '9' < c) &&
- (c < 'A' || 'Z' < c))
- return FALSE;
- }
- /* SQLSTATE class '00' : completion condition */
- if (strncmp(sqlstate->str, "00", 2) == 0)
- return FALSE;
- return TRUE;
+
+bool sp_condition_value::equals(const sp_condition_value *cv) const
+{
+ DBUG_ASSERT(cv);
+
+ if (this == cv)
+ return true;
+
+ if (type != cv->type)
+ return false;
+
+ switch (type)
+ {
+ case sp_condition_value::ERROR_CODE:
+ return (mysqlerr == cv->mysqlerr);
+
+ case sp_condition_value::SQLSTATE:
+ return (strcmp(sql_state, cv->sql_state) == 0);
+
+ default:
+ return true;
+ }
+}
+
+
+void sp_pcontext::init(uint var_offset,
+ uint cursor_offset,
+ int num_case_expressions)
+{
+ m_var_offset= var_offset;
+ m_cursor_offset= cursor_offset;
+ m_num_case_exprs= num_case_expressions;
+
+ m_labels.empty();
}
+
sp_pcontext::sp_pcontext()
: Sql_alloc(),
- m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
- m_context_handlers(0), m_parent(NULL), m_pboundary(0),
+ m_max_var_index(0), m_max_cursor_index(0),
+ m_parent(NULL), m_pboundary(0),
m_scope(REGULAR_SCOPE)
{
- (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_conds, sizeof(sp_condition *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_handlers, sizeof(sp_condition_value *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- m_label.empty();
- m_children.empty();
-
- m_var_offset= m_cursor_offset= 0;
- m_num_case_exprs= 0;
+ init(0, 0, 0);
}
+
sp_pcontext::sp_pcontext(sp_pcontext *prev, sp_pcontext::enum_scope scope)
: Sql_alloc(),
- m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
- m_context_handlers(0), m_parent(prev), m_pboundary(0),
+ m_max_var_index(0), m_max_cursor_index(0),
+ m_parent(prev), m_pboundary(0),
m_scope(scope)
{
- (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_conds, sizeof(sp_condition *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_handlers, sizeof(sp_condition_value *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- m_label.empty();
- m_children.empty();
-
- m_var_offset= prev->m_var_offset + prev->m_max_var_index;
- m_cursor_offset= prev->current_cursor_count();
- m_num_case_exprs= prev->get_num_case_exprs();
-}
-
-void
-sp_pcontext::destroy()
-{
- List_iterator_fast<sp_pcontext> li(m_children);
- sp_pcontext *child;
-
- while ((child= li++))
- child->destroy();
-
- m_children.empty();
- m_label.empty();
- delete_dynamic(&m_vars);
- delete_dynamic(&m_case_expr_id_lst);
- delete_dynamic(&m_conds);
- delete_dynamic(&m_cursors);
- delete_dynamic(&m_handlers);
+ init(prev->m_var_offset + prev->m_max_var_index,
+ prev->current_cursor_count(),
+ prev->get_num_case_exprs());
}
-sp_pcontext *
-sp_pcontext::push_context(sp_pcontext::enum_scope scope)
+
+sp_pcontext::~sp_pcontext()
+{
+ for (int i= 0; i < m_children.elements(); ++i)
+ delete m_children.at(i);
+}
+
+
+sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
{
- sp_pcontext *child= new sp_pcontext(this, scope);
+ sp_pcontext *child= new (thd->mem_root) sp_pcontext(this, scope);
if (child)
- m_children.push_back(child);
+ m_children.append(child);
return child;
}
-sp_pcontext *
-sp_pcontext::pop_context()
+
+sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
- uint submax= max_handler_index();
- if (submax > m_parent->m_max_handler_index)
- m_parent->m_max_handler_index= submax;
-
- submax= max_cursor_index();
+ uint submax= max_cursor_index();
if (submax > m_parent->m_max_cursor_index)
m_parent->m_max_cursor_index= submax;
@@ -154,142 +108,118 @@ sp_pcontext::pop_context()
return m_parent;
}
-uint
-sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive)
+
+uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const
{
uint n= 0;
- sp_pcontext *pctx= this;
- sp_pcontext *last_ctx= NULL;
+ const sp_pcontext *pctx= this;
+ const sp_pcontext *last_ctx= NULL;
while (pctx && pctx != ctx)
{
- n+= pctx->m_context_handlers;
+ n+= pctx->m_handlers.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_context_handlers : n);
+ return (exclusive && last_ctx ? n - last_ctx->m_handlers.elements() : n);
return 0; // Didn't find ctx
}
-uint
-sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive)
+
+uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
{
uint n= 0;
- sp_pcontext *pctx= this;
- sp_pcontext *last_ctx= NULL;
+ const sp_pcontext *pctx= this;
+ const sp_pcontext *last_ctx= NULL;
while (pctx && pctx != ctx)
{
- n+= pctx->m_cursors.elements;
+ n+= pctx->m_cursors.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements : n);
+ return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements() : n);
return 0; // Didn't find ctx
}
-/*
- This does a linear search (from newer to older variables, in case
- we have shadowed names).
- It's possible to have a more efficient allocation and search method,
- but it might not be worth it. The typical number of parameters and
- variables will in most cases be low (a handfull).
- ...and, this is only called during parsing.
-*/
-sp_variable *
-sp_pcontext::find_variable(LEX_STRING *name, my_bool scoped)
+
+sp_variable *sp_pcontext::find_variable(LEX_STRING name,
+ bool current_scope_only) const
{
- uint i= m_vars.elements - m_pboundary;
+ uint i= m_vars.elements() - m_pboundary;
while (i--)
{
- sp_variable *p;
+ sp_variable *p= m_vars.at(i);
- get_dynamic(&m_vars, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
+ (const uchar *)name.str, name.length,
(const uchar *)p->name.str, p->name.length) == 0)
{
return p;
}
}
- if (!scoped && m_parent)
- return m_parent->find_variable(name, scoped);
- return NULL;
-}
-
-/*
- Find a variable by offset from the top.
- This used for two things:
- - When evaluating parameters at the beginning, and setting out parameters
- at the end, of invokation. (Top frame only, so no recursion then.)
- - For printing of sp_instr_set. (Debug mode only.)
-*/
-sp_variable *
-sp_pcontext::find_variable(uint offset)
-{
- if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements)
- { // This frame
- sp_variable *p;
-
- get_dynamic(&m_vars, (uchar*)&p, offset - m_var_offset);
- return p;
- }
- if (m_parent)
- return m_parent->find_variable(offset); // Some previous frame
- return NULL; // index out of bounds
-}
-
-sp_variable *
-sp_pcontext::push_variable(LEX_STRING *name, enum enum_field_types type,
- sp_variable::enum_mode mode)
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_variable(name, false) :
+ NULL;
+}
+
+
+sp_variable *sp_pcontext::find_variable(uint offset) const
+{
+ if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements())
+ return m_vars.at(offset - m_var_offset); // This frame
+
+ return m_parent ?
+ m_parent->find_variable(offset) : // Some previous frame
+ NULL; // Index out of bounds
+}
+
+
+sp_variable *sp_pcontext::add_variable(THD *thd,
+ LEX_STRING name,
+ enum enum_field_types type,
+ sp_variable::enum_mode mode)
{
- sp_variable *p= (sp_variable *)sql_alloc(sizeof(sp_variable));
+ sp_variable *p=
+ new (thd->mem_root) sp_variable(name, type,mode, current_var_count());
if (!p)
return NULL;
++m_max_var_index;
- p->name.str= name->str;
- p->name.length= name->length;
- p->type= type;
- p->mode= mode;
- p->offset= current_var_count();
- p->dflt= NULL;
- if (insert_dynamic(&m_vars, &p))
- return NULL;
- return p;
+ return m_vars.append(p) ? NULL : p;
}
-sp_label *
-sp_pcontext::push_label(char *name, uint ip)
+sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip)
{
- sp_label *lab = (sp_label *)sql_alloc(sizeof(sp_label));
+ sp_label *label=
+ new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this);
- if (lab)
- {
- lab->name= name;
- lab->ip= ip;
- lab->type= sp_label::IMPLICIT;
- lab->ctx= this;
- m_label.push_front(lab);
- }
- return lab;
+ if (!label)
+ return NULL;
+
+ m_labels.push_front(label);
+
+ return label;
}
-sp_label *
-sp_pcontext::find_label(char *name)
+
+sp_label *sp_pcontext::find_label(LEX_STRING name)
{
- List_iterator_fast<sp_label> li(m_label);
+ List_iterator_fast<sp_label> li(m_labels);
sp_label *lab;
while ((lab= li++))
- if (my_strcasecmp(system_charset_info, name, lab->name) == 0)
+ {
+ if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
return lab;
+ }
/*
Note about exception handlers.
@@ -299,159 +229,253 @@ sp_pcontext::find_label(char *name)
In short, a DECLARE HANDLER block can not refer
to labels from the parent context, as they are out of scope.
*/
- if (m_parent && (m_scope == REGULAR_SCOPE))
- return m_parent->find_label(name);
- return NULL;
+ return (m_parent && (m_scope == REGULAR_SCOPE)) ?
+ m_parent->find_label(name) :
+ NULL;
}
-int
-sp_pcontext::push_cond(LEX_STRING *name, sp_condition_value *val)
+
+bool sp_pcontext::add_condition(THD *thd,
+ LEX_STRING name,
+ sp_condition_value *value)
{
- sp_condition *p= (sp_condition *)sql_alloc(sizeof(sp_condition));
+ sp_condition *p= new (thd->mem_root) sp_condition(name, value);
if (p == NULL)
- return 1;
- p->name.str= name->str;
- p->name.length= name->length;
- p->val= val;
- return insert_dynamic(&m_conds, &p);
+ return true;
+
+ return m_conditions.append(p);
}
-/*
- See comment for find_variable() above
-*/
-sp_condition_value *
-sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
+
+sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
+ bool current_scope_only) const
{
- uint i= m_conds.elements;
+ uint i= m_conditions.elements();
while (i--)
{
- sp_condition *p;
+ sp_condition *p= m_conditions.at(i);
- get_dynamic(&m_conds, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)p->name.str, p->name.length) == 0)
+ (const uchar *) name.str, name.length,
+ (const uchar *) p->name.str, p->name.length) == 0)
{
- return p->val;
+ return p->value;
}
}
- if (!scoped && m_parent)
- return m_parent->find_cond(name, scoped);
- return NULL;
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_condition(name, false) :
+ NULL;
}
-/*
- This only searches the current context, for error checking of
- duplicates.
- Returns TRUE if found.
-*/
-bool
-sp_pcontext::find_handler(sp_condition_value *cond)
+
+sp_handler *sp_pcontext::add_handler(THD *thd,
+ sp_handler::enum_type type)
{
- uint i= m_handlers.elements;
+ sp_handler *h= new (thd->mem_root) sp_handler(type);
- while (i--)
+ if (!h)
+ return NULL;
+
+ return m_handlers.append(h) ? NULL : h;
+}
+
+
+bool sp_pcontext::check_duplicate_handler(
+ const sp_condition_value *cond_value) const
+{
+ for (int i= 0; i < m_handlers.elements(); ++i)
{
- sp_condition_value *p;
+ sp_handler *h= m_handlers.at(i);
- get_dynamic(&m_handlers, (uchar*)&p, i);
- if (cond->type == p->type)
+ List_iterator_fast<sp_condition_value> li(h->condition_values);
+ sp_condition_value *cv;
+
+ while ((cv= li++))
{
- switch (p->type)
+ if (cond_value->equals(cv))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+sp_handler*
+sp_pcontext::find_handler(const char *sql_state,
+ uint sql_errno,
+ Sql_condition::enum_warning_level level) const
+{
+ sp_handler *found_handler= NULL;
+ sp_condition_value *found_cv= NULL;
+
+ for (int i= 0; i < m_handlers.elements(); ++i)
+ {
+ sp_handler *h= m_handlers.at(i);
+
+ List_iterator_fast<sp_condition_value> li(h->condition_values);
+ sp_condition_value *cv;
+
+ while ((cv= li++))
+ {
+ switch (cv->type)
{
- case sp_condition_value::number:
- if (cond->mysqlerr == p->mysqlerr)
- return TRUE;
- break;
- case sp_condition_value::state:
- if (strcmp(cond->sqlstate, p->sqlstate) == 0)
- return TRUE;
- break;
- default:
- return TRUE;
+ case sp_condition_value::ERROR_CODE:
+ if (sql_errno == cv->mysqlerr &&
+ (!found_cv ||
+ found_cv->type > sp_condition_value::ERROR_CODE))
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::SQLSTATE:
+ if (strcmp(sql_state, cv->sql_state) == 0 &&
+ (!found_cv ||
+ found_cv->type > sp_condition_value::SQLSTATE))
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::WARNING:
+ if ((is_sqlstate_warning(sql_state) ||
+ level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::NOT_FOUND:
+ if (is_sqlstate_not_found(sql_state) && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::EXCEPTION:
+ if (is_sqlstate_exception(sql_state) &&
+ level == Sql_condition::WARN_LEVEL_ERROR && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
}
}
}
- return FALSE;
+
+ if (found_handler)
+ return found_handler;
+
+
+ // There is no appropriate handler in this parsing context. We need to look up
+ // in parent contexts. There might be two cases here:
+ //
+ // 1. The current context has REGULAR_SCOPE. That means, it's a simple
+ // BEGIN..END block:
+ // ...
+ // BEGIN
+ // ... # We're here.
+ // END
+ // ...
+ // In this case we simply call find_handler() on parent's context recursively.
+ //
+ // 2. The current context has HANDLER_SCOPE. That means, we're inside an
+ // SQL-handler block:
+ // ...
+ // DECLARE ... HANDLER FOR ...
+ // BEGIN
+ // ... # We're here.
+ // END
+ // ...
+ // In this case we can not just call parent's find_handler(), because
+ // parent's handler don't catch conditions from this scope. Instead, we should
+ // try to find first parent context (we might have nested handler
+ // declarations), which has REGULAR_SCOPE (i.e. which is regular BEGIN..END
+ // block).
+
+ const sp_pcontext *p= this;
+
+ while (p && p->m_scope == HANDLER_SCOPE)
+ p= p->m_parent;
+
+ if (!p || !p->m_parent)
+ return NULL;
+
+ return p->m_parent->find_handler(sql_state, sql_errno, level);
}
-int
-sp_pcontext::push_cursor(LEX_STRING *name)
+
+bool sp_pcontext::add_cursor(LEX_STRING name)
{
- LEX_STRING n;
+ if (m_cursors.elements() == (int) m_max_cursor_index)
+ ++m_max_cursor_index;
- if (m_cursors.elements == m_max_cursor_index)
- m_max_cursor_index+= 1;
- n.str= name->str;
- n.length= name->length;
- return insert_dynamic(&m_cursors, &n);
+ return m_cursors.append(name);
}
-/*
- See comment for find_variable() above
-*/
-my_bool
-sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
+
+bool sp_pcontext::find_cursor(LEX_STRING name,
+ uint *poff,
+ bool current_scope_only) const
{
- uint i= m_cursors.elements;
+ uint i= m_cursors.elements();
while (i--)
{
- LEX_STRING n;
+ LEX_STRING n= m_cursors.at(i);
- get_dynamic(&m_cursors, (uchar*)&n, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)n.str, n.length) == 0)
+ (const uchar *) name.str, name.length,
+ (const uchar *) n.str, n.length) == 0)
{
*poff= m_cursor_offset + i;
- return TRUE;
+ return true;
}
}
- if (!scoped && m_parent)
- return m_parent->find_cursor(name, poff, scoped);
- return FALSE;
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_cursor(name, poff, false) :
+ false;
}
-void
-sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
+void sp_pcontext::retrieve_field_definitions(
+ List<Create_field> *field_def_lst) const
{
/* Put local/context fields in the result list. */
- for (uint i = 0; i < m_vars.elements; ++i)
+ for (int i= 0; i < m_vars.elements(); ++i)
{
- sp_variable *var_def;
- get_dynamic(&m_vars, (uchar*) &var_def, i);
+ sp_variable *var_def= m_vars.at(i);
field_def_lst->push_back(&var_def->field_def);
}
/* Put the fields of the enclosed contexts in the result list. */
- List_iterator_fast<sp_pcontext> li(m_children);
- sp_pcontext *ctx;
-
- while ((ctx = li++))
- ctx->retrieve_field_definitions(field_def_lst);
+ for (int i= 0; i < m_children.elements(); ++i)
+ m_children.at(i)->retrieve_field_definitions(field_def_lst);
}
-/*
- Find a cursor by offset from the top.
- This is only used for debugging.
-*/
-my_bool
-sp_pcontext::find_cursor(uint offset, LEX_STRING *n)
+
+const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
{
if (m_cursor_offset <= offset &&
- offset < m_cursor_offset + m_cursors.elements)
- { // This frame
- get_dynamic(&m_cursors, (uchar*)n, offset - m_cursor_offset);
- return TRUE;
- }
- if (m_parent)
- return m_parent->find_cursor(offset, n); // Some previous frame
- return FALSE; // index out of bounds
+ offset < m_cursor_offset + m_cursors.elements())
+ {
+ return &m_cursors.at(offset - m_cursor_offset); // This frame
+ }
+
+ return m_parent ?
+ m_parent->find_cursor(offset) : // Some previous frame
+ NULL; // Index out of bounds
}
=== modified file 'sql/sp_pcontext.h'
--- a/sql/sp_pcontext.h 2011-08-02 08:34:30 +0000
+++ b/sql/sp_pcontext.h 2011-09-20 12:54:31 +0000
@@ -20,9 +20,15 @@
#include "sql_string.h" // LEX_STRING
#include "mysql_com.h" // enum_field_types
#include "field.h" // Create_field
+#include "sql_array.h" // Dynamic_array
-struct sp_variable
+
+/// This class represents a stored program variable or a parameter
+/// (also referenced as 'SP-variable').
+
+class sp_variable : public Sql_alloc
{
+public:
enum enum_mode
{
MODE_IN,
@@ -30,34 +36,52 @@ struct sp_variable
MODE_INOUT
};
+ /// Name of the SP-variable.
LEX_STRING name;
+
+ /// Field-type of the SP-variable.
enum enum_field_types type;
+
+ /// Mode of the SP-variable.
enum_mode mode;
-
- /*
- offset -- this the index to the variable's value in the runtime frame.
- This is calculated during parsing and used when creating sp_instr_set
- instructions and Item_splocal items.
- I.e. values are set/referred by array indexing in runtime.
- */
+
+ /// The index to the variable's value in the runtime frame.
+ ///
+ /// It is calculated during parsing and used when creating sp_instr_set
+ /// instructions and Item_splocal items. I.e. values are set/referred by
+ /// array indexing in runtime.
uint offset;
- Item *dflt;
+ /// Default value of the SP-variable (if any).
+ Item *default_value;
+
+ /// Full type information (field meta-data) of the SP-variable.
Create_field field_def;
+
+public:
+ sp_variable(LEX_STRING _name, enum_field_types _type, enum_mode _mode,
+ uint _offset)
+ :Sql_alloc(),
+ name(_name),
+ type(_type),
+ mode(_mode),
+ offset(_offset),
+ default_value(NULL)
+ { }
};
+///////////////////////////////////////////////////////////////////////////
-/*
- An SQL/PSM label. Can refer to the identifier used with the
- "label_name:" construct which may precede some SQL/PSM statements, or
- to an implicit implementation-dependent identifier which the parser
- inserts before a high-level flow control statement such as
- IF/WHILE/REPEAT/LOOP, when such statement is rewritten into
- a combination of low-level jump/jump_if instructions and labels.
-*/
+/// This class represents an SQL/PSM label. Can refer to the identifier
+/// used with the "label_name:" construct which may precede some SQL/PSM
+/// statements, or to an implicit implementation-dependent identifier which
+/// the parser inserts before a high-level flow control statement such as
+/// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a
+/// combination of low-level jump/jump_if instructions and labels.
-struct sp_label
+class sp_label : public Sql_alloc
{
+public:
enum enum_type
{
/// Implicit label generated by parser.
@@ -70,388 +94,467 @@ struct sp_label
ITERATION
};
- char *name;
- uint ip; // Instruction index
- enum_type type; // begin/iter or ref/free
- class sp_pcontext *ctx; // The label's context
-};
+ /// Name of the label.
+ LEX_STRING name;
-struct sp_condition_value
-{
- enum { number, state, warning, notfound, exception } type;
- char sqlstate[SQLSTATE_LENGTH+1];
- uint mysqlerr;
-};
+ /// Instruction pointer of the label.
+ uint ip;
-/*
- Sanity check for SQLSTATEs. Will not check if it's really an existing
- state (there are just too many), but will check length bad characters.
-*/
-extern bool
-sp_cond_check(LEX_STRING *sqlstate);
+ /// Type of the label.
+ enum_type type;
-struct sp_condition
-{
- LEX_STRING name;
- sp_condition_value *val;
+ /// Scope of the label.
+ class sp_pcontext *ctx;
+
+public:
+ sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx)
+ :Sql_alloc(),
+ name(_name),
+ ip(_ip),
+ type(_type),
+ ctx(_ctx)
+ { }
};
-/**
- The parse-time context, used to keep track of declared variables/parameters,
- conditions, handlers, cursors and labels, during parsing.
- sp_contexts are organized as a tree, with one object for each begin-end
- block, one object for each exception handler,
- plus a root-context for the parameters.
- This is used during parsing for looking up defined names (e.g. declared
- variables and visible labels), for error checking, and to calculate offsets
- to be used at runtime. (During execution variable values, active handlers
- and cursors, etc, are referred to by an index in a stack.)
- Parsing contexts for exception handlers limit the visibility of labels.
- The pcontext tree is also kept during execution and is used for error
- checking (e.g. correct number of parameters), and in the future, used by
- the debugger.
-*/
+///////////////////////////////////////////////////////////////////////////
-class sp_pcontext : public Sql_alloc
+/// This class represents condition-value term in DECLARE CONDITION or
+/// DECLARE HANDLER statements. sp_condition_value has little to do with
+/// SQL-conditions.
+///
+/// In some sense, this class is a union -- a set of filled attributes
+/// depends on the sp_condition_value::type value.
+
+class sp_condition_value : public Sql_alloc
{
public:
- enum enum_scope
+ enum enum_type
{
- /// REGULAR_SCOPE designates regular BEGIN ... END blocks.
- REGULAR_SCOPE,
-
- /// HANDLER_SCOPE designates SQL-handler blocks.
- HANDLER_SCOPE
+ ERROR_CODE,
+ SQLSTATE,
+ WARNING,
+ NOT_FOUND,
+ EXCEPTION
};
-public:
+ /// Type of the condition value.
+ enum_type type;
- /**
- Constructor.
- Builds a parsing context root node.
- */
- sp_pcontext();
+ /// SQLSTATE of the condition value.
+ char sql_state[SQLSTATE_LENGTH+1];
- // Free memory
- void
- destroy();
-
- /**
- Create and push a new context in the tree.
- @param scope scope of the new parsing context
- @return the node created
- */
- sp_pcontext *
- push_context(enum_scope scope);
-
- /**
- Pop a node from the parsing context tree.
- @return the parent node
- */
- sp_pcontext *
- pop_context();
+ /// MySQL error code of the condition value.
+ uint mysqlerr;
- sp_pcontext *
- parent_context()
- {
- return m_parent;
- }
+public:
+ sp_condition_value(uint _mysqlerr)
+ :Sql_alloc(),
+ type(ERROR_CODE),
+ mysqlerr(_mysqlerr)
+ { }
- /*
- Number of handlers/cursors to pop between this context and 'ctx'.
- If 'exclusive' is true, don't count the last block we are leaving;
- this is used for LEAVE where we will jump to the cpop/hpop instructions.
- */
- uint
- diff_handlers(sp_pcontext *ctx, bool exclusive);
- uint
- diff_cursors(sp_pcontext *ctx, bool exclusive);
-
-
- //
- // Parameters and variables
- //
-
- /*
- The maximum number of variables used in this and all child contexts
- In the root, this gives us the number of slots needed for variables
- during execution.
- */
- inline uint
- max_var_index()
+ sp_condition_value(const char *_sql_state)
+ :Sql_alloc(),
+ type(SQLSTATE)
{
- return m_max_var_index;
+ memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
+ sql_state[SQLSTATE_LENGTH]= 0;
}
- /*
- The current number of variables used in the parents (from the root),
- including this context.
- */
- inline uint
- current_var_count()
+ sp_condition_value(enum_type _type)
+ :Sql_alloc(),
+ type(_type)
{
- return m_var_offset + m_vars.elements;
+ DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
}
- /* The number of variables in this context alone */
- inline uint
- context_var_count()
- {
- return m_vars.elements;
- }
+ /// Check if two instances of sp_condition_value are equal or not.
+ ///
+ /// @param cv another instance of sp_condition_value to check.
+ ///
+ /// @return true if the instances are equal, false otherwise.
+ bool equals(const sp_condition_value *cv) const;
+};
- /* Map index in this pcontext to runtime offset */
- inline uint
- var_context2runtime(uint i)
- {
- return m_var_offset + i;
- }
+///////////////////////////////////////////////////////////////////////////
- /* Set type of variable. 'i' is the offset from the top */
- inline void
- set_type(uint i, enum enum_field_types type)
- {
- sp_variable *p= find_variable(i);
+/// This class represents 'DECLARE CONDITION' statement.
+/// sp_condition has little to do with SQL-conditions.
- if (p)
- p->type= type;
- }
+class sp_condition : public Sql_alloc
+{
+public:
+ /// Name of the condition.
+ LEX_STRING name;
- /* Set default value of variable. 'i' is the offset from the top */
- inline void
- set_default(uint i, Item *it)
- {
- sp_variable *p= find_variable(i);
+ /// Value of the condition.
+ sp_condition_value *value;
- if (p)
- p->dflt= it;
- }
+public:
+ sp_condition(LEX_STRING _name, sp_condition_value *_value)
+ :Sql_alloc(),
+ name(_name),
+ value(_value)
+ { }
+};
- sp_variable *
- push_variable(LEX_STRING *name, enum enum_field_types type,
- sp_variable::enum_mode mode);
-
- /*
- Retrieve definitions of fields from the current context and its
- children.
- */
- void
- retrieve_field_definitions(List<Create_field> *field_def_lst);
-
- // Find by name
- sp_variable *
- find_variable(LEX_STRING *name, my_bool scoped=0);
-
- // Find by offset (from the top)
- sp_variable *
- find_variable(uint offset);
-
- /*
- Set the current scope boundary (for default values).
- The argument is the number of variables to skip.
- */
- inline void
- declare_var_boundary(uint n)
- {
- m_pboundary= n;
- }
+///////////////////////////////////////////////////////////////////////////
- /*
- CASE expressions support.
- */
+/// This class represents 'DECLARE HANDLER' statement.
- inline int
- register_case_expr()
+class sp_handler : public Sql_alloc
+{
+public:
+ /// Enumeration of possible handler types.
+ /// Note: UNDO handlers are not (and have never been) supported.
+ enum enum_type
{
- return m_num_case_exprs++;
- }
+ EXIT,
+ CONTINUE
+ };
- inline int
- get_num_case_exprs() const
- {
- return m_num_case_exprs;
- }
+ /// Handler type.
+ enum_type type;
- inline bool
- push_case_expr_id(int case_expr_id)
- {
- return insert_dynamic(&m_case_expr_id_lst, &case_expr_id);
- }
+ /// Conditions caught by this handler.
+ List<sp_condition_value> condition_values;
- inline void
- pop_case_expr_id()
- {
- pop_dynamic(&m_case_expr_id_lst);
- }
+public:
+ /// The constructor.
+ ///
+ /// @param _type SQL-handler type.
+ sp_handler(enum_type _type)
+ :Sql_alloc(),
+ type(_type)
+ { }
+};
- inline int
- get_current_case_expr_id() const
- {
- int case_expr_id;
+///////////////////////////////////////////////////////////////////////////
- get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (uchar*) &case_expr_id,
- m_case_expr_id_lst.elements - 1);
+/// The class represents parse-time context, which keeps track of declared
+/// variables/parameters, conditions, handlers, cursors and labels.
+///
+/// sp_context objects are organized in a tree according to the following
+/// rules:
+/// - one sp_pcontext object corresponds for for each BEGIN..END block;
+/// - one sp_pcontext object corresponds for each exception handler;
+/// - one additional sp_pcontext object is created to contain
+/// Stored Program parameters.
+///
+/// sp_pcontext objects are used both at parse-time and at runtime.
+///
+/// During the parsing stage sp_pcontext objects are used:
+/// - to look up defined names (e.g. declared variables and visible
+/// labels);
+/// - to check for duplicates;
+/// - for error checking;
+/// - to calculate offsets to be used at runtime.
+///
+/// During the runtime phase, a tree of sp_pcontext objects is used:
+/// - for error checking (e.g. to check correct number of parameters);
+/// - to resolve SQL-handlers.
- return case_expr_id;
- }
+class sp_pcontext : public Sql_alloc
+{
+public:
+ enum enum_scope
+ {
+ /// REGULAR_SCOPE designates regular BEGIN ... END blocks.
+ REGULAR_SCOPE,
- //
- // Labels
- //
+ /// HANDLER_SCOPE designates SQL-handler blocks.
+ HANDLER_SCOPE
+ };
- sp_label *
- push_label(char *name, uint ip);
+public:
+ sp_pcontext();
+ ~sp_pcontext();
- sp_label *
- find_label(char *name);
- inline sp_label *
- last_label()
- {
- sp_label *lab= m_label.head();
+ /// Create and push a new context in the tree.
- if (!lab && m_parent)
- lab= m_parent->last_label();
- return lab;
- }
+ /// @param thd thread context.
+ /// @param scope scope of the new parsing context.
+ /// @return the node created.
+ sp_pcontext *push_context(THD *thd, enum_scope scope);
+
+ /// Pop a node from the parsing context tree.
+ /// @return the parent node.
+ sp_pcontext *pop_context();
+
+ sp_pcontext *parent_context() const
+ { return m_parent; }
+
+ /// Calculate and return the number of handlers to pop between the given
+ /// context and this one.
+ ///
+ /// @param ctx the other parsing context.
+ /// @param exclusive specifies if the last scope should be excluded.
+ ///
+ /// @return the number of handlers to pop between the given context and
+ /// this one. If 'exclusive' is true, don't count the last scope we are
+ /// leaving; this is used for LEAVE where we will jump to the hpop
+ /// instructions.
+ uint diff_handlers(const sp_pcontext *ctx, bool exclusive) const;
+
+ /// Calculate and return the number of cursors to pop between the given
+ /// context and this one.
+ ///
+ /// @param ctx the other parsing context.
+ /// @param exclusive specifies if the last scope should be excluded.
+ ///
+ /// @return the number of cursors to pop between the given context and
+ /// this one. If 'exclusive' is true, don't count the last scope we are
+ /// leaving; this is used for LEAVE where we will jump to the cpop
+ /// instructions.
+ uint diff_cursors(const sp_pcontext *ctx, bool exclusive) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // SP-variables (parameters and variables).
+ /////////////////////////////////////////////////////////////////////////
+
+ /// @return the maximum number of variables used in this and all child
+ /// contexts. For the root parsing context, this gives us the number of
+ /// slots needed for variables during the runtime phase.
+ uint max_var_index() const
+ { return m_max_var_index; }
+
+ /// @return the current number of variables used in the parent contexts
+ /// (from the root), including this context.
+ uint current_var_count() const
+ { return m_var_offset + m_vars.elements(); }
+
+ /// @return the number of variables in this context alone.
+ uint context_var_count() const
+ { return m_vars.elements(); }
+
+ /// @return map index in this parsing context to runtime offset.
+ uint var_context2runtime(uint i) const
+ { return m_var_offset + i; }
+
+ /// Add SP-variable to the parsing context.
+ ///
+ /// @param thd Thread context.
+ /// @param name Name of the SP-variable.
+ /// @param type Type of the SP-variable.
+ /// @param mode Mode of the SP-variable.
+ ///
+ /// @return instance of newly added SP-variable.
+ sp_variable *add_variable(THD *thd,
+ LEX_STRING name,
+ enum enum_field_types type,
+ sp_variable::enum_mode mode);
+
+ /// Retrieve full type information about SP-variables in this parsing
+ /// context and its children.
+ ///
+ /// @param field_def_lst[out] Container to store type information.
+ void retrieve_field_definitions(List<Create_field> *field_def_lst) const;
+
+ /// Find SP-variable by name.
+ ///
+ /// The function does a linear search (from newer to older variables,
+ /// in case we have shadowed names).
+ ///
+ /// The function is called only at parsing time.
+ ///
+ /// @param name Variable name.
+ /// @param current_scope_only A flag if we search only in current scope.
+ ///
+ /// @return instance of found SP-variable, or NULL if not found.
+ sp_variable *find_variable(LEX_STRING name, bool current_scope_only) const;
+
+ /// Find SP-variable by the offset in the root parsing context.
+ ///
+ /// The function is used for two things:
+ /// - When evaluating parameters at the beginning, and setting out parameters
+ /// at the end, of invocation. (Top frame only, so no recursion then.)
+ /// - For printing of sp_instr_set. (Debug mode only.)
+ ///
+ /// @param offset Variable offset in the root parsing context.
+ ///
+ /// @return instance of found SP-variable, or NULL if not found.
+ sp_variable *find_variable(uint offset) const;
+
+ /// Set the current scope boundary (for default values).
+ ///
+ /// @param n The number of variables to skip.
+ void declare_var_boundary(uint n)
+ { m_pboundary= n; }
+
+ /////////////////////////////////////////////////////////////////////////
+ // CASE expressions.
+ /////////////////////////////////////////////////////////////////////////
+
+ int register_case_expr()
+ { return m_num_case_exprs++; }
+
+ int get_num_case_exprs() const
+ { return m_num_case_exprs; }
+
+ bool push_case_expr_id(int case_expr_id)
+ { return m_case_expr_ids.append(case_expr_id); }
+
+ void pop_case_expr_id()
+ { m_case_expr_ids.pop(); }
+
+ int get_current_case_expr_id() const
+ { return *m_case_expr_ids.back(); }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Labels.
+ /////////////////////////////////////////////////////////////////////////
+
+ sp_label *push_label(THD *thd, LEX_STRING name, uint ip);
+
+ sp_label *find_label(LEX_STRING name);
+
+ sp_label *last_label()
+ {
+ sp_label *label= m_labels.head();
+
+ if (!label && m_parent)
+ label= m_parent->last_label();
+
+ return label;
+ }
+
+ sp_label *pop_label()
+ { return m_labels.pop(); }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Conditions.
+ /////////////////////////////////////////////////////////////////////////
+
+ bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value);
+
+ /// See comment for find_variable() above.
+ sp_condition_value *find_condition(LEX_STRING name,
+ bool current_scope_only) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // Handlers.
+ /////////////////////////////////////////////////////////////////////////
+
+ sp_handler *add_handler(THD* thd, sp_handler::enum_type type);
+
+ /// This is an auxilary parsing-time function to check if an SQL-handler
+ /// exists in the current parsing context (current scope) for the given
+ /// SQL-condition. This function is used to check for duplicates during
+ /// the parsing phase.
+ ///
+ /// This function can not be used during the runtime phase to check
+ /// SQL-handler existence because it searches for the SQL-handler in the
+ /// current scope only (during runtime, current and parent scopes
+ /// should be checked according to the SQL-handler resolution rules).
+ ///
+ /// @param condition_value the handler condition value
+ /// (not SQL-condition!).
+ ///
+ /// @retval true if such SQL-handler exists.
+ /// @retval false otherwise.
+ bool check_duplicate_handler(const sp_condition_value *cond_value) const;
+
+ /// Find an SQL handler for the given SQL condition according to the
+ /// SQL-handler resolution rules. This function is used at runtime.
+ ///
+ /// @param sql_state The SQL condition state
+ /// @param sql_errno The error code
+ /// @param level The SQL condition level
+ ///
+ /// @return a pointer to the found SQL-handler or NULL.
+ sp_handler *find_handler(const char *sql_state,
+ uint sql_errno,
+ Sql_condition::enum_warning_level level) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // Cursors.
+ /////////////////////////////////////////////////////////////////////////
+
+ bool add_cursor(LEX_STRING name);
+
+ /// See comment for find_variable() above.
+ bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const;
- inline sp_label *
- pop_label()
- {
- return m_label.pop();
- }
+ /// Find cursor by offset (for debugging only).
+ const LEX_STRING *find_cursor(uint offset) const;
- //
- // Conditions
- //
-
- int
- push_cond(LEX_STRING *name, sp_condition_value *val);
-
- sp_condition_value *
- find_cond(LEX_STRING *name, my_bool scoped=0);
-
- //
- // Handlers
- //
+ uint max_cursor_index() const
+ { return m_max_cursor_index + m_cursors.elements(); }
- inline void
- push_handler(sp_condition_value *cond)
- {
- insert_dynamic(&m_handlers, &cond);
- }
+ uint current_cursor_count() const
+ { return m_cursor_offset + m_cursors.elements(); }
- bool
- find_handler(sp_condition_value *cond);
+private:
+ /// Constructor for a tree node.
+ /// @param prev the parent parsing context
+ /// @param scope scope of this parsing context
+ sp_pcontext(sp_pcontext *prev, enum_scope scope);
- inline uint
- max_handler_index()
- {
- return m_max_handler_index + m_context_handlers;
- }
+ void init(uint var_offset, uint cursor_offset, int num_case_expressions);
- inline void
- add_handlers(uint n)
- {
- m_context_handlers+= n;
- }
+ /* Prevent use of these */
+ sp_pcontext(const sp_pcontext &);
+ void operator=(sp_pcontext &);
- //
- // Cursors
- //
-
- int
- push_cursor(LEX_STRING *name);
-
- my_bool
- find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0);
-
- /* Find by offset (for debugging only) */
- my_bool
- find_cursor(uint offset, LEX_STRING *n);
+private:
+ /// m_max_var_index -- number of variables (including all types of arguments)
+ /// in this context including all children contexts.
+ ///
+ /// m_max_var_index >= m_vars.elements().
+ ///
+ /// m_max_var_index of the root parsing context contains number of all
+ /// variables (including arguments) in all enclosed contexts.
+ uint m_max_var_index;
- inline uint
- max_cursor_index()
- {
- return m_max_cursor_index + m_cursors.elements;
- }
+ /// The maximum sub context's framesizes.
+ uint m_max_cursor_index;
- inline uint
- current_cursor_count()
- {
- return m_cursor_offset + m_cursors.elements;
- }
+ /// Parent context.
+ sp_pcontext *m_parent;
-protected:
+ /// An index of the first SP-variable in this parsing context. The index
+ /// belongs to a runtime table of SP-variables.
+ ///
+ /// Note:
+ /// - m_var_offset is 0 for root parsing context;
+ /// - m_var_offset is different for all nested parsing contexts.
+ uint m_var_offset;
- /**
- Constructor for a tree node.
- @param prev the parent parsing context
- @param scope scope of this parsing context
- */
- sp_pcontext(sp_pcontext *prev, enum_scope scope);
+ /// Cursor offset for this context.
+ uint m_cursor_offset;
- /*
- m_max_var_index -- number of variables (including all types of arguments)
- in this context including all children contexts.
-
- m_max_var_index >= m_vars.elements.
-
- m_max_var_index of the root parsing context contains number of all
- variables (including arguments) in all enclosed contexts.
- */
- uint m_max_var_index;
+ /// Boundary for finding variables in this context. This is the number of
+ /// variables currently "invisible" to default clauses. This is normally 0,
+ /// but will be larger during parsing of DECLARE ... DEFAULT, to get the
+ /// scope right for DEFAULT values.
+ uint m_pboundary;
- // The maximum sub context's framesizes
- uint m_max_cursor_index;
- uint m_max_handler_index;
- uint m_context_handlers; // No. of handlers in this context
+ int m_num_case_exprs;
-private:
+ /// SP parameters/variables.
+ Dynamic_array<sp_variable *> m_vars;
- sp_pcontext *m_parent; // Parent context
+ /// Stack of CASE expression ids.
+ Dynamic_array<int> m_case_expr_ids;
- /*
- m_var_offset -- this is an index of the first variable in this
- parsing context.
-
- m_var_offset is 0 for root context.
-
- Since now each variable is stored in separate place, no reuse is done,
- so m_var_offset is different for all enclosed contexts.
- */
- uint m_var_offset;
+ /// Stack of SQL-conditions.
+ Dynamic_array<sp_condition *> m_conditions;
- uint m_cursor_offset; // Cursor offset for this context
+ /// Stack of cursors.
+ Dynamic_array<LEX_STRING> m_cursors;
- /*
- Boundary for finding variables in this context. This is the number
- of variables currently "invisible" to default clauses.
- This is normally 0, but will be larger during parsing of
- DECLARE ... DEFAULT, to get the scope right for DEFAULT values.
- */
- uint m_pboundary;
+ /// Stack of SQL-handlers.
+ Dynamic_array<sp_handler *> m_handlers;
- int m_num_case_exprs;
+ /// List of labels.
+ List<sp_label> m_labels;
- DYNAMIC_ARRAY m_vars; // Parameters/variables
- DYNAMIC_ARRAY m_case_expr_id_lst; /* Stack of CASE expression ids. */
- DYNAMIC_ARRAY m_conds; // Conditions
- DYNAMIC_ARRAY m_cursors; // Cursors
- DYNAMIC_ARRAY m_handlers; // Handlers, for checking for duplicates
-
- List<sp_label> m_label; // The label list
-
- List<sp_pcontext> m_children; // Children contexts, used for destruction
-
- /**
- Scope of this parsing context.
- */
- enum_scope m_scope;
+ /// Children contexts, used for destruction.
+ Dynamic_array<sp_pcontext *> m_children;
-private:
- sp_pcontext(const sp_pcontext &); /* Prevent use of these */
- void operator=(sp_pcontext &);
+ /// Scope of this parsing context.
+ enum_scope m_scope;
}; // class sp_pcontext : public Sql_alloc
=== modified file 'sql/sp_rcontext.cc'
--- a/sql/sp_rcontext.cc 2011-08-02 08:34:30 +0000
+++ b/sql/sp_rcontext.cc 2011-10-11 15:01:02 +0000
@@ -22,23 +22,22 @@
#include "sp_pcontext.h"
#include "sql_select.h" // create_virtual_tmp_table
-sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
+
+///////////////////////////////////////////////////////////////////////////
+// sp_rcontext implementation.
+///////////////////////////////////////////////////////////////////////////
+
+
+sp_rcontext::sp_rcontext(const sp_pcontext *root_parsing_ctx,
Field *return_value_fld,
- sp_rcontext *prev_runtime_ctx)
- :end_partial_result_set(FALSE),
+ bool in_sub_stmt)
+ :end_partial_result_set(false),
m_root_parsing_ctx(root_parsing_ctx),
- m_var_table(0),
- m_var_items(0),
+ m_var_table(NULL),
m_return_value_fld(return_value_fld),
- m_return_value_set(FALSE),
- in_sub_stmt(FALSE),
- m_hcount(0),
- m_hsp(0),
- m_ihsp(0),
- m_hfound(-1),
- m_ccount(0),
- m_case_expr_holders(0),
- m_prev_runtime_ctx(prev_runtime_ctx)
+ m_return_value_set(false),
+ m_in_sub_stmt(in_sub_stmt),
+ m_ccount(0)
{
}
@@ -47,427 +46,307 @@ sp_rcontext::~sp_rcontext()
{
if (m_var_table)
free_blobs(m_var_table);
-}
-
-/*
- Initialize sp_rcontext instance.
+ // Leave m_handlers, m_handler_call_stack, m_var_items, m_cstack
+ // and m_case_expr_holders untouched.
+ // They are allocated in mem roots and will be freed accordingly.
+}
- SYNOPSIS
- thd Thread handle
- RETURN
- FALSE on success
- TRUE on error
-*/
-bool sp_rcontext::init(THD *thd)
+sp_rcontext *sp_rcontext::create(THD *thd,
+ const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld)
{
- uint handler_count= m_root_parsing_ctx->max_handler_index();
- uint i;
+ sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
+ return_value_fld,
+ thd->in_sub_stmt);
+
+ if (!ctx)
+ return NULL;
- in_sub_stmt= thd->in_sub_stmt;
+ if (ctx->alloc_arrays(thd) ||
+ ctx->init_var_table(thd) ||
+ ctx->init_var_items(thd))
+ {
+ delete ctx;
+ return NULL;
+ }
- if (init_var_table(thd) || init_var_items())
- return TRUE;
-
- if (!(m_raised_conditions= new (thd->mem_root) Sql_condition[handler_count]))
- return TRUE;
-
- for (i= 0; i<handler_count; i++)
- m_raised_conditions[i].init(thd->mem_root);
-
- return
- !(m_handler=
- (sp_handler*)thd->alloc(handler_count * sizeof(sp_handler))) ||
- !(m_hstack=
- (uint*)thd->alloc(handler_count * sizeof(uint))) ||
- !(m_in_handler=
- (sp_active_handler*)thd->alloc(handler_count *
- sizeof(sp_active_handler))) ||
- !(m_cstack=
- (sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursor_index() *
- sizeof(sp_cursor*))) ||
- !(m_case_expr_holders=
- (Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() *
- sizeof (Item_cache*)));
+ return ctx;
}
-/*
- Create and initialize a table to store SP-vars.
+bool sp_rcontext::alloc_arrays(THD *thd)
+{
+ {
+ size_t n= m_root_parsing_ctx->max_cursor_index();
+ m_cstack.reset(
+ static_cast<sp_cursor **> (
+ thd->alloc(n * sizeof (sp_cursor*))),
+ n);
+ }
- SYNOPSIS
- thd Thread handler.
- RETURN
- FALSE on success
- TRUE on error
-*/
+ {
+ size_t n= m_root_parsing_ctx->get_num_case_exprs();
+ m_case_expr_holders.reset(
+ static_cast<Item_cache **> (
+ thd->calloc(n * sizeof (Item_cache*))),
+ n);
+ }
+
+ return !m_cstack.array() || !m_case_expr_holders.array();
+}
-bool
-sp_rcontext::init_var_table(THD *thd)
+
+bool sp_rcontext::init_var_table(THD *thd)
{
List<Create_field> field_def_lst;
if (!m_root_parsing_ctx->max_var_index())
- return FALSE;
+ return false;
m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
-
+
if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
- return TRUE;
+ return true;
- m_var_table->copy_blobs= TRUE;
+ m_var_table->copy_blobs= true;
m_var_table->alias= "";
- return FALSE;
+ return false;
}
-/*
- Create and initialize an Item-adapter (Item_field) for each SP-var field.
-
- RETURN
- FALSE on success
- TRUE on error
-*/
-
-bool
-sp_rcontext::init_var_items()
+bool sp_rcontext::init_var_items(THD *thd)
{
- uint idx;
uint num_vars= m_root_parsing_ctx->max_var_index();
- if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *))))
- return TRUE;
+ m_var_items.reset(
+ static_cast<Item **> (
+ thd->alloc(num_vars * sizeof (Item *))),
+ num_vars);
+
+ if (!m_var_items.array())
+ return true;
- for (idx = 0; idx < num_vars; ++idx)
+ for (uint idx = 0; idx < num_vars; ++idx)
{
if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
-bool
-sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
+bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
{
DBUG_ASSERT(m_return_value_fld);
- m_return_value_set = TRUE;
+ m_return_value_set = true;
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
}
-#define IS_WARNING_CONDITION(S) ((S)[0] == '0' && (S)[1] == '1')
-#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
-#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')
-
-/**
- Find an SQL handler for the given error.
-
- SQL handlers are pushed on the stack m_handler, with the latest/innermost
- one on the top; we then search for matching handlers from the top and
- down.
-
- We search through all the handlers, looking for the most specific one
- (sql_errno more specific than sqlstate more specific than the rest).
- Note that mysql error code handlers is a MySQL extension, not part of
- the standard.
-
- SQL handlers for warnings are searched in the current scope only.
-
- SQL handlers for errors are searched in the current and in outer scopes.
- That's why finding and activation of handler must be separated: an errror
- handler might be located in the outer scope, which is not active at the
- moment. Before such handler can be activated, execution flow should
- unwind to that scope.
-
- Found SQL handler is remembered in m_hfound for future activation.
- If no handler is found, m_hfound is -1.
-
- @param thd Thread handle
- @param sql_errno The error code
- @param sqlstate The error SQL state
- @param level The error level
- @param msg The error message
-
- @retval TRUE if an SQL handler was found
- @retval FALSE otherwise
-*/
-
-bool
-sp_rcontext::find_handler(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- Sql_condition::enum_warning_level level,
- const char *msg)
+bool sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper,
+ sp_instr_cpush *i)
{
- int i= m_hcount;
-
- /* Reset previously found handler. */
- m_hfound= -1;
-
/*
- If this is a fatal sub-statement error, and this runtime
- context corresponds to a sub-statement, no CONTINUE/EXIT
- handlers from this context are applicable: try to locate one
- in the outer scope.
+ We should create cursors in the callers arena, as
+ it could be (and usually is) used in several instructions.
*/
- if (thd->is_fatal_sub_stmt_error && in_sub_stmt)
- i= 0;
-
- /* Search handlers from the latest (innermost) to the oldest (outermost) */
- while (i--)
- {
- sp_condition_value *cond= m_handler[i].cond;
- int j= m_ihsp;
+ sp_cursor *c= new (callers_arena->mem_root) sp_cursor(lex_keeper, i);
- /* Check active handlers, to avoid invoking one recursively */
- while (j--)
- if (m_in_handler[j].ip == m_handler[i].handler)
- break;
- if (j >= 0)
- continue; // Already executing this handler
+ if (c == NULL)
+ return true;
- switch (cond->type)
- {
- case sp_condition_value::number:
- if (sql_errno == cond->mysqlerr &&
- (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_condition_value::number))
- m_hfound= i; // Always the most specific
- break;
- case sp_condition_value::state:
- if (strcmp(sqlstate, cond->sqlstate) == 0 &&
- (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_condition_value::state))
- m_hfound= i;
- break;
- case sp_condition_value::warning:
- if ((IS_WARNING_CONDITION(sqlstate) ||
- level == Sql_condition::WARN_LEVEL_WARN) &&
- m_hfound < 0)
- m_hfound= i;
- break;
- case sp_condition_value::notfound:
- if (IS_NOT_FOUND_CONDITION(sqlstate) && m_hfound < 0)
- m_hfound= i;
- break;
- case sp_condition_value::exception:
- if (IS_EXCEPTION_CONDITION(sqlstate) &&
- level == Sql_condition::WARN_LEVEL_ERROR &&
- m_hfound < 0)
- m_hfound= i;
- break;
- }
- }
-
- if (m_hfound >= 0)
- {
- DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
-
- m_raised_conditions[m_hfound].clear();
- m_raised_conditions[m_hfound].set(sql_errno, sqlstate, level, msg);
-
- return TRUE;
- }
-
- /*
- Only "exception conditions" are propagated to handlers in calling
- contexts. If no handler is found locally for a "completion condition"
- (warning or "not found") we will simply resume execution.
- */
- if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
- level == Sql_condition::WARN_LEVEL_ERROR)
- {
- return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
- level, msg);
- }
-
- return FALSE;
+ m_cstack[m_ccount++]= c;
+ return false;
}
-void
-sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
-{
- DBUG_ENTER("sp_rcontext::push_cursor");
- DBUG_ASSERT(m_ccount < m_root_parsing_ctx->max_cursor_index());
- m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
- DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
- DBUG_VOID_RETURN;
-}
-void
-sp_rcontext::pop_cursors(uint count)
+void sp_rcontext::pop_cursors(uint count)
{
- DBUG_ENTER("sp_rcontext::pop_cursors");
DBUG_ASSERT(m_ccount >= count);
+
while (count--)
- {
delete m_cstack[--m_ccount];
- }
- DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
- DBUG_VOID_RETURN;
}
-void
-sp_rcontext::push_handler(sp_condition_value *cond, uint h, int type)
-{
- DBUG_ENTER("sp_rcontext::push_handler");
- DBUG_ASSERT(m_hcount < m_root_parsing_ctx->max_handler_index());
-
- m_handler[m_hcount].cond= cond;
- m_handler[m_hcount].handler= h;
- m_handler[m_hcount].type= type;
- m_hcount+= 1;
- DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
- DBUG_VOID_RETURN;
-}
-
-void
-sp_rcontext::pop_handlers(uint count)
+bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip)
{
- DBUG_ENTER("sp_rcontext::pop_handlers");
- DBUG_ASSERT(m_hcount >= count);
+ /*
+ We should create handler entries in the callers arena, as
+ they could be (and usually are) used in several instructions.
+ */
+ sp_handler_entry *he=
+ new (callers_arena->mem_root) sp_handler_entry(handler, first_ip);
- m_hcount-= count;
+ if (he == NULL)
+ return true;
- DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
- DBUG_VOID_RETURN;
+ return m_handlers.append(he);
}
-void
-sp_rcontext::push_hstack(uint h)
-{
- DBUG_ENTER("sp_rcontext::push_hstack");
- DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
- m_hstack[m_hsp++]= h;
+void sp_rcontext::pop_handlers(int count)
+{
+ DBUG_ASSERT(m_handlers.elements() >= count);
- DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
- DBUG_VOID_RETURN;
+ for (int i= 0; i < count; ++i)
+ m_handlers.pop();
}
-uint
-sp_rcontext::pop_hstack()
+
+bool sp_rcontext::handle_sql_condition(THD *thd,
+ uint *ip,
+ const sp_instr *cur_spi)
{
- uint handler;
- DBUG_ENTER("sp_rcontext::pop_hstack");
- DBUG_ASSERT(m_hsp);
+ DBUG_ENTER("sp_rcontext::handle_sql_condition");
- handler= m_hstack[--m_hsp];
+ /*
+ If this is a fatal sub-statement error, and this runtime
+ context corresponds to a sub-statement, no CONTINUE/EXIT
+ handlers from this context are applicable: try to locate one
+ in the outer scope.
+ */
+ if (thd->is_fatal_sub_stmt_error && m_in_sub_stmt)
+ DBUG_RETURN(false);
- DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
- DBUG_RETURN(handler);
-}
+ Diagnostics_area *da= thd->get_stmt_da();
+ const sp_handler *found_handler= NULL;
+ const Sql_condition *found_condition= NULL;
-/**
- Prepare found handler to be executed.
+ if (thd->is_error())
+ {
+ found_handler=
+ cur_spi->m_ctx->find_handler(da->get_sqlstate(),
+ da->sql_errno(),
+ Sql_condition::WARN_LEVEL_ERROR);
- @retval TRUE if an SQL handler is activated (was found) and IP of the
- first handler instruction.
- @retval FALSE if there is no active handler
-*/
+ if (found_handler)
+ found_condition= da->get_error_condition();
+ }
+ else if (da->current_statement_warn_count())
+ {
+ Diagnostics_area::Sql_condition_iterator it= da->sql_conditions();
+ const Sql_condition *c;
-bool
-sp_rcontext::activate_handler(THD *thd,
- uint *ip,
- sp_instr *instr,
- Query_arena *execute_arena,
- Query_arena *backup_arena)
-{
- if (m_hfound < 0)
- return FALSE;
+ // Here we need to find the last warning/note from the stack.
+ // In MySQL most substantial warning is the last one.
+ // (We could have used a reverse iterator here if one existed)
- switch (m_handler[m_hfound].type) {
- case SP_HANDLER_NONE:
- break;
+ while ((c= it++))
+ {
+ if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
+ c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
+ {
+ const sp_handler *handler=
+ cur_spi->m_ctx->find_handler(c->get_sqlstate(),
+ c->get_sql_errno(),
+ c->get_level());
+ if (handler)
+ {
+ found_handler= handler;
+ found_condition= c;
+ }
+ }
+ }
+ }
- case SP_HANDLER_CONTINUE:
- thd->restore_active_arena(execute_arena, backup_arena);
- thd->set_n_backup_active_arena(execute_arena, backup_arena);
- push_hstack(instr->get_cont_dest());
+ if (!found_handler)
+ DBUG_RETURN(false);
- /* Fall through */
+ // At this point, we know that:
+ // - there is a pending SQL-condition (error or warning);
+ // - there is an SQL-handler for it.
- default:
- /* End aborted result set. */
+ DBUG_ASSERT(found_condition);
- if (end_partial_result_set)
- thd->protocol->end_partial_result_set(thd);
+ sp_handler_entry *handler_entry= NULL;
+ for (int i= 0; i < m_handlers.elements(); ++i)
+ {
+ sp_handler_entry *h= m_handlers.at(i);
- /* Enter handler. */
+ if (h->handler == found_handler)
+ {
+ handler_entry= h;
+ break;
+ }
+ }
- DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
- DBUG_ASSERT(m_hfound >= 0);
+ /*
+ handler_entry usually should not be NULL here, as that indicates
+ that the parser context thinks a HANDLER should be activated,
+ but the runtime context cannot find it.
+
+ However, this can happen (and this is in line with the Standard)
+ if SQL-condition has been raised before DECLARE HANDLER instruction
+ is processed.
+
+ For example:
+ CREATE PROCEDURE p()
+ BEGIN
+ DECLARE v INT DEFAULT 'get'; -- raises SQL-warning here
+ DECLARE EXIT HANDLER ... -- this handler does not catch the warning
+ END
+ */
+ if (!handler_entry)
+ DBUG_RETURN(false);
- m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler;
- m_in_handler[m_ihsp].index= m_hfound;
- m_ihsp++;
+ // Mark active conditions so that they can be deleted when the handler exits.
+ da->mark_sql_conditions_for_removal();
- DBUG_PRINT("info", ("Entering handler..."));
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
+ uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ?
+ cur_spi->get_cont_dest() : 0;
- /* Reset error state. */
+ /* End aborted result set. */
+ if (end_partial_result_set)
+ thd->protocol->end_partial_result_set(thd);
- thd->clear_error();
- thd->killed= THD::NOT_KILLED; // Some errors set thd->killed
- // (e.g. "bad data").
+ /* Reset error state. */
+ thd->clear_error();
+ thd->killed= THD::NOT_KILLED; // Some errors set thd->killed
+ // (e.g. "bad data").
- /* Return IP of the activated SQL handler. */
- *ip= m_handler[m_hfound].handler;
+ /* Add a frame to handler-call-stack. */
+ Sql_condition_info *cond_info=
+ new (callers_arena->mem_root) Sql_condition_info(found_condition,
+ callers_arena);
+ Handler_call_frame *frame=
+ new (callers_arena->mem_root) Handler_call_frame(cond_info, continue_ip);
+ m_handler_call_stack.append(frame);
- /* Reset found handler. */
- m_hfound= -1;
- }
+ *ip= handler_entry->first_ip;
- return TRUE;
+ DBUG_RETURN(true);
}
-void
-sp_rcontext::exit_handler()
-{
- DBUG_ENTER("sp_rcontext::exit_handler");
- DBUG_ASSERT(m_ihsp);
-
- uint hindex= m_in_handler[m_ihsp-1].index;
- m_raised_conditions[hindex].clear();
- m_ihsp-= 1;
-
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
- DBUG_VOID_RETURN;
-}
-Sql_condition*
-sp_rcontext::raised_condition() const
+uint sp_rcontext::exit_handler(Diagnostics_area *da)
{
- if (m_ihsp > 0)
- {
- uint hindex= m_in_handler[m_ihsp - 1].index;
- Sql_condition *raised= & m_raised_conditions[hindex];
- return raised;
- }
+ DBUG_ENTER("sp_rcontext::exit_handler");
+ DBUG_ASSERT(m_handler_call_stack.elements() > 0);
- if (m_prev_runtime_ctx)
- return m_prev_runtime_ctx->raised_condition();
+ Handler_call_frame *f= m_handler_call_stack.pop();
- return NULL;
-}
+ /*
+ Remove the SQL conditions that were present in DA when the
+ handler was activated.
+ */
+ da->remove_marked_sql_conditions();
+ uint continue_ip= f->continue_ip;
-int
-sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value)
-{
- return set_variable(thd, m_var_table->field[var_idx], value);
+ DBUG_RETURN(continue_ip);
}
-int
-sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
+int sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
{
if (!value)
{
@@ -479,25 +358,47 @@ sp_rcontext::set_variable(THD *thd, Fiel
}
-Item *
-sp_rcontext::get_item(uint var_idx)
+Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
+ const Item *item) const
{
- return m_var_items[var_idx];
+ Item_cache *holder;
+ Query_arena current_arena;
+
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena);
+
+ holder= Item_cache::get_cache(item);
+
+ thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena);
+
+ return holder;
}
-Item **
-sp_rcontext::get_item_addr(uint var_idx)
+bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
+ Item **case_expr_item_ptr)
{
- return m_var_items + var_idx;
+ Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
+ if (!case_expr_item)
+ return true;
+
+ if (!m_case_expr_holders[case_expr_id] ||
+ m_case_expr_holders[case_expr_id]->result_type() !=
+ case_expr_item->result_type())
+ {
+ m_case_expr_holders[case_expr_id]=
+ create_case_expr_holder(thd, case_expr_item);
+ }
+
+ m_case_expr_holders[case_expr_id]->store(case_expr_item);
+ m_case_expr_holders[case_expr_id]->cache_value();
+ return false;
}
-/*
- *
- * sp_cursor
- *
- */
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor implementation.
+///////////////////////////////////////////////////////////////////////////
+
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
:m_lex_keeper(lex_keeper),
@@ -524,8 +425,7 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_
0 in case of success, -1 otherwise
*/
-int
-sp_cursor::open(THD *thd)
+int sp_cursor::open(THD *thd)
{
if (server_side_cursor)
{
@@ -539,8 +439,7 @@ sp_cursor::open(THD *thd)
}
-int
-sp_cursor::close(THD *thd)
+int sp_cursor::close(THD *thd)
{
if (! server_side_cursor)
{
@@ -552,16 +451,14 @@ sp_cursor::close(THD *thd)
}
-void
-sp_cursor::destroy()
+void sp_cursor::destroy()
{
delete server_side_cursor;
- server_side_cursor= 0;
+ server_side_cursor= NULL;
}
-int
-sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
+int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
{
if (! server_side_cursor)
{
@@ -600,108 +497,13 @@ sp_cursor::fetch(THD *thd, List<sp_varia
}
-/*
- Create an instance of appropriate Item_cache class depending on the
- specified type in the callers arena.
-
- SYNOPSIS
- thd thread handler
- result_type type of the expression
-
- RETURN
- Pointer to valid object on success
- NULL on error
-
- NOTE
- We should create cache items in the callers arena, as they are used
- between in several instructions.
-*/
-
-Item_cache *
-sp_rcontext::create_case_expr_holder(THD *thd, const Item *item)
-{
- Item_cache *holder;
- Query_arena current_arena;
-
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena);
-
- holder= Item_cache::get_cache(item);
-
- thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena);
-
- return holder;
-}
-
-
-/*
- Set CASE expression to the specified value.
-
- SYNOPSIS
- thd thread handler
- case_expr_id identifier of the CASE expression
- case_expr_item a value of the CASE expression
-
- RETURN
- FALSE on success
- TRUE on error
-
- NOTE
- The idea is to reuse Item_cache for the expression of the one CASE
- statement. This optimization takes place when there is CASE statement
- inside of a loop. So, in other words, we will use the same object on each
- iteration instead of creating a new one for each iteration.
-
- TODO
- Hypothetically, a type of CASE expression can be different for each
- iteration. For instance, this can happen if the expression contains a
- session variable (something like @@VAR) and its type is changed from one
- iteration to another.
-
- In order to cope with this problem, we check type each time, when we use
- already created object. If the type does not match, we re-create Item.
- This also can (should?) be optimized.
-*/
-
-int
-sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr)
-{
- Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
- if (!case_expr_item)
- return TRUE;
-
- if (!m_case_expr_holders[case_expr_id] ||
- m_case_expr_holders[case_expr_id]->result_type() !=
- case_expr_item->result_type())
- {
- m_case_expr_holders[case_expr_id]=
- create_case_expr_holder(thd, case_expr_item);
- }
-
- m_case_expr_holders[case_expr_id]->store(case_expr_item);
- m_case_expr_holders[case_expr_id]->cache_value();
- return FALSE;
-}
-
-
-Item *
-sp_rcontext::get_case_expr(int case_expr_id)
-{
- return m_case_expr_holders[case_expr_id];
-}
-
-
-Item **
-sp_rcontext::get_case_expr_addr(int case_expr_id)
-{
- return (Item**) m_case_expr_holders + case_expr_id;
-}
-
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor::Select_fetch_into_spvars implementation.
+///////////////////////////////////////////////////////////////////////////
-/***************************************************************************
- Select_fetch_into_spvars
-****************************************************************************/
-int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
+int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields,
+ SELECT_LEX_UNIT *u)
{
/*
Cache the number of columns in the result set in order to easily
@@ -712,7 +514,7 @@ int Select_fetch_into_spvars::prepare(Li
}
-bool Select_fetch_into_spvars::send_data(List<Item> &items)
+bool sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
{
List_iterator_fast<sp_variable> spvar_iter(*spvar_list);
List_iterator_fast<Item> item_iter(items);
@@ -729,7 +531,7 @@ bool Select_fetch_into_spvars::send_data
for (; spvar= spvar_iter++, item= item_iter++; )
{
if (thd->spcont->set_variable(thd, spvar->offset, &item))
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
=== modified file 'sql/sp_rcontext.h'
--- a/sql/sp_rcontext.h 2011-08-02 08:34:30 +0000
+++ b/sql/sp_rcontext.h 2011-09-20 12:54:31 +0000
@@ -18,41 +18,18 @@
#define _SP_RCONTEXT_H_
#include "sql_class.h" // select_result_interceptor
+#include "sp_pcontext.h" // sp_condition_value
+
+///////////////////////////////////////////////////////////////////////////
+// sp_rcontext declaration.
+///////////////////////////////////////////////////////////////////////////
-struct sp_condition_value;
class sp_cursor;
-struct sp_variable;
class sp_lex_keeper;
class sp_instr_cpush;
class Query_arena;
class sp_head;
-class sp_pcontext;
class Item_cache;
-typedef class st_select_lex_unit SELECT_LEX_UNIT;
-class Server_side_cursor;
-
-#define SP_HANDLER_NONE 0
-#define SP_HANDLER_EXIT 1
-#define SP_HANDLER_CONTINUE 2
-#define SP_HANDLER_UNDO 3
-
-struct sp_handler
-{
- /** Condition caught by this HANDLER. */
- sp_condition_value *cond;
- /** Location (instruction pointer) of the handler code. */
- uint handler;
- /** Handler type (EXIT, CONTINUE). */
- int type;
-};
-
-struct sp_active_handler
-{
- /** Instruction pointer of the active handler. */
- uint ip;
- /** Handler index of the active handler. */
- uint index;
-};
/*
This class is a runtime context of a Stored Routine. It is used in an
@@ -75,252 +52,412 @@ struct sp_active_handler
class sp_rcontext : public Sql_alloc
{
- sp_rcontext(const sp_rcontext &); /* Prevent use of these */
- void operator=(sp_rcontext &);
-
- public:
-
- /*
- Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
- SP parameters when they don't fit into prealloced items. This
- is common situation with String items. It is used mainly in
- sp_eval_func_item().
- */
- Query_arena *callers_arena;
-
- /*
- End a open result set before start executing a continue/exit
- handler if one is found as otherwise the client will hang
- due to a violation of the client/server protocol.
- */
- bool end_partial_result_set;
-
-#ifndef DBUG_OFF
- /*
- The routine for which this runtime context is created. Used for checking
- if correct runtime context is used for variable handling.
- */
- sp_head *sp;
-#endif
-
- sp_rcontext(sp_pcontext *root_parsing_ctx, Field *return_value_fld,
- sp_rcontext *prev_runtime_ctx);
- bool init(THD *thd);
+public:
+ /// Construct and properly initialize a new sp_rcontext instance. The static
+ /// create-function is needed because we need a way to return an error from
+ /// the constructor.
+ ///
+ /// @param thd Thread handle.
+ /// @param root_parsing_ctx Top-level parsing context for this stored program.
+ /// @param return_value_fld Field object to store the return value
+ /// (for stored functions only).
+ ///
+ /// @return valid sp_rcontext object or NULL in case of OOM-error.
+ static sp_rcontext *create(THD *thd,
+ const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld);
~sp_rcontext();
- int
- set_variable(THD *thd, uint var_idx, Item **value);
-
- Item *
- get_item(uint var_idx);
-
- Item **
- get_item_addr(uint var_idx);
+private:
+ sp_rcontext(const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld,
+ bool in_sub_stmt);
- bool
- set_return_value(THD *thd, Item **return_value_item);
+ // Prevent use of copying constructor and operator.
+ sp_rcontext(const sp_rcontext &);
+ void operator=(sp_rcontext &);
- inline bool
- is_return_value_set() const
+private:
+ /// This is an auxillary class to store entering instruction pointer for an
+ /// SQL-handler.
+ class sp_handler_entry : public Sql_alloc
{
- return m_return_value_set;
- }
-
- /*
- SQL handlers support.
- */
-
- void push_handler(sp_condition_value *cond, uint h, int type);
+ public:
+ /// Handler definition (from parsing context).
+ const sp_handler *handler;
+
+ /// Instruction pointer to the first instruction.
+ uint first_ip;
+
+ /// The constructor.
+ ///
+ /// @param _handler sp_handler object.
+ /// @param _first_ip first instruction pointer.
+ sp_handler_entry(const sp_handler *_handler, uint _first_ip)
+ :handler(_handler), first_ip(_first_ip)
+ { }
+ };
- void pop_handlers(uint count);
-
- bool
- find_handler(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- Sql_condition::enum_warning_level level,
- const char *msg);
-
- Sql_condition *
- raised_condition() const;
-
- void
- push_hstack(uint h);
-
- uint
- pop_hstack();
+public:
+ /// This class stores basic information about SQL-condition, such as:
+ /// - SQL error code;
+ /// - error level;
+ /// - SQLSTATE;
+ /// - text message.
+ ///
+ /// It's used to organize runtime SQL-handler call stack.
+ ///
+ /// Standard Sql_condition class can not be used, because we don't always have
+ /// an Sql_condition object for an SQL-condition in Diagnostics_area.
+ ///
+ /// Eventually, this class should be moved to sql_error.h, and be a part of
+ /// standard SQL-condition processing (Diagnostics_area should contain an
+ /// object for active SQL-condition, not just information stored in DA's
+ /// fields).
+ class Sql_condition_info : public Sql_alloc
+ {
+ public:
+ /// SQL error code.
+ uint sql_errno;
+
+ /// Error level.
+ Sql_condition::enum_warning_level level;
+
+ /// SQLSTATE.
+ char sql_state[SQLSTATE_LENGTH + 1];
+
+ /// Text message.
+ char *message;
+
+ /// The constructor.
+ ///
+ /// @param _sql_condition The SQL condition.
+ /// @param arena Query arena for SP
+ Sql_condition_info(const Sql_condition *_sql_condition,
+ Query_arena *arena)
+ :sql_errno(_sql_condition->get_sql_errno()),
+ level(_sql_condition->get_level())
+ {
+ memcpy(sql_state, _sql_condition->get_sqlstate(), SQLSTATE_LENGTH);
+ sql_state[SQLSTATE_LENGTH]= '\0';
+
+ message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
+ }
+ };
- bool
- activate_handler(THD *thd,
- uint *ip,
- sp_instr *instr,
- Query_arena *execute_arena,
- Query_arena *backup_arena);
+private:
+ /// This class represents a call frame of SQL-handler (one invocation of a
+ /// handler). Basically, it's needed to store continue instruction pointer for
+ /// CONTINUE SQL-handlers.
+ class Handler_call_frame : public Sql_alloc
+ {
+ public:
+ /// SQL-condition, triggered handler activation.
+ const Sql_condition_info *sql_condition;
+
+ /// Continue-instruction-pointer for CONTINUE-handlers.
+ /// The attribute contains 0 for EXIT-handlers.
+ uint continue_ip;
+
+ /// The constructor.
+ ///
+ /// @param _sql_condition SQL-condition, triggered handler activation.
+ /// @param _continue_ip Continue instruction pointer.
+ Handler_call_frame(const Sql_condition_info *_sql_condition,
+ uint _continue_ip)
+ :sql_condition(_sql_condition),
+ continue_ip(_continue_ip)
+ { }
+ };
+public:
+ /// Arena used to (re) allocate items on. E.g. reallocate INOUT/OUT
+ /// SP-variables when they don't fit into prealloced items. This is common
+ /// situation with String items. It is used mainly in sp_eval_func_item().
+ Query_arena *callers_arena;
- void
- exit_handler();
+ /// Flag to end an open result set before start executing an SQL-handler
+ /// (if one is found). Otherwise the client will hang due to a violation
+ /// of the client/server protocol.
+ bool end_partial_result_set;
- void
- push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+#ifndef DBUG_OFF
+ /// The stored program for which this runtime context is created. Used for
+ /// checking if correct runtime context is used for variable handling.
+ sp_head *sp;
+#endif
- void
- pop_cursors(uint count);
+ /////////////////////////////////////////////////////////////////////////
+ // SP-variables.
+ /////////////////////////////////////////////////////////////////////////
+
+ int set_variable(THD *thd, uint var_idx, Item **value)
+ { return set_variable(thd, m_var_table->field[var_idx], value); }
+
+ Item *get_item(uint var_idx) const
+ { return m_var_items[var_idx]; }
+
+ Item **get_item_addr(uint var_idx) const
+ { return m_var_items.array() + var_idx; }
+
+ bool set_return_value(THD *thd, Item **return_value_item);
+
+ bool is_return_value_set() const
+ { return m_return_value_set; }
+
+ /////////////////////////////////////////////////////////////////////////
+ // SQL-handlers.
+ /////////////////////////////////////////////////////////////////////////
+
+ /// Create a new sp_handler_entry instance and push it to the handler call
+ /// stack.
+ ///
+ /// @param handler SQL-handler object.
+ /// @param first_ip First instruction pointer of the handler.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool push_handler(sp_handler *handler, uint first_ip);
+
+ /// Pop and delete given number of sp_handler_entry instances from the handler
+ /// call stack.
+ ///
+ /// @param count Number of handler entries to pop & delete.
+ void pop_handlers(int count);
- inline void
- pop_all_cursors()
+ const Sql_condition_info *raised_condition() const
{
- pop_cursors(m_ccount);
+ return m_handler_call_stack.elements() ?
+ (*m_handler_call_stack.back())->sql_condition : NULL;
}
- inline sp_cursor *
- get_cursor(uint i)
- {
- return m_cstack[i];
- }
+ /// Handle current SQL condition (if any).
+ ///
+ /// This is the public-interface function to handle SQL conditions in
+ /// stored routines.
+ ///
+ /// @param thd Thread handle.
+ /// @param ip[out] Instruction pointer to the first handler
+ /// instruction.
+ /// @param cur_spi Current SP instruction.
+ ///
+ /// @retval true if an SQL-handler has been activated. That means, all of
+ /// the following conditions are satisfied:
+ /// - the SP-instruction raised SQL-condition(s),
+ /// - and there is an SQL-handler to process at least one of those
+ /// SQL-conditions,
+ /// - and that SQL-handler has been activated.
+ /// Note, that the return value has nothing to do with "error flag"
+ /// semantics.
+ ///
+ /// @retval false otherwise.
+ bool handle_sql_condition(THD *thd,
+ uint *ip,
+ const sp_instr *cur_spi);
+
+ /// Remove latest call frame from the handler call stack.
+ ///
+ /// @param da Diagnostics area containing handled conditions.
+ ///
+ /// @return continue instruction pointer of the removed handler.
+ uint exit_handler(Diagnostics_area *da);
+
+ /////////////////////////////////////////////////////////////////////////
+ // Cursors.
+ /////////////////////////////////////////////////////////////////////////
+
+ /// Create a new sp_cursor instance and push it to the cursor stack.
+ ///
+ /// @param lex_keeper SP-instruction execution helper.
+ /// @param i Cursor-push instruction.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+
+ /// Pop and delete given number of sp_cursor instance from the cursor stack.
+ ///
+ /// @param count Number of cursors to pop & delete.
+ void pop_cursors(uint count);
+
+ void pop_all_cursors()
+ { pop_cursors(m_ccount); }
+
+ sp_cursor *get_cursor(uint i) const
+ { return m_cstack[i]; }
+
+ /////////////////////////////////////////////////////////////////////////
+ // CASE expressions.
+ /////////////////////////////////////////////////////////////////////////
+
+ /// Set CASE expression to the specified value.
+ ///
+ /// @param thd Thread handler.
+ /// @param case_expr_id The CASE expression identifier.
+ /// @param case_expr_item The CASE expression value
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ ///
+ /// @note The idea is to reuse Item_cache for the expression of the one
+ /// CASE statement. This optimization takes place when there is CASE
+ /// statement inside of a loop. So, in other words, we will use the same
+ /// object on each iteration instead of creating a new one for each
+ /// iteration.
+ ///
+ /// TODO
+ /// Hypothetically, a type of CASE expression can be different for each
+ /// iteration. For instance, this can happen if the expression contains
+ /// a session variable (something like @@VAR) and its type is changed
+ /// from one iteration to another.
+ ///
+ /// In order to cope with this problem, we check type each time, when we
+ /// use already created object. If the type does not match, we re-create
+ /// Item. This also can (should?) be optimized.
+ bool set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
+
+ Item *get_case_expr(int case_expr_id) const
+ { return m_case_expr_holders[case_expr_id]; }
- /*
- CASE expressions support.
- */
+ Item ** get_case_expr_addr(int case_expr_id) const
+ { return (Item**) m_case_expr_holders.array() + case_expr_id; }
- int
- set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
+private:
+ /// Internal function to allocate memory for arrays.
+ ///
+ /// @param thd Thread handle.
+ ///
+ /// @return error flag: false on success, true in case of failure.
+ bool alloc_arrays(THD *thd);
+
+ /// Create and initialize a table to store SP-variables.
+ ///
+ /// param thd Thread handle.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool init_var_table(THD *thd);
- Item *
- get_case_expr(int case_expr_id);
+ /// Create and initialize an Item-adapter (Item_field) for each SP-var field.
+ ///
+ /// param thd Thread handle.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool init_var_items(THD *thd);
+
+ /// Create an instance of appropriate Item_cache class depending on the
+ /// specified type in the callers arena.
+ ///
+ /// @note We should create cache items in the callers arena, as they are
+ /// used between in several instructions.
+ ///
+ /// @param thd Thread handler.
+ /// @param item Item to get the expression type.
+ ///
+ /// @return Pointer to valid object on success, or NULL in case of error.
+ Item_cache *create_case_expr_holder(THD *thd, const Item *item) const;
- Item **
- get_case_expr_addr(int case_expr_id);
+ int set_variable(THD *thd, Field *field, Item **value);
private:
- sp_pcontext *m_root_parsing_ctx;
+ /// Top-level (root) parsing context for this runtime context.
+ const sp_pcontext *m_root_parsing_ctx;
- /* Virtual table for storing variables. */
+ /// Virtual table for storing SP-variables.
TABLE *m_var_table;
- /*
- Collection of Item_field proxies, each of them points to the corresponding
- field in m_var_table.
- */
- Item **m_var_items;
-
- /*
- This is a pointer to a field, which should contain return value for stored
- functions (only). For stored procedures, this pointer is NULL.
- */
+ /// Collection of Item_field proxies, each of them points to the
+ /// corresponding field in m_var_table.
+ Bounds_checked_array<Item *> m_var_items;
+
+ /// This is a pointer to a field, which should contain return value for
+ /// stored functions (only). For stored procedures, this pointer is NULL.
Field *m_return_value_fld;
- /*
- Indicates whether the return value (in m_return_value_fld) has been set
- during execution.
- */
+ /// Indicates whether the return value (in m_return_value_fld) has been
+ /// set during execution.
bool m_return_value_set;
- /**
- TRUE if the context is created for a sub-statement.
- */
- bool in_sub_stmt;
-
- sp_handler *m_handler; // Visible handlers
-
- /**
- SQL conditions caught by each handler.
- This is an array indexed by handler index.
- */
- Sql_condition *m_raised_conditions;
-
- uint m_hcount; // Stack pointer for m_handler
- uint *m_hstack; // Return stack for continue handlers
- uint m_hsp; // Stack pointer for m_hstack
- /** Active handler stack. */
- sp_active_handler *m_in_handler;
- uint m_ihsp; // Stack pointer for m_in_handler
- int m_hfound; // Set by find_handler; -1 if not found
+ /// Flag to tell if the runtime context is created for a sub-statement.
+ bool m_in_sub_stmt;
- sp_cursor **m_cstack;
- uint m_ccount;
+ /// Stack of visible handlers.
+ Dynamic_array<sp_handler_entry *> m_handlers;
- Item_cache **m_case_expr_holders;
+ /// Stack of caught SQL conditions.
+ Dynamic_array<Handler_call_frame *> m_handler_call_stack;
- /* Previous runtime context (NULL if none) */
- sp_rcontext *m_prev_runtime_ctx;
-
-private:
- bool init_var_table(THD *thd);
- bool init_var_items();
+ /// Stack of cursors.
+ Bounds_checked_array<sp_cursor *> m_cstack;
- Item_cache *create_case_expr_holder(THD *thd, const Item *item);
+ /// Current number of cursors in m_cstack.
+ uint m_ccount;
- int set_variable(THD *thd, Field *field, Item **value);
+ /// Array of CASE expression holders.
+ Bounds_checked_array<Item_cache *> m_case_expr_holders;
}; // class sp_rcontext : public Sql_alloc
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor declaration.
+///////////////////////////////////////////////////////////////////////////
-/*
- An interceptor of cursor result set used to implement
- FETCH <cname> INTO <varlist>.
-*/
-
-class Select_fetch_into_spvars: public select_result_interceptor
-{
- List<sp_variable> *spvar_list;
- uint field_count;
-public:
- Select_fetch_into_spvars() {} /* Remove gcc warning */
- uint get_field_count() { return field_count; }
- void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
-
- virtual bool send_eof() { return FALSE; }
- virtual bool send_data(List<Item> &items);
- virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-};
-
+class Server_side_cursor;
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
/* A mediator between stored procedures and server side cursors */
class sp_cursor : public Sql_alloc
{
-public:
+private:
+ /// An interceptor of cursor result set used to implement
+ /// FETCH <cname> INTO <varlist>.
+ class Select_fetch_into_spvars: public select_result_interceptor
+ {
+ List<sp_variable> *spvar_list;
+ uint field_count;
+ public:
+ Select_fetch_into_spvars() {} /* Remove gcc warning */
+ uint get_field_count() { return field_count; }
+ void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
+
+ virtual bool send_eof() { return FALSE; }
+ virtual bool send_data(List<Item> &items);
+ virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+};
+public:
sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
virtual ~sp_cursor()
- {
- destroy();
- }
+ { destroy(); }
- sp_lex_keeper *
- get_lex_keeper() { return m_lex_keeper; }
+ sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; }
- int
- open(THD *thd);
+ int open(THD *thd);
- int
- close(THD *thd);
+ int close(THD *thd);
- inline my_bool
- is_open()
- {
- return test(server_side_cursor);
- }
+ my_bool is_open()
+ { return test(server_side_cursor); }
- int
- fetch(THD *, List<sp_variable> *vars);
+ int fetch(THD *, List<sp_variable> *vars);
- inline sp_instr_cpush *
- get_instr()
- {
- return m_i;
- }
+ sp_instr_cpush *get_instr()
+ { return m_i; }
private:
-
Select_fetch_into_spvars result;
sp_lex_keeper *m_lex_keeper;
Server_side_cursor *server_side_cursor;
sp_instr_cpush *m_i; // My push instruction
- void
- destroy();
+ void destroy();
}; // class sp_cursor : public Sql_alloc
=== modified file 'sql/sql_array.h'
--- a/sql/sql_array.h 2011-07-19 15:11:15 +0000
+++ b/sql/sql_array.h 2011-08-25 13:51:44 +0000
@@ -40,6 +40,12 @@ public:
{}
void reset() { m_array= NULL; m_size= 0; }
+
+ void reset(Element_type *array, size_t size)
+ {
+ m_array= array;
+ m_size= size;
+ }
Element_type &operator[](size_t n)
{
@@ -111,6 +117,13 @@ public:
return (Elem*)array.buffer;
}
+ /// @returns pointer to first element; undefined behaviour if array is empty
+ const Elem *front() const
+ {
+ DBUG_ASSERT(array.elements >= 1);
+ return (const Elem*)array.buffer;
+ }
+
/// @returns pointer to last element; undefined behaviour if array is empty.
Elem *back()
{
@@ -118,6 +131,13 @@ public:
return ((Elem*)array.buffer) + (array.elements - 1);
}
+ /// @returns pointer to last element; undefined behaviour if array is empty.
+ const Elem *back() const
+ {
+ DBUG_ASSERT(array.elements >= 1);
+ return ((const Elem*)array.buffer) + (array.elements - 1);
+ }
+
/**
@retval false ok
@retval true OOM, @c my_error() has been called.
@@ -128,9 +148,9 @@ public:
}
/// Pops the last element. Does nothing if array is empty.
- void pop()
+ Elem& pop()
{
- (void)pop_dynamic(&array);
+ return *((Elem*)pop_dynamic(&array));
}
void del(uint idx)
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2011-10-06 11:06:34 +0000
+++ b/sql/sql_class.cc 2011-10-14 15:04:41 +0000
@@ -67,6 +67,8 @@
char internal_table_name[2]= "*";
char empty_c_string[1]= {0}; /* used for not defined db */
+LEX_STRING EMPTY_STR= { (char *) "", 0 };
+
const char * const THD::DEFAULT_WHERE= "field list";
/****************************************************************************
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2011-10-13 14:01:50 +0000
+++ b/sql/sql_class.h 2011-10-14 15:04:41 +0000
@@ -125,6 +125,7 @@ enum enum_filetype { FILETYPE_CSV, FILET
extern char internal_table_name[2];
extern char empty_c_string[1];
+extern LEX_STRING EMPTY_STR;
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern bool volatile shutdown_in_progress;
@@ -3142,6 +3143,7 @@ public:
*/
void push_internal_handler(Internal_error_handler *handler);
+private:
/**
Handle a sql condition.
@param sql_errno the condition error number
@@ -3151,12 +3153,13 @@ public:
@param[out] cond_hdl the sql condition raised, if any
@return true if the condition is handled
*/
- virtual bool handle_condition(uint sql_errno,
- const char* sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg,
- Sql_condition ** cond_hdl);
+ bool handle_condition(uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg,
+ Sql_condition ** cond_hdl);
+public:
/**
Remove the error handler last pushed.
*/
=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc 2011-09-21 11:01:41 +0000
+++ b/sql/sql_error.cc 2011-10-14 15:04:41 +0000
@@ -548,6 +548,7 @@ void Warning_info::clear(ulonglong new_i
{
id(new_id);
m_warn_list.empty();
+ m_marked_sql_conditions.empty();
free_root(&m_warn_root, MYF(0));
memset(m_warn_count, 0, sizeof(m_warn_count));
m_current_statement_warn_count= 0;
@@ -562,16 +563,16 @@ void Warning_info::append_warning_info(T
Diagnostics_area::Sql_condition_iterator it(source->m_warn_list);
const Sql_condition *src_error_condition = source->get_error_condition();
- /*
- Don't use ::push_warning() to avoid invocation of condition
- handlers or escalation of warnings to errors.
- */
while ((err= it++))
{
+ // Do not use ::push_warning() to avoid invocation of THD-internal-handlers.
Sql_condition *new_error= Warning_info::push_warning(thd, err);
if (src_error_condition && src_error_condition == err)
set_error_condition(new_error);
+
+ if (source->is_marked_for_removal(err))
+ mark_condition_for_removal(new_error);
}
}
@@ -589,47 +590,66 @@ void Diagnostics_area::copy_non_errors_f
const Warning_info *src_wi)
{
Sql_condition_iterator it(src_wi->m_warn_list);
- const Sql_condition *err;
+ const Sql_condition *cond;
+ Warning_info *wi= get_warning_info();
- while ((err= it++))
+ while ((cond= it++))
{
- if (err->get_level() != Sql_condition::WARN_LEVEL_ERROR)
- push_warning(thd, err);
+ if (cond->get_level() == Sql_condition::WARN_LEVEL_ERROR)
+ continue;
+
+ Sql_condition *new_condition= wi->push_warning(thd, cond);
+
+ if (src_wi->is_marked_for_removal(cond))
+ wi->mark_condition_for_removal(new_condition);
}
}
-void Warning_info::remove_sql_condition(const Sql_condition *sql_condition)
+void Warning_info::mark_sql_conditions_for_removal()
{
- if (!sql_condition)
- return;
-
Sql_condition_list::Iterator it(m_warn_list);
- Sql_condition *err;
- bool found = false;
+ Sql_condition *cond;
- while ((err= it++))
+ while ((cond= it++))
+ mark_condition_for_removal(cond);
+}
+
+
+void Warning_info::remove_marked_sql_conditions()
+{
+ List_iterator_fast<Sql_condition> it(m_marked_sql_conditions);
+ Sql_condition *cond;
+
+ while ((cond= it++))
{
- if (err == sql_condition)
- {
- m_warn_list.remove(err);
- found= true;
- break;
- }
+ m_warn_list.remove(cond);
+ m_warn_count[cond->get_level()]--;
+ m_current_statement_warn_count--;
+ if (cond == m_error_condition)
+ m_error_condition= NULL;
}
- if (!found)
- return;
+ m_marked_sql_conditions.empty();
+}
+
- m_warn_count[sql_condition->get_level()]--;
- m_current_statement_warn_count--;
+bool Warning_info::is_marked_for_removal(const Sql_condition *cond) const
+{
+ List_iterator_fast<Sql_condition> it(
+ const_cast<List<Sql_condition>&> (m_marked_sql_conditions));
+ Sql_condition *c;
- if (sql_condition == m_error_condition)
- m_error_condition= NULL;
+ while ((c= it++))
+ {
+ if (c == cond)
+ return true;
+ }
- return;
+ return false;
}
+
void Warning_info::reserve_space(THD *thd, uint count)
{
while ((m_warn_list.elements() + count) > thd->variables.max_error_count)
@@ -962,3 +982,32 @@ uint32 convert_error_message(char *to, u
*errors= error_count;
return (uint32) (to - to_start);
}
+
+
+/**
+ Sanity check for SQLSTATEs. The function does not check if it's really an
+ existing SQL-state (there are just too many), it just checks string length and
+ looks for bad characters.
+
+ @param sqlstate the condition SQLSTATE.
+
+ @retval true if it's ok.
+ @retval false if it's bad.
+*/
+
+bool is_sqlstate_valid(const LEX_STRING *sqlstate)
+{
+ if (sqlstate->length != 5)
+ return false;
+
+ for (int i= 0 ; i < 5 ; ++i)
+ {
+ char c = sqlstate->str[i];
+
+ if ((c < '0' || '9' < c) &&
+ (c < 'A' || 'Z' < c))
+ return false;
+ }
+
+ return true;
+}
=== modified file 'sql/sql_error.h'
--- a/sql/sql_error.h 2011-07-28 10:54:44 +0000
+++ b/sql/sql_error.h 2011-09-20 12:13:07 +0000
@@ -276,6 +276,8 @@ class Warning_info
Warning_info *m_next_in_da;
Warning_info **m_prev_in_da;
+ List<Sql_condition> m_marked_sql_conditions;
+
public:
Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings);
~Warning_info();
@@ -342,11 +344,42 @@ private:
{ m_current_statement_warn_count= 0; }
/**
- Remove given SQL-condition from the list.
+ Mark active SQL-conditions for later removal.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void mark_sql_conditions_for_removal();
+
+ /**
+ Unmark SQL-conditions, which were marked for later removal.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void unmark_sql_conditions_from_removal()
+ { m_marked_sql_conditions.empty(); }
+
+ /**
+ Remove SQL-conditions that are marked for deletion.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void remove_marked_sql_conditions();
+
+ /**
+ Check if the given SQL-condition is marked for removal in this Warning_info
+ instance.
+
+ @param cond the SQL-condition.
- @param sql_condition The SQL-condition to remove (may be NULL).
+ @retval true if the given SQL-condition is marked for removal in this
+ Warning_info instance.
+ @retval false otherwise.
*/
- void remove_sql_condition(const Sql_condition *sql_condition);
+ bool is_marked_for_removal(const Sql_condition *cond) const;
+
+ /**
+ Mark a single SQL-condition for removal (add the given SQL-condition to the
+ removal list of this Warning_info instance).
+ */
+ void mark_condition_for_removal(Sql_condition *cond)
+ { m_marked_sql_conditions.push_back(cond, &m_warn_root); }
/**
Used for @@warning_count system variable, which prints
@@ -473,6 +506,7 @@ private:
// for:
// - m_next_in_da / m_prev_in_da
+ // - is_marked_for_removal()
friend class Diagnostics_area;
};
@@ -691,8 +725,14 @@ public:
sql_errno, sqlstate, level, msg);
}
- void remove_sql_condition(const Sql_condition *sql_condition)
- { get_warning_info()->remove_sql_condition(sql_condition); }
+ void mark_sql_conditions_for_removal()
+ { get_warning_info()->mark_sql_conditions_for_removal(); }
+
+ void unmark_sql_conditions_from_removal()
+ { get_warning_info()->unmark_sql_conditions_from_removal(); }
+
+ void remove_marked_sql_conditions()
+ { get_warning_info()->remove_marked_sql_conditions(); }
const Sql_condition *get_error_condition() const
{ return get_warning_info()->get_error_condition(); }
@@ -781,4 +821,59 @@ uint32 convert_error_message(char *to, u
extern const LEX_STRING warning_level_names[];
+bool is_sqlstate_valid(const LEX_STRING *sqlstate);
+
+
+/**
+ Checks if the specified SQL-state-string defines COMPLETION condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines COMPLETION condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_completion(const char *s)
+{ return s[0] == '0' && s[1] == '0'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines WARNING condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines WARNING condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_warning(const char *s)
+{ return s[0] == '0' && s[1] == '1'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines NOT FOUND condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines NOT FOUND condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_not_found(const char *s)
+{ return s[0] == '0' && s[1] == '2'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines EXCEPTION condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines EXCEPTION condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_exception(const char *s)
+{ return s[0] != '0' || s[1] > '2'; }
+
+
#endif // SQL_ERROR_H
=== modified file 'sql/sql_signal.cc'
--- a/sql/sql_signal.cc 2011-08-02 08:34:30 +0000
+++ b/sql/sql_signal.cc 2011-09-20 12:13:07 +0000
@@ -114,8 +114,8 @@ void Sql_cmd_common_signal::eval_default
/*
SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
*/
- DBUG_ASSERT(m_cond->type == sp_condition_value::state);
- sqlstate= m_cond->sqlstate;
+ DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE);
+ sqlstate= m_cond->sql_state;
cond->set_sqlstate(sqlstate);
}
else
@@ -490,7 +490,7 @@ bool Sql_cmd_signal::execute(THD *thd)
bool Sql_cmd_resignal::execute(THD *thd)
{
Diagnostics_area *da= thd->get_stmt_da();
- Sql_condition *signaled;
+ const sp_rcontext::Sql_condition_info *signaled;
DBUG_ENTER("Sql_cmd_resignal::execute");
@@ -504,18 +504,34 @@ bool Sql_cmd_resignal::execute(THD *thd)
DBUG_RETURN(true);
}
+ Sql_condition signaled_err(thd->mem_root);
+ signaled_err.set(signaled->sql_errno,
+ signaled->sql_state,
+ signaled->level,
+ signaled->message);
+
+
if (m_cond) // RESIGNAL with signal_value.
{
query_cache_abort(&thd->query_cache_tls);
- /* Make room for 2 conditions. */
- da->reserve_space(thd, 2);
+ /* Keep handled conditions. */
+ da->unmark_sql_conditions_from_removal();
- Sql_condition *cond= da->push_warning(thd, signaled);
+ /* Check if the old condition still exists. */
+ if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
+ {
+ /* Make room for the new RESIGNAL condition. */
+ da->reserve_space(thd, 1);
+ }
+ else
+ {
+ /* Make room for old condition + the new RESIGNAL condition. */
+ da->reserve_space(thd, 2);
- if (cond)
- cond->copy_opt_attributes(signaled);
+ da->push_warning(thd, &signaled_err);
+ }
}
- DBUG_RETURN(raise_condition(thd, signaled));
+ DBUG_RETURN(raise_condition(thd, &signaled_err));
}
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2011-09-26 07:19:04 +0000
+++ b/sql/sql_yacc.yy 2011-10-14 15:04:41 +0000
@@ -279,7 +279,7 @@ void case_stmt_action_case(LEX *lex)
(Instruction 12 in the example)
*/
- lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ lex->spcont->push_label(current_thd, EMPTY_STR, lex->sphead->instructions());
}
/**
@@ -348,7 +348,7 @@ int case_stmt_action_when(LEX *lex, Item
*/
return !test(i) ||
- sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
+ sp->push_backpatch(i, ctx->push_label(current_thd, EMPTY_STR, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i);
}
@@ -472,8 +472,8 @@ set_local_variable(THD *thd, sp_variable
if (val)
it= val;
- else if (spv->dflt)
- it= spv->dflt;
+ else if (spv->default_value)
+ it= spv->default_value;
else
{
it= new (thd->mem_root) Item_null();
@@ -764,7 +764,7 @@ static bool add_create_index (LEX *lex,
timestamp_type date_time_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
- struct sp_condition_value *spcondvalue;
+ class sp_condition_value *spcondvalue;
struct { int vars, conds, hndlrs, curs; } spblock;
sp_name *spname;
LEX *lex;
@@ -2586,14 +2586,16 @@ sp_fdparam:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$1, TRUE))
+ if (spc->find_variable($1, TRUE))
{
my_error(ER_SP_DUP_PARAM, MYF(0), $1.str);
MYSQL_YYABORT;
}
- sp_variable *spvar= spc->push_variable(&$1,
- (enum enum_field_types)$3,
- sp_variable::MODE_IN);
+
+ sp_variable *spvar= spc->add_variable(YYTHD,
+ $1,
+ (enum enum_field_types) $3,
+ sp_variable::MODE_IN);
if (lex->sphead->fill_field_definition(YYTHD, lex,
(enum enum_field_types) $3,
@@ -2623,14 +2625,15 @@ sp_pdparam:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$3, TRUE))
+ if (spc->find_variable($3, TRUE))
{
my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
MYSQL_YYABORT;
}
- sp_variable *spvar= spc->push_variable(&$3,
- (enum enum_field_types)$4,
- (sp_variable::enum_mode)$1);
+ sp_variable *spvar= spc->add_variable(YYTHD,
+ $3,
+ (enum enum_field_types) $4,
+ (sp_variable::enum_mode) $1);
if (lex->sphead->fill_field_definition(YYTHD, lex,
(enum enum_field_types) $4,
@@ -2725,7 +2728,7 @@ sp_decl:
MYSQL_YYABORT;
spvar->type= var_type;
- spvar->dflt= dflt_value_item;
+ spvar->default_value= dflt_value_item;
if (lex->sphead->fill_field_definition(YYTHD, lex, var_type,
&spvar->field_def))
@@ -2761,36 +2764,41 @@ sp_decl:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_cond(&$2, TRUE))
+ if (spc->find_condition($2, TRUE))
{
my_error(ER_SP_DUP_COND, MYF(0), $2.str);
MYSQL_YYABORT;
}
- if(YYTHD->lex->spcont->push_cond(&$2, $5))
+ if(spc->add_condition(YYTHD, $2, $5))
MYSQL_YYABORT;
$$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1;
}
| DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
{
+ THD *thd= YYTHD;
LEX *lex= Lex;
sp_head *sp= lex->sphead;
- lex->spcont= lex->spcont->push_context(sp_pcontext::HANDLER_SCOPE);
+ sp_handler *h= lex->spcont->add_handler(thd,
+ (sp_handler::enum_type) $2);
+
+ lex->spcont= lex->spcont->push_context(thd,
+ sp_pcontext::HANDLER_SCOPE);
sp_pcontext *ctx= lex->spcont;
sp_instr_hpush_jump *i=
- new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
- ctx->current_var_count());
+ new sp_instr_hpush_jump(sp->instructions(), ctx, h);
+
if (i == NULL || sp->add_instr(i))
MYSQL_YYABORT;
/* For continue handlers, mark end of handler scope. */
- if ($2 == SP_HANDLER_CONTINUE &&
+ if ($2 == sp_handler::CONTINUE &&
sp->push_backpatch(i, ctx->last_label()))
MYSQL_YYABORT;
- if (sp->push_backpatch(i, ctx->push_label(empty_c_string, 0)))
+ if (sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0)))
MYSQL_YYABORT;
}
sp_hcond_list sp_proc_stmt
@@ -2801,17 +2809,16 @@ sp_decl:
sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */
sp_instr_hreturn *i;
- if ($2 == SP_HANDLER_CONTINUE)
+ if ($2 == sp_handler::CONTINUE)
{
- i= new sp_instr_hreturn(sp->instructions(), ctx,
- ctx->current_var_count());
+ i= new sp_instr_hreturn(sp->instructions(), ctx);
if (i == NULL ||
sp->add_instr(i))
MYSQL_YYABORT;
}
else
{ /* EXIT or UNDO handler, just jump to the end of the block */
- i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
+ i= new sp_instr_hreturn(sp->instructions(), ctx);
if (i == NULL ||
sp->add_instr(i) ||
sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */
@@ -2822,8 +2829,7 @@ sp_decl:
lex->spcont= ctx->pop_context();
$$.vars= $$.conds= $$.curs= 0;
- $$.hndlrs= $6;
- lex->spcont->add_handlers($6);
+ $$.hndlrs= 1;
}
| DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
{
@@ -2833,7 +2839,7 @@ sp_decl:
uint offp;
sp_instr_cpush *i;
- if (ctx->find_cursor(&$2, &offp, TRUE))
+ if (ctx->find_cursor($2, &offp, TRUE))
{
my_error(ER_SP_DUP_CURS, MYF(0), $2.str);
delete $5;
@@ -2843,7 +2849,7 @@ sp_decl:
ctx->current_cursor_count());
if (i == NULL ||
sp->add_instr(i) ||
- ctx->push_cursor(&$2))
+ ctx->add_cursor($2))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= 0;
$$.curs= 1;
@@ -2874,8 +2880,8 @@ sp_cursor_stmt:
;
sp_handler_type:
- EXIT_SYM { $$= SP_HANDLER_EXIT; }
- | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
+ EXIT_SYM { $$= sp_handler::EXIT; }
+ | CONTINUE_SYM { $$= sp_handler::CONTINUE; }
/*| UNDO_SYM { QQ No yet } */
;
@@ -2893,7 +2899,7 @@ sp_hcond_element:
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont->parent_context();
- if (ctx->find_handler($1))
+ if (ctx->check_duplicate_handler($1))
{
my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0));
MYSQL_YYABORT;
@@ -2904,7 +2910,6 @@ sp_hcond_element:
(sp_instr_hpush_jump *)sp->last_instruction();
i->add_condition($1);
- ctx->push_handler($1);
}
}
;
@@ -2917,11 +2922,9 @@ sp_cond:
my_error(ER_WRONG_VALUE, MYF(0), "CONDITION", "0");
MYSQL_YYABORT;
}
- $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
+ $$= new (YYTHD->mem_root) sp_condition_value($1);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_condition_value::number;
- $$->mysqlerr= $1;
}
| sqlstate
;
@@ -2929,17 +2932,22 @@ sp_cond:
sqlstate:
SQLSTATE_SYM opt_value TEXT_STRING_literal
{ /* SQLSTATE */
- if (!sp_cond_check(&$3))
+
+ /*
+ An error is triggered:
+ - if the specified string is not a valid SQLSTATE,
+ - or if it represents the completion condition -- it is not
+ allowed to SIGNAL, or declare a handler for the completion
+ condition.
+ */
+ if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str))
{
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
MYSQL_YYABORT;
}
- $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
+ $$= new (YYTHD->mem_root) sp_condition_value($3.str);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_condition_value::state;
- memcpy($$->sqlstate, $3.str, SQLSTATE_LENGTH);
- $$->sqlstate[SQLSTATE_LENGTH]= '\0';
}
;
@@ -2955,7 +2963,7 @@ sp_hcond:
}
| ident /* CONDITION name */
{
- $$= Lex->spcont->find_cond(&$1);
+ $$= Lex->spcont->find_condition($1, false);
if ($$ == NULL)
{
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
@@ -2964,24 +2972,21 @@ sp_hcond:
}
| SQLWARNING_SYM /* SQLSTATEs 01??? */
{
- $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
+ $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::WARNING);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_condition_value::warning;
}
| not FOUND_SYM /* SQLSTATEs 02??? */
{
- $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
+ $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_condition_value::notfound;
}
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
{
- $$= (sp_condition_value *)YYTHD->alloc(sizeof(sp_condition_value));
+ $$= new (YYTHD->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_condition_value::exception;
}
;
@@ -3011,13 +3016,13 @@ signal_value:
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
MYSQL_YYABORT;
}
- cond= lex->spcont->find_cond(&$1);
+ cond= lex->spcont->find_condition($1, false);
if (cond == NULL)
{
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
MYSQL_YYABORT;
}
- if (cond->type != sp_condition_value::state)
+ if (cond->type != sp_condition_value::SQLSTATE)
{
my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0));
MYSQL_YYABORT;
@@ -3148,12 +3153,15 @@ sp_decl_idents:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$1, TRUE))
+ if (spc->find_variable($1, TRUE))
{
my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
}
- spc->push_variable(&$1, (enum_field_types)0, sp_variable::MODE_IN);
+ spc->add_variable(YYTHD,
+ $1,
+ MYSQL_TYPE_DECIMAL,
+ sp_variable::MODE_IN);
$$= 1;
}
| sp_decl_idents ',' ident
@@ -3163,12 +3171,15 @@ sp_decl_idents:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$3, TRUE))
+ if (spc->find_variable($3, TRUE))
{
my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
MYSQL_YYABORT;
}
- spc->push_variable(&$3, (enum_field_types)0, sp_variable::MODE_IN);
+ spc->add_variable(YYTHD,
+ $3,
+ MYSQL_TYPE_DECIMAL,
+ sp_variable::MODE_IN);
$$= $1 + 1;
}
;
@@ -3290,7 +3301,9 @@ sp_proc_stmt_unlabeled:
{ /* Unlabeled controls get a secret label. */
LEX *lex= Lex;
- lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ lex->spcont->push_label(YYTHD,
+ EMPTY_STR,
+ lex->sphead->instructions());
}
sp_unlabeled_control
{
@@ -3306,7 +3319,7 @@ sp_proc_stmt_leave:
LEX *lex= Lex;
sp_head *sp = lex->sphead;
sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($2.str);
+ sp_label *lab= ctx->find_label($2);
if (! lab)
{
@@ -3359,7 +3372,7 @@ sp_proc_stmt_iterate:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($2.str);
+ sp_label *lab= ctx->find_label($2);
if (! lab || lab->type != sp_label::ITERATION)
{
@@ -3404,7 +3417,7 @@ sp_proc_stmt_open:
uint offset;
sp_instr_copen *i;
- if (! lex->spcont->find_cursor(&$2, &offset))
+ if (! lex->spcont->find_cursor($2, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
MYSQL_YYABORT;
@@ -3424,7 +3437,7 @@ sp_proc_stmt_fetch:
uint offset;
sp_instr_cfetch *i;
- if (! lex->spcont->find_cursor(&$3, &offset))
+ if (! lex->spcont->find_cursor($3, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str);
MYSQL_YYABORT;
@@ -3446,7 +3459,7 @@ sp_proc_stmt_close:
uint offset;
sp_instr_cclose *i;
- if (! lex->spcont->find_cursor(&$2, &offset))
+ if (! lex->spcont->find_cursor($2, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
MYSQL_YYABORT;
@@ -3472,7 +3485,7 @@ sp_fetch_list:
sp_pcontext *spc= lex->spcont;
sp_variable *spv;
- if (!spc || !(spv = spc->find_variable(&$1)))
+ if (!spc || !(spv = spc->find_variable($1, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
@@ -3492,7 +3505,7 @@ sp_fetch_list:
sp_pcontext *spc= lex->spcont;
sp_variable *spv;
- if (!spc || !(spv = spc->find_variable(&$3)))
+ if (!spc || !(spv = spc->find_variable($3, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str);
MYSQL_YYABORT;
@@ -3518,7 +3531,7 @@ sp_if:
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
$2, lex);
if (i == NULL ||
- sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
+ sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i))
MYSQL_YYABORT;
@@ -3535,7 +3548,7 @@ sp_if:
sp->add_instr(i))
MYSQL_YYABORT;
sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->push_backpatch(i, ctx->push_label(YYTHD, EMPTY_STR, 0));
}
sp_elseifs
{
@@ -3677,7 +3690,7 @@ sp_labeled_control:
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($1.str);
+ sp_label *lab= ctx->find_label($1);
if (lab)
{
@@ -3686,8 +3699,7 @@ sp_labeled_control:
}
else
{
- lab= lex->spcont->push_label($1.str,
- lex->sphead->instructions());
+ lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions());
lab->type= sp_label::ITERATION;
}
}
@@ -3698,7 +3710,7 @@ sp_labeled_control:
if ($5.str)
{
- if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
{
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
MYSQL_YYABORT;
@@ -3718,7 +3730,7 @@ sp_labeled_block:
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
- sp_label *lab= ctx->find_label($1.str);
+ sp_label *lab= ctx->find_label($1);
if (lab)
{
@@ -3726,8 +3738,7 @@ sp_labeled_block:
MYSQL_YYABORT;
}
- lab= lex->spcont->push_label($1.str,
- lex->sphead->instructions());
+ lab= lex->spcont->push_label(YYTHD, $1, lex->sphead->instructions());
lab->type= sp_label::BEGIN;
}
sp_block_content sp_opt_label
@@ -3737,7 +3748,7 @@ sp_labeled_block:
if ($5.str)
{
- if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
{
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
MYSQL_YYABORT;
@@ -3750,7 +3761,7 @@ sp_unlabeled_block:
{ /* Unlabeled blocks get a secret label. */
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label *lab= lex->spcont->push_label((char *)"", ip);
+ sp_label *lab= lex->spcont->push_label(YYTHD, EMPTY_STR, ip);
lab->type= sp_label::BEGIN;
}
sp_block_content
@@ -3766,7 +3777,8 @@ sp_block_content:
together. No [[NOT] ATOMIC] yet, and we need to figure out how
make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
LEX *lex= Lex;
- lex->spcont= lex->spcont->push_context(sp_pcontext::REGULAR_SCOPE);
+ lex->spcont= lex->spcont->push_context(YYTHD,
+ sp_pcontext::REGULAR_SCOPE);
}
sp_decls
sp_proc_stmts
@@ -10116,7 +10128,7 @@ limit_option:
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
sp_variable *spv;
sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable(&$1)))
+ if (spc && (spv = spc->find_variable($1, false)))
{
splocal= new (thd->mem_root)
Item_splocal($1, spv->offset, spv->type,
@@ -10316,7 +10328,7 @@ select_var_ident:
LEX *lex=Lex;
sp_variable *t;
- if (!lex->spcont || !(t=lex->spcont->find_variable(&$1)))
+ if (!lex->spcont || !(t=lex->spcont->find_variable($1, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
@@ -12156,7 +12168,7 @@ simple_ident:
Lex_input_stream *lip= YYLIP;
sp_variable *spv;
sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable(&$1)))
+ if (spc && (spv = spc->find_variable($1, false)))
{
/* We're compiling a stored procedure and found a variable */
if (! lex->parsing_options.allows_variable)
@@ -13112,7 +13124,7 @@ sys_option_value:
else
{
sp_pcontext *spc= lex->spcont;
- sp_variable *spv= spc->find_variable(name);
+ sp_variable *spv= spc->find_variable(*name, false);
if ($1)
{
@@ -13192,7 +13204,7 @@ option_value:
names.str= (char *)"names";
names.length= 5;
- if (spc && spc->find_variable(&names))
+ if (spc && spc->find_variable(names, false))
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
else
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -13231,7 +13243,7 @@ option_value:
pw.str= (char *)"password";
pw.length= 8;
- if (spc && spc->find_variable(&pw))
+ if (spc && spc->find_variable(pw, false))
{
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
MYSQL_YYABORT;
@@ -13269,7 +13281,7 @@ internal_variable_name:
sp_variable *spv;
/* Best effort lookup for system variable. */
- if (!spc || !(spv = spc->find_variable(&$1)))
+ if (!spc || !(spv = spc->find_variable($1, false)))
{
struct sys_var_with_base tmp= {NULL, $1};
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (jon.hauglid:3496 to 3497) WL#5986 | Jon Olav Hauglid | 17 Oct |