Commit 64b7a75a authored by malff/marcsql@weblab.(none)'s avatar malff/marcsql@weblab.(none)

Merge malff@bk-internal.mysql.com:/home/bk/mysql-5.0-runtime

into  weblab.(none):/home/marcsql/TREE/mysql-5.0-runtime
parents 704951fa 7696592b
......@@ -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;
......
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;
......@@ -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
......
#
# 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;
......@@ -603,27 +603,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
......@@ -2171,7 +2150,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
......@@ -2184,7 +2163,7 @@ void sp_head::optimize()
sp_instr *i;
uint src, dst;
opt_mark(0);
opt_mark();
bp.empty();
src= dst= 0;
......@@ -2218,13 +2197,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);
}
}
}
......@@ -2617,7 +2633,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? */
......@@ -2711,7 +2727,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;
......@@ -2721,13 +2737,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;
}
......@@ -2848,7 +2864,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;
......@@ -2858,7 +2874,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;
}
......@@ -2923,15 +2939,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;
}
......@@ -3274,7 +3288,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;
......@@ -3284,7 +3298,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;
}
......
......@@ -302,8 +302,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);
......@@ -393,6 +404,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.
......@@ -460,10 +477,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;
......@@ -735,7 +752,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);
......@@ -785,7 +802,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)
......@@ -831,7 +848,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;
......@@ -868,7 +885,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)
......@@ -933,7 +950,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:
......@@ -1103,7 +1120,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;
......@@ -1136,7 +1153,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);
......
......@@ -1164,7 +1164,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();
......
......@@ -518,7 +518,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
......
......@@ -96,6 +96,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;
......@@ -832,7 +1013,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
......@@ -860,6 +1041,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
......@@ -1995,43 +2177,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. */
......@@ -2242,72 +2388,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 ':'
......@@ -4374,8 +4562,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,
......@@ -5184,23 +5372,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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment