summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <joerg@trift2.>2007-01-15 12:13:11 +0100
committerunknown <joerg@trift2.>2007-01-15 12:13:11 +0100
commit932a29e7e8775c2c5659dfb99495593e5ef4404e (patch)
tree43e016fa782a4e8f0709bf36e23c9fc7d32952d4
parent4b21e1404af8390eaa8429ebce60bd6828579a94 (diff)
parentbe672162c534803f697d8b9e31ad220ff552d67d (diff)
downloadmariadb-git-clone-5.0.34-build.tar.gz
Merge trift2.:/MySQL/M50/mysql-5.0clone-5.0.34-build
into trift2.:/MySQL/M50/push-5.0 VC++Files/libmysqld/libmysqld.dsp: Auto merged VC++Files/mysql.dsw: Auto merged VC++Files/mysqldemb/mysqldemb.dsp: Auto merged VC++Files/sql/mysqld.dsp: Auto merged VC++Files/sql/mysqldmax.dsp: Auto merged innobase/buf/buf0buf.c: Auto merged innobase/dict/dict0dict.c: Auto merged innobase/fil/fil0fil.c: Auto merged innobase/ha/ha0ha.c: Auto merged innobase/include/hash0hash.h: Auto merged innobase/lock/lock0lock.c: Auto merged innobase/log/log0recv.c: Auto merged mysys/my_read.c: Auto merged scripts/make_binary_distribution.sh: Auto merged support-files/MySQL-shared-compat.spec.sh: Auto merged
-rw-r--r--heap/hp_block.c2
-rw-r--r--heap/hp_write.c12
-rw-r--r--mysql-test/r/sp-code.result415
-rw-r--r--mysql-test/r/sp_stress_case.result120
-rw-r--r--mysql-test/r/view.result9
-rwxr-xr-xmysql-test/t/log.sh2
-rw-r--r--mysql-test/t/sp-code.test235
-rw-r--r--mysql-test/t/sp_stress_case.test89
-rw-r--r--mysql-test/t/view.test15
-rw-r--r--server-tools/instance-manager/listener.cc10
-rw-r--r--sql/item_func.cc5
-rw-r--r--sql/sp_head.cc96
-rw-r--r--sql/sp_head.h39
-rw-r--r--sql/sql_class.cc14
-rw-r--r--sql/sql_class.h12
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_prepare.cc3
-rw-r--r--sql/sql_string.cc4
-rw-r--r--sql/sql_yacc.yy412
-rw-r--r--tests/mysql_client_test.c28
21 files changed, 1338 insertions, 186 deletions
diff --git a/heap/hp_block.c b/heap/hp_block.c
index 35e65a94603..85219380287 100644
--- a/heap/hp_block.c
+++ b/heap/hp_block.c
@@ -75,7 +75,7 @@ int hp_get_new_block(HP_BLOCK *block, ulong *alloc_length)
and my_default_record_cache_size we get about 1/128 unused memory.
*/
*alloc_length=sizeof(HP_PTRS)*i+block->records_in_block* block->recbuffer;
- if (!(root=(HP_PTRS*) my_malloc(*alloc_length,MYF(0))))
+ if (!(root=(HP_PTRS*) my_malloc(*alloc_length,MYF(MY_WME))))
return 1;
if (i == 0)
diff --git a/heap/hp_write.c b/heap/hp_write.c
index f8b268ee06a..8a392d54c51 100644
--- a/heap/hp_write.c
+++ b/heap/hp_write.c
@@ -67,11 +67,17 @@ int heap_write(HP_INFO *info, const byte *record)
DBUG_RETURN(0);
err:
- DBUG_PRINT("info",("Duplicate key: %d", (int) (keydef - share->keydef)));
+ if (my_errno == HA_ERR_FOUND_DUPP_KEY)
+ DBUG_PRINT("info",("Duplicate key: %d", keydef - share->keydef));
info->errkey= keydef - share->keydef;
- if (keydef->algorithm == HA_KEY_ALG_BTREE)
+ /*
+ We don't need to delete non-inserted key from rb-tree. Also, if
+ we got ENOMEM, the key wasn't inserted, so don't try to delete it
+ either. Otherwise for HASH index on HA_ERR_FOUND_DUPP_KEY the key
+ was inserted and we have to delete it.
+ */
+ if (keydef->algorithm == HA_KEY_ALG_BTREE || my_errno == ENOMEM)
{
- /* we don't need to delete non-inserted key from rb-tree */
keydef--;
}
while (keydef >= share->keydef)
diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result
index 4ae38861d29..0b0ad802b54 100644
--- a/mysql-test/r/sp-code.result
+++ b/mysql-test/r/sp-code.result
@@ -199,6 +199,421 @@ Pos Instruction
44 jump 14
45 stmt 9 "drop temporary table sudoku_work, sud..."
drop procedure sudoku_solve;
+DROP PROCEDURE IF EXISTS proc_19194_simple;
+DROP PROCEDURE IF EXISTS proc_19194_searched;
+DROP PROCEDURE IF EXISTS proc_19194_nested_1;
+DROP PROCEDURE IF EXISTS proc_19194_nested_2;
+DROP PROCEDURE IF EXISTS proc_19194_nested_3;
+DROP PROCEDURE IF EXISTS proc_19194_nested_4;
+CREATE PROCEDURE proc_19194_simple(i int)
+BEGIN
+DECLARE str CHAR(10);
+CASE i
+WHEN 1 THEN SET str="1";
+WHEN 2 THEN SET str="2";
+WHEN 3 THEN SET str="3";
+ELSE SET str="unknown";
+END CASE;
+SELECT str;
+END|
+CREATE PROCEDURE proc_19194_searched(i int)
+BEGIN
+DECLARE str CHAR(10);
+CASE
+WHEN i=1 THEN SET str="1";
+WHEN i=2 THEN SET str="2";
+WHEN i=3 THEN SET str="3";
+ELSE SET str="unknown";
+END CASE;
+SELECT str;
+END|
+CREATE PROCEDURE proc_19194_nested_1(i int, j int)
+BEGIN
+DECLARE str_i CHAR(10);
+DECLARE str_j CHAR(10);
+CASE i
+WHEN 10 THEN SET str_i="10";
+WHEN 20 THEN
+BEGIN
+set str_i="20";
+CASE
+WHEN j=1 THEN SET str_j="1";
+WHEN j=2 THEN SET str_j="2";
+WHEN j=3 THEN SET str_j="3";
+ELSE SET str_j="unknown";
+END CASE;
+select "i was 20";
+END;
+WHEN 30 THEN SET str_i="30";
+WHEN 40 THEN SET str_i="40";
+ELSE SET str_i="unknown";
+END CASE;
+SELECT str_i, str_j;
+END|
+CREATE PROCEDURE proc_19194_nested_2(i int, j int)
+BEGIN
+DECLARE str_i CHAR(10);
+DECLARE str_j CHAR(10);
+CASE
+WHEN i=10 THEN SET str_i="10";
+WHEN i=20 THEN
+BEGIN
+set str_i="20";
+CASE j
+WHEN 1 THEN SET str_j="1";
+WHEN 2 THEN SET str_j="2";
+WHEN 3 THEN SET str_j="3";
+ELSE SET str_j="unknown";
+END CASE;
+select "i was 20";
+END;
+WHEN i=30 THEN SET str_i="30";
+WHEN i=40 THEN SET str_i="40";
+ELSE SET str_i="unknown";
+END CASE;
+SELECT str_i, str_j;
+END|
+CREATE PROCEDURE proc_19194_nested_3(i int, j int)
+BEGIN
+DECLARE str_i CHAR(10);
+DECLARE str_j CHAR(10);
+CASE i
+WHEN 10 THEN SET str_i="10";
+WHEN 20 THEN
+BEGIN
+set str_i="20";
+CASE j
+WHEN 1 THEN SET str_j="1";
+WHEN 2 THEN SET str_j="2";
+WHEN 3 THEN SET str_j="3";
+ELSE SET str_j="unknown";
+END CASE;
+select "i was 20";
+END;
+WHEN 30 THEN SET str_i="30";
+WHEN 40 THEN SET str_i="40";
+ELSE SET str_i="unknown";
+END CASE;
+SELECT str_i, str_j;
+END|
+CREATE PROCEDURE proc_19194_nested_4(i int, j int)
+BEGIN
+DECLARE str_i CHAR(10);
+DECLARE str_j CHAR(10);
+CASE
+WHEN i=10 THEN SET str_i="10";
+WHEN i=20 THEN
+BEGIN
+set str_i="20";
+CASE
+WHEN j=1 THEN SET str_j="1";
+WHEN j=2 THEN SET str_j="2";
+WHEN j=3 THEN SET str_j="3";
+ELSE SET str_j="unknown";
+END CASE;
+select "i was 20";
+END;
+WHEN i=30 THEN SET str_i="30";
+WHEN i=40 THEN SET str_i="40";
+ELSE SET str_i="unknown";
+END CASE;
+SELECT str_i, str_j;
+END|
+SHOW PROCEDURE CODE proc_19194_simple;
+Pos Instruction
+0 set str@1 NULL
+1 set_case_expr (12) 0 i@0
+2 jump_if_not 5(12) (case_expr@0 = 1)
+3 set str@1 _latin1'1'
+4 jump 12
+5 jump_if_not 8(12) (case_expr@0 = 2)
+6 set str@1 _latin1'2'
+7 jump 12
+8 jump_if_not 11(12) (case_expr@0 = 3)
+9 set str@1 _latin1'3'
+10 jump 12
+11 set str@1 _latin1'unknown'
+12 stmt 0 "SELECT str"
+SHOW PROCEDURE CODE proc_19194_searched;
+Pos Instruction
+0 set str@1 NULL
+1 jump_if_not 4(11) (i@0 = 1)
+2 set str@1 _latin1'1'
+3 jump 11
+4 jump_if_not 7(11) (i@0 = 2)
+5 set str@1 _latin1'2'
+6 jump 11
+7 jump_if_not 10(11) (i@0 = 3)
+8 set str@1 _latin1'3'
+9 jump 11
+10 set str@1 _latin1'unknown'
+11 stmt 0 "SELECT str"
+SHOW PROCEDURE CODE proc_19194_nested_1;
+Pos Instruction
+0 set str_i@2 NULL
+1 set str_j@3 NULL
+2 set_case_expr (27) 0 i@0
+3 jump_if_not 6(27) (case_expr@0 = 10)
+4 set str_i@2 _latin1'10'
+5 jump 27
+6 jump_if_not 20(27) (case_expr@0 = 20)
+7 set str_i@2 _latin1'20'
+8 jump_if_not 11(18) (j@1 = 1)
+9 set str_j@3 _latin1'1'
+10 jump 18
+11 jump_if_not 14(18) (j@1 = 2)
+12 set str_j@3 _latin1'2'
+13 jump 18
+14 jump_if_not 17(18) (j@1 = 3)
+15 set str_j@3 _latin1'3'
+16 jump 18
+17 set str_j@3 _latin1'unknown'
+18 stmt 0 "select "i was 20""
+19 jump 27
+20 jump_if_not 23(27) (case_expr@0 = 30)
+21 set str_i@2 _latin1'30'
+22 jump 27
+23 jump_if_not 26(27) (case_expr@0 = 40)
+24 set str_i@2 _latin1'40'
+25 jump 27
+26 set str_i@2 _latin1'unknown'
+27 stmt 0 "SELECT str_i, str_j"
+SHOW PROCEDURE CODE proc_19194_nested_2;
+Pos Instruction
+0 set str_i@2 NULL
+1 set str_j@3 NULL
+2 jump_if_not 5(27) (i@0 = 10)
+3 set str_i@2 _latin1'10'
+4 jump 27
+5 jump_if_not 20(27) (i@0 = 20)
+6 set str_i@2 _latin1'20'
+7 set_case_expr (18) 0 j@1
+8 jump_if_not 11(18) (case_expr@0 = 1)
+9 set str_j@3 _latin1'1'
+10 jump 18
+11 jump_if_not 14(18) (case_expr@0 = 2)
+12 set str_j@3 _latin1'2'
+13 jump 18
+14 jump_if_not 17(18) (case_expr@0 = 3)
+15 set str_j@3 _latin1'3'
+16 jump 18
+17 set str_j@3 _latin1'unknown'
+18 stmt 0 "select "i was 20""
+19 jump 27
+20 jump_if_not 23(27) (i@0 = 30)
+21 set str_i@2 _latin1'30'
+22 jump 27
+23 jump_if_not 26(27) (i@0 = 40)
+24 set str_i@2 _latin1'40'
+25 jump 27
+26 set str_i@2 _latin1'unknown'
+27 stmt 0 "SELECT str_i, str_j"
+SHOW PROCEDURE CODE proc_19194_nested_3;
+Pos Instruction
+0 set str_i@2 NULL
+1 set str_j@3 NULL
+2 set_case_expr (28) 0 i@0
+3 jump_if_not 6(28) (case_expr@0 = 10)
+4 set str_i@2 _latin1'10'
+5 jump 28
+6 jump_if_not 21(28) (case_expr@0 = 20)
+7 set str_i@2 _latin1'20'
+8 set_case_expr (19) 1 j@1
+9 jump_if_not 12(19) (case_expr@1 = 1)
+10 set str_j@3 _latin1'1'
+11 jump 19
+12 jump_if_not 15(19) (case_expr@1 = 2)
+13 set str_j@3 _latin1'2'
+14 jump 19
+15 jump_if_not 18(19) (case_expr@1 = 3)
+16 set str_j@3 _latin1'3'
+17 jump 19
+18 set str_j@3 _latin1'unknown'
+19 stmt 0 "select "i was 20""
+20 jump 28
+21 jump_if_not 24(28) (case_expr@0 = 30)
+22 set str_i@2 _latin1'30'
+23 jump 28
+24 jump_if_not 27(28) (case_expr@0 = 40)
+25 set str_i@2 _latin1'40'
+26 jump 28
+27 set str_i@2 _latin1'unknown'
+28 stmt 0 "SELECT str_i, str_j"
+SHOW PROCEDURE CODE proc_19194_nested_4;
+Pos Instruction
+0 set str_i@2 NULL
+1 set str_j@3 NULL
+2 jump_if_not 5(26) (i@0 = 10)
+3 set str_i@2 _latin1'10'
+4 jump 26
+5 jump_if_not 19(26) (i@0 = 20)
+6 set str_i@2 _latin1'20'
+7 jump_if_not 10(17) (j@1 = 1)
+8 set str_j@3 _latin1'1'
+9 jump 17
+10 jump_if_not 13(17) (j@1 = 2)
+11 set str_j@3 _latin1'2'
+12 jump 17
+13 jump_if_not 16(17) (j@1 = 3)
+14 set str_j@3 _latin1'3'
+15 jump 17
+16 set str_j@3 _latin1'unknown'
+17 stmt 0 "select "i was 20""
+18 jump 26
+19 jump_if_not 22(26) (i@0 = 30)
+20 set str_i@2 _latin1'30'
+21 jump 26
+22 jump_if_not 25(26) (i@0 = 40)
+23 set str_i@2 _latin1'40'
+24 jump 26
+25 set str_i@2 _latin1'unknown'
+26 stmt 0 "SELECT str_i, str_j"
+CALL proc_19194_nested_1(10, 1);
+str_i str_j
+10 NULL
+CALL proc_19194_nested_1(25, 1);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_1(20, 1);
+i was 20
+i was 20
+str_i str_j
+20 1
+CALL proc_19194_nested_1(20, 2);
+i was 20
+i was 20
+str_i str_j
+20 2
+CALL proc_19194_nested_1(20, 3);
+i was 20
+i was 20
+str_i str_j
+20 3
+CALL proc_19194_nested_1(20, 4);
+i was 20
+i was 20
+str_i str_j
+20 unknown
+CALL proc_19194_nested_1(30, 1);
+str_i str_j
+30 NULL
+CALL proc_19194_nested_1(40, 1);
+str_i str_j
+40 NULL
+CALL proc_19194_nested_1(0, 0);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_2(10, 1);
+str_i str_j
+10 NULL
+CALL proc_19194_nested_2(25, 1);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_2(20, 1);
+i was 20
+i was 20
+str_i str_j
+20 1
+CALL proc_19194_nested_2(20, 2);
+i was 20
+i was 20
+str_i str_j
+20 2
+CALL proc_19194_nested_2(20, 3);
+i was 20
+i was 20
+str_i str_j
+20 3
+CALL proc_19194_nested_2(20, 4);
+i was 20
+i was 20
+str_i str_j
+20 unknown
+CALL proc_19194_nested_2(30, 1);
+str_i str_j
+30 NULL
+CALL proc_19194_nested_2(40, 1);
+str_i str_j
+40 NULL
+CALL proc_19194_nested_2(0, 0);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_3(10, 1);
+str_i str_j
+10 NULL
+CALL proc_19194_nested_3(25, 1);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_3(20, 1);
+i was 20
+i was 20
+str_i str_j
+20 1
+CALL proc_19194_nested_3(20, 2);
+i was 20
+i was 20
+str_i str_j
+20 2
+CALL proc_19194_nested_3(20, 3);
+i was 20
+i was 20
+str_i str_j
+20 3
+CALL proc_19194_nested_3(20, 4);
+i was 20
+i was 20
+str_i str_j
+20 unknown
+CALL proc_19194_nested_3(30, 1);
+str_i str_j
+30 NULL
+CALL proc_19194_nested_3(40, 1);
+str_i str_j
+40 NULL
+CALL proc_19194_nested_3(0, 0);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_4(10, 1);
+str_i str_j
+10 NULL
+CALL proc_19194_nested_4(25, 1);
+str_i str_j
+unknown NULL
+CALL proc_19194_nested_4(20, 1);
+i was 20
+i was 20
+str_i str_j
+20 1
+CALL proc_19194_nested_4(20, 2);
+i was 20
+i was 20
+str_i str_j
+20 2
+CALL proc_19194_nested_4(20, 3);
+i was 20
+i was 20
+str_i str_j
+20 3
+CALL proc_19194_nested_4(20, 4);
+i was 20
+i was 20
+str_i str_j
+20 unknown
+CALL proc_19194_nested_4(30, 1);
+str_i str_j
+30 NULL
+CALL proc_19194_nested_4(40, 1);
+str_i str_j
+40 NULL
+CALL proc_19194_nested_4(0, 0);
+str_i str_j
+unknown NULL
+DROP PROCEDURE proc_19194_simple;
+DROP PROCEDURE proc_19194_searched;
+DROP PROCEDURE proc_19194_nested_1;
+DROP PROCEDURE proc_19194_nested_2;
+DROP PROCEDURE proc_19194_nested_3;
+DROP PROCEDURE proc_19194_nested_4;
DROP PROCEDURE IF EXISTS p1;
CREATE PROCEDURE p1() CREATE INDEX idx ON t1 (c1);
SHOW PROCEDURE CODE p1;
diff --git a/mysql-test/r/sp_stress_case.result b/mysql-test/r/sp_stress_case.result
new file mode 100644
index 00000000000..8ec68363c8d
--- /dev/null
+++ b/mysql-test/r/sp_stress_case.result
@@ -0,0 +1,120 @@
+DROP PROCEDURE IF EXISTS proc_19194_codegen;
+DROP PROCEDURE IF EXISTS bug_19194_simple;
+DROP PROCEDURE IF EXISTS bug_19194_searched;
+CREATE PROCEDURE proc_19194_codegen(
+IN proc_name VARCHAR(50),
+IN count INTEGER,
+IN simple INTEGER,
+OUT body MEDIUMTEXT)
+BEGIN
+DECLARE code MEDIUMTEXT;
+DECLARE i INT DEFAULT 1;
+SET code = concat("CREATE PROCEDURE ", proc_name, "(i INT)\n");
+SET code = concat(code, "BEGIN\n");
+SET code = concat(code, " DECLARE str CHAR(10);\n");
+IF (simple)
+THEN
+SET code = concat(code, " CASE i\n");
+ELSE
+SET code = concat(code, " CASE\n");
+END IF;
+WHILE (i <= count)
+DO
+IF (simple)
+THEN
+SET code = concat(code, " WHEN ", i, " THEN SET str=\"", i, "\";\n");
+ELSE
+SET code = concat(code, " WHEN i=", i, " THEN SET str=\"", i, "\";\n");
+END IF;
+SET i = i + 1;
+END WHILE;
+SET code = concat(code, " ELSE SET str=\"unknown\";\n");
+SET code = concat(code, " END CASE;\n");
+SET code = concat(code, " SELECT str;\n");
+SET code = concat(code, "END\n");
+SET body = code;
+END|
+set @body="";
+call proc_19194_codegen("test_simple", 10, 1, @body);
+select @body;
+@body
+CREATE PROCEDURE test_simple(i INT)
+BEGIN
+ DECLARE str CHAR(10);
+ CASE i
+ WHEN 1 THEN SET str="1";
+ WHEN 2 THEN SET str="2";
+ WHEN 3 THEN SET str="3";
+ WHEN 4 THEN SET str="4";
+ WHEN 5 THEN SET str="5";
+ WHEN 6 THEN SET str="6";
+ WHEN 7 THEN SET str="7";
+ WHEN 8 THEN SET str="8";
+ WHEN 9 THEN SET str="9";
+ WHEN 10 THEN SET str="10";
+ ELSE SET str="unknown";
+ END CASE;
+ SELECT str;
+END
+
+call proc_19194_codegen("test_searched", 10, 0, @body);
+select @body;
+@body
+CREATE PROCEDURE test_searched(i INT)
+BEGIN
+ DECLARE str CHAR(10);
+ CASE
+ WHEN i=1 THEN SET str="1";
+ WHEN i=2 THEN SET str="2";
+ WHEN i=3 THEN SET str="3";
+ WHEN i=4 THEN SET str="4";
+ WHEN i=5 THEN SET str="5";
+ WHEN i=6 THEN SET str="6";
+ WHEN i=7 THEN SET str="7";
+ WHEN i=8 THEN SET str="8";
+ WHEN i=9 THEN SET str="9";
+ WHEN i=10 THEN SET str="10";
+ ELSE SET str="unknown";
+ END CASE;
+ SELECT str;
+END
+
+CALL bug_19194_simple(1);
+str
+1
+CALL bug_19194_simple(2);
+str
+2
+CALL bug_19194_simple(1000);
+str
+1000
+CALL bug_19194_simple(4998);
+str
+4998
+CALL bug_19194_simple(4999);
+str
+4999
+CALL bug_19194_simple(9999);
+str
+unknown
+CALL bug_19194_searched(1);
+str
+1
+CALL bug_19194_searched(2);
+str
+2
+CALL bug_19194_searched(1000);
+str
+1000
+CALL bug_19194_searched(4998);
+str
+4998
+CALL bug_19194_searched(4999);
+str
+4999
+CALL bug_19194_searched(9999);
+str
+unknown
+DROP PROCEDURE proc_19194_codegen;
+DROP PROCEDURE bug_19194_simple;
+DROP PROCEDURE bug_19194_searched;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 4d076db5c22..f8584275a5a 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -3014,4 +3014,13 @@ i j
6 3
DROP VIEW v1, v2;
DROP TABLE t1;
+DROP VIEW IF EXISTS v1;
+CREATE VIEW v1 AS SELECT 'The\ZEnd';
+SELECT * FROM v1;
+TheEnd
+TheEnd
+SHOW CREATE VIEW v1;
+View Create View
+v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select _latin1'The\ZEnd' AS `TheEnd`
+DROP VIEW v1;
End of 5.0 tests.
diff --git a/mysql-test/t/log.sh b/mysql-test/t/log.sh
index 20b265087cc..29cf8d3e1a3 100755
--- a/mysql-test/t/log.sh
+++ b/mysql-test/t/log.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
###########################################################################
diff --git a/mysql-test/t/sp-code.test b/mysql-test/t/sp-code.test
index 72efa831059..97bc29fcad2 100644
--- a/mysql-test/t/sp-code.test
+++ b/mysql-test/t/sp-code.test
@@ -191,6 +191,241 @@ show procedure code sudoku_solve;
drop procedure sudoku_solve;
+#
+# Bug#19194 (Right recursion in parser for CASE causes excessive stack
+# usage, limitation)
+# This bug also exposed a flaw in the generated code with nested case
+# statements
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS proc_19194_simple;
+DROP PROCEDURE IF EXISTS proc_19194_searched;
+DROP PROCEDURE IF EXISTS proc_19194_nested_1;
+DROP PROCEDURE IF EXISTS proc_19194_nested_2;
+DROP PROCEDURE IF EXISTS proc_19194_nested_3;
+DROP PROCEDURE IF EXISTS proc_19194_nested_4;
+--enable_warnings
+
+delimiter |;
+
+CREATE PROCEDURE proc_19194_simple(i int)
+BEGIN
+ DECLARE str CHAR(10);
+
+ CASE i
+ WHEN 1 THEN SET str="1";
+ WHEN 2 THEN SET str="2";
+ WHEN 3 THEN SET str="3";
+ ELSE SET str="unknown";
+ END CASE;
+
+ SELECT str;
+END|
+
+CREATE PROCEDURE proc_19194_searched(i int)
+BEGIN
+ DECLARE str CHAR(10);
+
+ CASE
+ WHEN i=1 THEN SET str="1";
+ WHEN i=2 THEN SET str="2";
+ WHEN i=3 THEN SET str="3";
+ ELSE SET str="unknown";
+ END CASE;
+
+ SELECT str;
+END|
+
+# Outer SIMPLE case, inner SEARCHED case
+CREATE PROCEDURE proc_19194_nested_1(i int, j int)
+BEGIN
+ DECLARE str_i CHAR(10);
+ DECLARE str_j CHAR(10);
+
+ CASE i
+ WHEN 10 THEN SET str_i="10";
+ WHEN 20 THEN
+ BEGIN
+ set str_i="20";
+ CASE
+ WHEN j=1 THEN SET str_j="1";
+ WHEN j=2 THEN SET str_j="2";
+ WHEN j=3 THEN SET str_j="3";
+ ELSE SET str_j="unknown";
+ END CASE;
+ select "i was 20";
+ END;
+ WHEN 30 THEN SET str_i="30";
+ WHEN 40 THEN SET str_i="40";
+ ELSE SET str_i="unknown";
+ END CASE;
+
+ SELECT str_i, str_j;
+END|
+
+# Outer SEARCHED case, inner SIMPLE case
+CREATE PROCEDURE proc_19194_nested_2(i int, j int)
+BEGIN
+ DECLARE str_i CHAR(10);
+ DECLARE str_j CHAR(10);
+
+ CASE
+ WHEN i=10 THEN SET str_i="10";
+ WHEN i=20 THEN
+ BEGIN
+ set str_i="20";
+ CASE j
+ WHEN 1 THEN SET str_j="1";
+ WHEN 2 THEN SET str_j="2";
+ WHEN 3 THEN SET str_j="3";
+ ELSE SET str_j="unknown";
+ END CASE;
+ select "i was 20";
+ END;
+ WHEN i=30 THEN SET str_i="30";
+ WHEN i=40 THEN SET str_i="40";
+ ELSE SET str_i="unknown";
+ END CASE;
+
+ SELECT str_i, str_j;
+END|
+
+# Outer SIMPLE case, inner SIMPLE case
+CREATE PROCEDURE proc_19194_nested_3(i int, j int)
+BEGIN
+ DECLARE str_i CHAR(10);
+ DECLARE str_j CHAR(10);
+
+ CASE i
+ WHEN 10 THEN SET str_i="10";
+ WHEN 20 THEN
+ BEGIN
+ set str_i="20";
+ CASE j
+ WHEN 1 THEN SET str_j="1";
+ WHEN 2 THEN SET str_j="2";
+ WHEN 3 THEN SET str_j="3";
+ ELSE SET str_j="unknown";
+ END CASE;
+ select "i was 20";
+ END;
+ WHEN 30 THEN SET str_i="30";
+ WHEN 40 THEN SET str_i="40";
+ ELSE SET str_i="unknown";
+ END CASE;
+
+ SELECT str_i, str_j;
+END|
+
+# Outer SEARCHED case, inner SEARCHED case
+CREATE PROCEDURE proc_19194_nested_4(i int, j int)
+BEGIN
+ DECLARE str_i CHAR(10);
+ DECLARE str_j CHAR(10);
+
+ CASE
+ WHEN i=10 THEN SET str_i="10";
+ WHEN i=20 THEN
+ BEGIN
+ set str_i="20";
+ CASE
+ WHEN j=1 THEN SET str_j="1";
+ WHEN j=2 THEN SET str_j="2";
+ WHEN j=3 THEN SET str_j="3";
+ ELSE SET str_j="unknown";
+ END CASE;
+ select "i was 20";
+ END;
+ WHEN i=30 THEN SET str_i="30";
+ WHEN i=40 THEN SET str_i="40";
+ ELSE SET str_i="unknown";
+ END CASE;
+
+ SELECT str_i, str_j;
+END|
+
+delimiter ;|
+
+SHOW PROCEDURE CODE proc_19194_simple;
+SHOW PROCEDURE CODE proc_19194_searched;
+SHOW PROCEDURE CODE proc_19194_nested_1;
+SHOW PROCEDURE CODE proc_19194_nested_2;
+SHOW PROCEDURE CODE proc_19194_nested_3;
+SHOW PROCEDURE CODE proc_19194_nested_4;
+
+CALL proc_19194_nested_1(10, 1);
+
+#
+# Before 19194, the generated code was:
+# 20 jump_if_not 23(27) 30
+# 21 set str_i@2 _latin1'30'
+# As opposed to the expected:
+# 20 jump_if_not 23(27) (case_expr@0 = 30)
+# 21 set str_i@2 _latin1'30'
+#
+# and as a result, this call returned "30",
+# because the expression 30 is always true,
+# masking the case 40, case 0 and the else.
+#
+CALL proc_19194_nested_1(25, 1);
+
+CALL proc_19194_nested_1(20, 1);
+CALL proc_19194_nested_1(20, 2);
+CALL proc_19194_nested_1(20, 3);
+CALL proc_19194_nested_1(20, 4);
+CALL proc_19194_nested_1(30, 1);
+CALL proc_19194_nested_1(40, 1);
+CALL proc_19194_nested_1(0, 0);
+
+CALL proc_19194_nested_2(10, 1);
+
+#
+# Before 19194, the generated code was:
+# 20 jump_if_not 23(27) (case_expr@0 = (i@0 = 30))
+# 21 set str_i@2 _latin1'30'
+# As opposed to the expected:
+# 20 jump_if_not 23(27) (i@0 = 30)
+# 21 set str_i@2 _latin1'30'
+# and as a result, this call crashed the server, because there is no
+# such variable as "case_expr@0".
+#
+CALL proc_19194_nested_2(25, 1);
+
+CALL proc_19194_nested_2(20, 1);
+CALL proc_19194_nested_2(20, 2);
+CALL proc_19194_nested_2(20, 3);
+CALL proc_19194_nested_2(20, 4);
+CALL proc_19194_nested_2(30, 1);
+CALL proc_19194_nested_2(40, 1);
+CALL proc_19194_nested_2(0, 0);
+
+CALL proc_19194_nested_3(10, 1);
+CALL proc_19194_nested_3(25, 1);
+CALL proc_19194_nested_3(20, 1);
+CALL proc_19194_nested_3(20, 2);
+CALL proc_19194_nested_3(20, 3);
+CALL proc_19194_nested_3(20, 4);
+CALL proc_19194_nested_3(30, 1);
+CALL proc_19194_nested_3(40, 1);
+CALL proc_19194_nested_3(0, 0);
+
+CALL proc_19194_nested_4(10, 1);
+CALL proc_19194_nested_4(25, 1);
+CALL proc_19194_nested_4(20, 1);
+CALL proc_19194_nested_4(20, 2);
+CALL proc_19194_nested_4(20, 3);
+CALL proc_19194_nested_4(20, 4);
+CALL proc_19194_nested_4(30, 1);
+CALL proc_19194_nested_4(40, 1);
+CALL proc_19194_nested_4(0, 0);
+
+DROP PROCEDURE proc_19194_simple;
+DROP PROCEDURE proc_19194_searched;
+DROP PROCEDURE proc_19194_nested_1;
+DROP PROCEDURE proc_19194_nested_2;
+DROP PROCEDURE proc_19194_nested_3;
+DROP PROCEDURE proc_19194_nested_4;
#
# Bug#19207: Final parenthesis omitted for CREATE INDEX in Stored
diff --git a/mysql-test/t/sp_stress_case.test b/mysql-test/t/sp_stress_case.test
new file mode 100644
index 00000000000..1b5bd8991a9
--- /dev/null
+++ b/mysql-test/t/sp_stress_case.test
@@ -0,0 +1,89 @@
+#
+# Bug#19194 (Right recursion in parser for CASE causes excessive stack
+# usage, limitation)
+#
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS proc_19194_codegen;
+DROP PROCEDURE IF EXISTS bug_19194_simple;
+DROP PROCEDURE IF EXISTS bug_19194_searched;
+--enable_warnings
+
+delimiter |;
+
+CREATE PROCEDURE proc_19194_codegen(
+ IN proc_name VARCHAR(50),
+ IN count INTEGER,
+ IN simple INTEGER,
+ OUT body MEDIUMTEXT)
+BEGIN
+ DECLARE code MEDIUMTEXT;
+ DECLARE i INT DEFAULT 1;
+
+ SET code = concat("CREATE PROCEDURE ", proc_name, "(i INT)\n");
+ SET code = concat(code, "BEGIN\n");
+ SET code = concat(code, " DECLARE str CHAR(10);\n");
+
+ IF (simple)
+ THEN
+ SET code = concat(code, " CASE i\n");
+ ELSE
+ SET code = concat(code, " CASE\n");
+ END IF;
+
+ WHILE (i <= count)
+ DO
+ IF (simple)
+ THEN
+ SET code = concat(code, " WHEN ", i, " THEN SET str=\"", i, "\";\n");
+ ELSE
+ SET code = concat(code, " WHEN i=", i, " THEN SET str=\"", i, "\";\n");
+ END IF;
+
+ SET i = i + 1;
+ END WHILE;
+
+ SET code = concat(code, " ELSE SET str=\"unknown\";\n");
+ SET code = concat(code, " END CASE;\n");
+ SET code = concat(code, " SELECT str;\n");
+
+ SET code = concat(code, "END\n");
+
+ SET body = code;
+END|
+
+delimiter ;|
+
+set @body="";
+call proc_19194_codegen("test_simple", 10, 1, @body);
+select @body;
+call proc_19194_codegen("test_searched", 10, 0, @body);
+select @body;
+
+--disable_query_log
+call proc_19194_codegen("bug_19194_simple", 5000, 1, @body);
+let $proc_body = `select @body`;
+eval $proc_body;
+call proc_19194_codegen("bug_19194_searched", 5000, 1, @body);
+let $proc_body = `select @body`;
+eval $proc_body;
+--enable_query_log
+
+CALL bug_19194_simple(1);
+CALL bug_19194_simple(2);
+CALL bug_19194_simple(1000);
+CALL bug_19194_simple(4998);
+CALL bug_19194_simple(4999);
+CALL bug_19194_simple(9999);
+
+CALL bug_19194_searched(1);
+CALL bug_19194_searched(2);
+CALL bug_19194_searched(1000);
+CALL bug_19194_searched(4998);
+CALL bug_19194_searched(4999);
+CALL bug_19194_searched(9999);
+
+DROP PROCEDURE proc_19194_codegen;
+DROP PROCEDURE bug_19194_simple;
+DROP PROCEDURE bug_19194_searched;
+
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index 8473458ae15..a34a1ba117d 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -2960,4 +2960,19 @@ DROP VIEW v1, v2;
DROP TABLE t1;
+#
+# BUG#24293: '\Z' token is not handled correctly in views
+#
+
+--disable_warnings
+DROP VIEW IF EXISTS v1;
+--enable_warnings
+
+CREATE VIEW v1 AS SELECT 'The\ZEnd';
+SELECT * FROM v1;
+
+SHOW CREATE VIEW v1;
+
+DROP VIEW v1;
+
--echo End of 5.0 tests.
diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc
index 0f05d1030d7..15583b233fb 100644
--- a/server-tools/instance-manager/listener.cc
+++ b/server-tools/instance-manager/listener.cc
@@ -187,7 +187,7 @@ void Listener_thread::run()
else
{
shutdown(client_fd, SHUT_RDWR);
- close(client_fd);
+ closesocket(client_fd);
}
}
}
@@ -199,7 +199,7 @@ void Listener_thread::run()
log_info("Listener_thread::run(): shutdown requested, exiting...");
for (i= 0; i < num_sockets; i++)
- close(sockets[i]);
+ closesocket(sockets[i]);
#ifndef __WIN__
unlink(unix_socket_address.sun_path);
@@ -212,7 +212,7 @@ void Listener_thread::run()
err:
// we have to close the ip sockets in case of error
for (i= 0; i < num_sockets; i++)
- close(sockets[i]);
+ closesocket(sockets[i]);
thread_registry.unregister_thread(&thread_info);
thread_registry.request_shutdown();
@@ -259,7 +259,7 @@ int Listener_thread::create_tcp_socket()
{
log_error("Listener_thread::run(): bind(ip socket) failed, '%s'",
strerror(errno));
- close(ip_socket);
+ closesocket(ip_socket);
return -1;
}
@@ -267,7 +267,7 @@ int Listener_thread::create_tcp_socket()
{
log_error("Listener_thread::run(): listen(ip socket) failed, %s",
strerror(errno));
- close(ip_socket);
+ closesocket(ip_socket);
return -1;
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4138573785e..1ef77208469 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3688,8 +3688,9 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
if (entry->value == pos)
entry->value=0;
- if (!(entry->value=(char*) my_realloc(entry->value, length,
- MYF(MY_ALLOW_ZERO_PTR))))
+ entry->value= (char*) my_realloc(entry->value, length,
+ MYF(MY_ALLOW_ZERO_PTR | MY_WME));
+ if (!entry->value)
return 1;
}
}
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ff39421cef7..fb99ed1bb5c 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -605,27 +605,6 @@ sp_head::create(THD *thd)
DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
m_type, m_name.str, m_params.str, m_body.str));
-#ifndef DBUG_OFF
- optimize();
- {
- String s;
- sp_instr *i;
- uint ip= 0;
- while ((i = get_instr(ip)))
- {
- char buf[8];
-
- sprintf(buf, "%4u: ", ip);
- s.append(buf);
- i->print(&s);
- s.append('\n');
- ip+= 1;
- }
- s.append('\0');
- DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr()));
- }
-#endif
-
if (m_type == TYPE_ENUM_FUNCTION)
ret= sp_create_function(thd, this);
else
@@ -2173,7 +2152,7 @@ sp_head::show_create_function(THD *thd)
This is the main mark and move loop; it relies on the following methods
in sp_instr and its subclasses:
- opt_mark() Mark instruction as reachable (will recurse for jumps)
+ opt_mark() Mark instruction as reachable
opt_shortcut_jump() Shortcut jumps to the final destination;
used by opt_mark().
opt_move() Update moved instruction
@@ -2186,7 +2165,7 @@ void sp_head::optimize()
sp_instr *i;
uint src, dst;
- opt_mark(0);
+ opt_mark();
bp.empty();
src= dst= 0;
@@ -2220,13 +2199,50 @@ void sp_head::optimize()
bp.empty();
}
+void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads)
+{
+ sp_instr *i= get_instr(ip);
+
+ if (i && ! i->marked)
+ leads->push_front(i);
+}
+
void
-sp_head::opt_mark(uint ip)
+sp_head::opt_mark()
{
+ uint ip;
sp_instr *i;
+ List<sp_instr> leads;
- while ((i= get_instr(ip)) && !i->marked)
- ip= i->opt_mark(this);
+ /*
+ Forward flow analysis algorithm in the instruction graph:
+ - first, add the entry point in the graph (the first instruction) to the
+ 'leads' list of paths to explore.
+ - while there are still leads to explore:
+ - pick one lead, and follow the path forward. Mark instruction reached.
+ Stop only if the end of the routine is reached, or the path converge
+ to code already explored (marked).
+ - while following a path, collect in the 'leads' list any fork to
+ another path (caused by conditional jumps instructions), so that these
+ paths can be explored as well.
+ */
+
+ /* Add the entry point */
+ i= get_instr(0);
+ leads.push_front(i);
+
+ /* For each path of code ... */
+ while (leads.elements != 0)
+ {
+ i= leads.pop();
+
+ /* Mark the entire path, collecting new leads. */
+ while (i && ! i->marked)
+ {
+ ip= i->opt_mark(this, & leads);
+ i= get_instr(ip);
+ }
+ }
}
@@ -2619,7 +2635,7 @@ sp_instr_jump::print(String *str)
}
uint
-sp_instr_jump::opt_mark(sp_head *sp)
+sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
m_dest= opt_shortcut_jump(sp, this);
if (m_dest != m_ip+1) /* Jumping to following instruction? */
@@ -2713,7 +2729,7 @@ sp_instr_jump_if_not::print(String *str)
uint
-sp_instr_jump_if_not::opt_mark(sp_head *sp)
+sp_instr_jump_if_not::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -2723,13 +2739,13 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp)
m_dest= i->opt_shortcut_jump(sp, this);
m_optdest= sp->get_instr(m_dest);
}
- sp->opt_mark(m_dest);
+ sp->add_mark_lead(m_dest, leads);
if ((i= sp->get_instr(m_cont_dest)))
{
m_cont_dest= i->opt_shortcut_jump(sp, this);
m_cont_optdest= sp->get_instr(m_cont_dest);
}
- sp->opt_mark(m_cont_dest);
+ sp->add_mark_lead(m_cont_dest, leads);
return m_ip+1;
}
@@ -2850,7 +2866,7 @@ sp_instr_hpush_jump::print(String *str)
uint
-sp_instr_hpush_jump::opt_mark(sp_head *sp)
+sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -2860,7 +2876,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp)
m_dest= i->opt_shortcut_jump(sp, this);
m_optdest= sp->get_instr(m_dest);
}
- sp->opt_mark(m_dest);
+ sp->add_mark_lead(m_dest, leads);
return m_ip+1;
}
@@ -2925,15 +2941,13 @@ sp_instr_hreturn::print(String *str)
uint
-sp_instr_hreturn::opt_mark(sp_head *sp)
+sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
if (m_dest)
- return sp_instr_jump::opt_mark(sp);
- else
- {
- marked= 1;
- return UINT_MAX;
- }
+ return sp_instr_jump::opt_mark(sp, leads);
+
+ marked= 1;
+ return UINT_MAX;
}
@@ -3276,7 +3290,7 @@ sp_instr_set_case_expr::print(String *str)
}
uint
-sp_instr_set_case_expr::opt_mark(sp_head *sp)
+sp_instr_set_case_expr::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -3286,7 +3300,7 @@ sp_instr_set_case_expr::opt_mark(sp_head *sp)
m_cont_dest= i->opt_shortcut_jump(sp, this);
m_cont_optdest= sp->get_instr(m_cont_dest);
}
- sp->opt_mark(m_cont_dest);
+ sp->add_mark_lead(m_cont_dest, leads);
return m_ip+1;
}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index a82a65458ea..0139f879ce4 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -301,8 +301,19 @@ public:
void restore_thd_mem_root(THD *thd);
+ /**
+ Optimize the code.
+ */
void optimize();
- void opt_mark(uint ip);
+
+ /**
+ Helper used during flow analysis during code optimization.
+ See the implementation of <code>opt_mark()</code>.
+ @param ip the instruction to add to the leads list
+ @param leads the list of remaining paths to explore in the graph that
+ represents the code, during flow analysis.
+ */
+ void add_mark_lead(uint ip, List<sp_instr> *leads);
void recursion_level_error(THD *thd);
@@ -392,6 +403,12 @@ private:
bool
execute(THD *thd);
+ /**
+ Perform a forward flow analysis in the generated code.
+ Mark reachable instructions, for the optimizer.
+ */
+ void opt_mark();
+
/*
Merge the list of tables used by query into the multi-set of tables used
by routine.
@@ -459,10 +476,10 @@ public:
/*
Mark this instruction as reachable during optimization and return the
- index to the next instruction. Jump instruction will mark their
- destination too recursively.
+ index to the next instruction. Jump instruction will add their
+ destination to the leads list.
*/
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return m_ip+1;
@@ -734,7 +751,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start);
@@ -784,7 +801,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
/* Override sp_instr_jump's shortcut; we stop here */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
@@ -830,7 +847,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return UINT_MAX;
@@ -867,7 +884,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
/* Override sp_instr_jump's shortcut; we stop here. */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
@@ -932,7 +949,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
private:
@@ -1102,7 +1119,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return UINT_MAX;
@@ -1135,7 +1152,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
virtual void opt_move(uint dst, List<sp_instr> *ibp);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 84d2ce77014..0794d4c797a 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -884,6 +884,13 @@ void select_result::cleanup()
/* do nothing */
}
+bool select_result::check_simple_select() const
+{
+ my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
+ return TRUE;
+}
+
+
static String default_line_term("\n",default_charset_info);
static String default_escaped("\\",default_charset_info);
static String default_field_term("\t",default_charset_info);
@@ -1521,6 +1528,13 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
}
+bool select_dumpvar::check_simple_select() const
+{
+ my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0));
+ return TRUE;
+}
+
+
void select_dumpvar::cleanup()
{
row_count= 0;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index efc13c02a59..d74158ec07c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1723,7 +1723,14 @@ public:
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
- virtual bool simple_select() { return 0; }
+ /**
+ Check if this query returns a result set and therefore is allowed in
+ cursors and set an error message if it is not the case.
+
+ @retval FALSE success
+ @retval TRUE error, an error message is set
+ */
+ virtual bool check_simple_select() const;
virtual void abort() {}
/*
Cleanup instance of this class for next execution of a prepared
@@ -1761,7 +1768,7 @@ public:
bool send_fields(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
- bool simple_select() { return 1; }
+ virtual bool check_simple_select() const { return FALSE; }
void abort();
};
@@ -2200,6 +2207,7 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
bool send_eof();
+ virtual bool check_simple_select() const;
void cleanup();
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 45272645633..986cb4760de 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1163,7 +1163,6 @@ void st_select_lex::init_select()
options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0;
- when_list.empty();
expr_list.empty();
udf_list.empty();
interval_list.empty();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index db119d527d9..a7b162c6062 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -517,7 +517,6 @@ public:
SQL_LIST order_list; /* ORDER clause */
List<List_item> expr_list;
- List<List_item> when_list; /* WHEN clause (expression) */
SQL_LIST *gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
// Arrays of pointers to top elements of all_fields list
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index e9f33a172c4..152cc3aa385 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2902,10 +2902,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
in INSERT ... SELECT and similar commands.
*/
- if (open_cursor && lex->result && !lex->result->simple_select())
+ if (open_cursor && lex->result && lex->result->check_simple_select())
{
DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
- my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
return TRUE;
}
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 16f35e09e02..ef50e5864a5 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1032,8 +1032,8 @@ void String::print(String *str)
case '\r':
str->append(STRING_WITH_LEN("\\r"));
break;
- case 26: //Ctrl-Z
- str->append(STRING_WITH_LEN("\\z"));
+ case '\032': // Ctrl-Z
+ str->append(STRING_WITH_LEN("\\Z"));
break;
default:
str->append(c);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 90dc6d54fe1..1fa23beb2f7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -95,6 +95,187 @@ void turn_parser_debug_on()
}
#endif
+
+/**
+ Helper action for a case statement (entering the CASE).
+ This helper is used for both 'simple' and 'searched' cases.
+ This helper, with the other case_stmt_action_..., is executed when
+ the following SQL code is parsed:
+<pre>
+CREATE PROCEDURE proc_19194_simple(i int)
+BEGIN
+ DECLARE str CHAR(10);
+
+ CASE i
+ WHEN 1 THEN SET str="1";
+ WHEN 2 THEN SET str="2";
+ WHEN 3 THEN SET str="3";
+ ELSE SET str="unknown";
+ END CASE;
+
+ SELECT str;
+END
+</pre>
+ The actions are used to generate the following code:
+<pre>
+SHOW PROCEDURE CODE proc_19194_simple;
+Pos Instruction
+0 set str@1 NULL
+1 set_case_expr (12) 0 i@0
+2 jump_if_not 5(12) (case_expr@0 = 1)
+3 set str@1 _latin1'1'
+4 jump 12
+5 jump_if_not 8(12) (case_expr@0 = 2)
+6 set str@1 _latin1'2'
+7 jump 12
+8 jump_if_not 11(12) (case_expr@0 = 3)
+9 set str@1 _latin1'3'
+10 jump 12
+11 set str@1 _latin1'unknown'
+12 stmt 0 "SELECT str"
+</pre>
+
+ @param lex the parser lex context
+*/
+
+void case_stmt_action_case(LEX *lex)
+{
+ lex->sphead->new_cont_backpatch(NULL);
+
+ /*
+ BACKPATCH: Creating target label for the jump to
+ "case_stmt_action_end_case"
+ (Instruction 12 in the example)
+ */
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+}
+
+/**
+ Helper action for a case expression statement (the expr in 'CASE expr').
+ This helper is used for 'searched' cases only.
+ @param lex the parser lex context
+ @param expr the parsed expression
+ @return 0 on success
+*/
+
+int case_stmt_action_expr(LEX *lex, Item* expr)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *parsing_ctx= lex->spcont;
+ int case_expr_id= parsing_ctx->register_case_expr();
+ sp_instr_set_case_expr *i;
+
+ if (parsing_ctx->push_case_expr_id(case_expr_id))
+ return 1;
+
+ i= new sp_instr_set_case_expr(sp->instructions(),
+ parsing_ctx, case_expr_id, expr, lex);
+
+ sp->add_cont_backpatch(i);
+ sp->add_instr(i);
+
+ return 0;
+}
+
+/**
+ Helper action for a case when condition.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+ @param when the parsed expression for the WHEN clause
+ @param simple true for simple cases, false for searched cases
+*/
+
+void case_stmt_action_when(LEX *lex, Item *when, bool simple)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+ Item_case_expr *var;
+ Item *expr;
+
+ if (simple)
+ {
+ var= new Item_case_expr(ctx->get_current_case_expr_id());
+
+#ifndef DBUG_OFF
+ if (var)
+ {
+ var->m_sp= sp;
+ }
+#endif
+
+ expr= new Item_func_eq(var, when);
+ i= new sp_instr_jump_if_not(ip, ctx, expr, lex);
+ }
+ else
+ i= new sp_instr_jump_if_not(ip, ctx, when, lex);
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_cont_backpatch(i);
+ sp->add_instr(i);
+}
+
+/**
+ Helper action for a case then statements.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+*/
+
+void case_stmt_action_then(LEX *lex)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+ sp->add_instr(i);
+
+ /*
+ BACKPATCH: Resolving forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ sp->backpatch(ctx->pop_label());
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_then" to "case_stmt_action_end_case"
+ (jump from instruction 4 to 12, 7 to 12 ... in the example)
+ */
+
+ sp->push_backpatch(i, ctx->last_label());
+}
+
+/**
+ Helper action for an end case.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+ @param simple true for simple cases, false for searched cases
+*/
+
+void case_stmt_action_end_case(LEX *lex, bool simple)
+{
+ /*
+ BACKPATCH: Resolving forward jump from
+ "case_stmt_action_then" to "case_stmt_action_end_case"
+ (jump from instruction 4 to 12, 7 to 12 ... in the example)
+ */
+ lex->sphead->backpatch(lex->spcont->pop_label());
+
+ if (simple)
+ lex->spcont->pop_case_expr_id();
+
+ lex->sphead->do_cont_backpatch();
+}
+
%}
%union {
int num;
@@ -831,7 +1012,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- when_list2 expr_list2 udf_expr_list3 handler
+ expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
@@ -859,6 +1040,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail
view_suid view_tail view_list_opt view_list view_select
view_check_option trigger_tail sp_tail
+ case_stmt_specification simple_case_stmt searched_case_stmt
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -1993,43 +2175,7 @@ sp_proc_stmt:
{ Lex->sphead->new_cont_backpatch(NULL); }
sp_if END IF
{ Lex->sphead->do_cont_backpatch(); }
- | CASE_SYM WHEN_SYM
- {
- Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE;
- Lex->sphead->new_cont_backpatch(NULL);
- }
- sp_case END CASE_SYM { Lex->sphead->do_cont_backpatch(); }
- | CASE_SYM
- {
- Lex->sphead->reset_lex(YYTHD);
- Lex->sphead->new_cont_backpatch(NULL);
- }
- expr WHEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *parsing_ctx= lex->spcont;
- int case_expr_id= parsing_ctx->register_case_expr();
- sp_instr_set_case_expr *i;
-
- if (parsing_ctx->push_case_expr_id(case_expr_id))
- YYABORT;
-
- i= new sp_instr_set_case_expr(sp->instructions(),
- parsing_ctx,
- case_expr_id,
- $3,
- lex);
- sp->add_cont_backpatch(i);
- sp->add_instr(i);
- sp->m_flags|= sp_head::IN_SIMPLE_CASE;
- sp->restore_lex(YYTHD);
- }
- sp_case END CASE_SYM
- {
- Lex->spcont->pop_case_expr_id();
- Lex->sphead->do_cont_backpatch();
- }
+ | case_stmt_specification
| sp_labeled_control
{}
| { /* Unlabeled controls get a secret label. */
@@ -2240,72 +2386,114 @@ sp_elseifs:
| ELSE sp_proc_stmts1
;
-sp_case:
- { Lex->sphead->reset_lex(YYTHD); }
- expr THEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i;
-
- if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE))
- i= new sp_instr_jump_if_not(ip, ctx, $2, lex);
- else
- { /* Simple case: <caseval> = <whenval> */
+case_stmt_specification:
+ simple_case_stmt
+ | searched_case_stmt
+ ;
- Item_case_expr *var;
- Item *expr;
+simple_case_stmt:
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_case(lex);
+ lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ LEX *lex= Lex;
+ if (case_stmt_action_expr(lex, $3))
+ YYABORT;
- var= new Item_case_expr(ctx->get_current_case_expr_id());
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ simple_when_clause_list
+ else_clause_opt
+ END
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_end_case(lex, true);
+ }
+ ;
-#ifndef DBUG_OFF
- if (var)
- var->m_sp= sp;
-#endif
+searched_case_stmt:
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_case(lex);
+ }
+ searched_when_clause_list
+ else_clause_opt
+ END
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_end_case(lex, false);
+ }
+ ;
- expr= new Item_func_eq(var, $2);
+simple_when_clause_list:
+ simple_when_clause
+ | simple_when_clause_list simple_when_clause
+ ;
- i= new sp_instr_jump_if_not(ip, ctx, expr, lex);
- }
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- sp->add_cont_backpatch(i);
- sp->add_instr(i);
- sp->restore_lex(YYTHD);
- }
- sp_proc_stmts1
- {
- sp_head *sp= Lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+searched_when_clause_list:
+ searched_when_clause
+ | searched_when_clause_list searched_when_clause
+ ;
- sp->add_instr(i);
- sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- }
- sp_whens
- {
- LEX *lex= Lex;
+simple_when_clause:
+ WHEN_SYM
+ {
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ /* Simple case: <caseval> = <whenval> */
- lex->sphead->backpatch(lex->spcont->pop_label());
- }
- ;
+ LEX *lex= Lex;
+ case_stmt_action_when(lex, $3, true);
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ THEN_SYM
+ sp_proc_stmts1
+ {
+ LEX *lex= Lex;
+ case_stmt_action_then(lex);
+ }
+ ;
-sp_whens:
- /* Empty */
- {
- sp_head *sp= Lex->sphead;
- uint ip= sp->instructions();
- sp_instr_error *i= new sp_instr_error(ip, Lex->spcont,
- ER_SP_CASE_NOT_FOUND);
+searched_when_clause:
+ WHEN_SYM
+ {
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ LEX *lex= Lex;
+ case_stmt_action_when(lex, $3, false);
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ THEN_SYM
+ sp_proc_stmts1
+ {
+ LEX *lex= Lex;
+ case_stmt_action_then(lex);
+ }
+ ;
- sp->add_instr(i);
- }
- | ELSE sp_proc_stmts1 {}
- | WHEN_SYM sp_case {}
- ;
+else_clause_opt:
+ /* empty */
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_error *i= new sp_instr_error(ip, lex->spcont,
+ ER_SP_CASE_NOT_FOUND);
+ sp->add_instr(i);
+ }
+ | ELSE sp_proc_stmts1
+ ;
sp_labeled_control:
label_ident ':'
@@ -4372,8 +4560,8 @@ simple_expr:
if (!$$)
YYABORT;
}
- | CASE_SYM opt_expr WHEN_SYM when_list opt_else END
- { $$= new Item_func_case(* $4, $2, $5 ); }
+ | CASE_SYM opt_expr when_list opt_else END
+ { $$= new Item_func_case(* $3, $2, $4 ); }
| CONVERT_SYM '(' expr ',' cast_type ')'
{
$$= create_func_cast($3, $5,
@@ -5182,23 +5370,19 @@ opt_else:
| ELSE expr { $$= $2; };
when_list:
- { Select->when_list.push_front(new List<Item>); }
- when_list2
- { $$= Select->when_list.pop(); };
-
-when_list2:
- expr THEN_SYM expr
- {
- SELECT_LEX *sel=Select;
- sel->when_list.head()->push_back($1);
- sel->when_list.head()->push_back($3);
- }
- | when_list2 WHEN_SYM expr THEN_SYM expr
- {
- SELECT_LEX *sel=Select;
- sel->when_list.head()->push_back($3);
- sel->when_list.head()->push_back($5);
- };
+ WHEN_SYM expr THEN_SYM expr
+ {
+ $$= new List<Item>;
+ $$->push_back($2);
+ $$->push_back($4);
+ }
+ | when_list WHEN_SYM expr THEN_SYM expr
+ {
+ $1->push_back($3);
+ $1->push_back($5);
+ $$= $1;
+ }
+ ;
/* Warning - may return NULL in case of incomplete SELECT */
table_ref:
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 9e3b5bfe10e..848eedc7a3f 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -15455,6 +15455,33 @@ static void test_bug21635()
DBUG_VOID_RETURN;
}
+/*
+ Bug#24179 "select b into $var" fails with --cursor_protocol"
+ The failure is correct, check that the returned message is meaningful.
+*/
+
+static void test_bug24179()
+{
+ int rc;
+ MYSQL_STMT *stmt;
+
+ DBUG_ENTER("test_bug24179");
+ myheader("test_bug24179");
+
+ stmt= open_cursor("select 1 into @a");
+ rc= mysql_stmt_execute(stmt);
+ DIE_UNLESS(rc);
+ if (!opt_silent)
+ {
+ printf("Got error (as expected): %d %s\n",
+ mysql_stmt_errno(stmt),
+ mysql_stmt_error(stmt));
+ }
+ DIE_UNLESS(mysql_stmt_errno(stmt) == 1323);
+
+ DBUG_VOID_RETURN;
+}
+
/*
Read and parse arguments and MySQL options from my.cnf
@@ -15734,6 +15761,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug21726", test_bug21726 },
{ "test_bug23383", test_bug23383 },
{ "test_bug21635", test_bug21635 },
+ { "test_bug24179", test_bug24179 },
{ 0, 0 }
};