mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Bug#19194 (Right recursion in parser for CASE causes excessive stack usage,
limitation) Bug#24854 (Mixing Searched Case with Simple Case inside Stored Procedure crashes Mysqld) Implemented code review (19194) comments
This commit is contained in:
@@ -1,42 +1,120 @@
|
||||
DROP PROCEDURE IF EXISTS bug_19194_a;
|
||||
DROP PROCEDURE IF EXISTS bug_19194_b;
|
||||
'Silently creating PROCEDURE bug_19194_a'
|
||||
'Silently creating PROCEDURE bug_19194_b'
|
||||
CALL bug_19194_a(1);
|
||||
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_a(2);
|
||||
CALL bug_19194_simple(2);
|
||||
str
|
||||
2
|
||||
CALL bug_19194_a(1000);
|
||||
CALL bug_19194_simple(1000);
|
||||
str
|
||||
1000
|
||||
CALL bug_19194_a(4998);
|
||||
CALL bug_19194_simple(4998);
|
||||
str
|
||||
4998
|
||||
CALL bug_19194_a(4999);
|
||||
CALL bug_19194_simple(4999);
|
||||
str
|
||||
4999
|
||||
CALL bug_19194_a(9999);
|
||||
CALL bug_19194_simple(9999);
|
||||
str
|
||||
unknown
|
||||
CALL bug_19194_b(1);
|
||||
CALL bug_19194_searched(1);
|
||||
str
|
||||
1
|
||||
CALL bug_19194_b(2);
|
||||
CALL bug_19194_searched(2);
|
||||
str
|
||||
2
|
||||
CALL bug_19194_b(1000);
|
||||
CALL bug_19194_searched(1000);
|
||||
str
|
||||
1000
|
||||
CALL bug_19194_b(4998);
|
||||
CALL bug_19194_searched(4998);
|
||||
str
|
||||
4998
|
||||
CALL bug_19194_b(4999);
|
||||
CALL bug_19194_searched(4999);
|
||||
str
|
||||
4999
|
||||
CALL bug_19194_b(9999);
|
||||
CALL bug_19194_searched(9999);
|
||||
str
|
||||
unknown
|
||||
DROP PROCEDURE bug_19194_a;
|
||||
DROP PROCEDURE bug_19194_b;
|
||||
DROP PROCEDURE proc_19194_codegen;
|
||||
DROP PROCEDURE bug_19194_simple;
|
||||
DROP PROCEDURE bug_19194_searched;
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Bug#19194 (Right recursion in parser for CASE causes excessive stack
|
||||
# usage, limitation)
|
||||
#
|
||||
# Because the code for the CASE statement is so massive,
|
||||
# checking in an already generated .test is not practical,
|
||||
# due to it's size (10 000 lines or 356 000 bytes).
|
||||
#
|
||||
# Patches are sent by email, which introduce size limitations.
|
||||
#
|
||||
# As a result, code is generated dynamically here.
|
||||
# This script takes no argument, and generates code in stdout.
|
||||
#
|
||||
|
||||
cat <<EOF
|
||||
echo 'Silently creating PROCEDURE bug_19194_a';
|
||||
--disable_query_log
|
||||
delimiter |;
|
||||
CREATE PROCEDURE bug_19194_a(i int)
|
||||
BEGIN
|
||||
DECLARE str CHAR(10);
|
||||
CASE i
|
||||
EOF
|
||||
|
||||
count=1;
|
||||
while true; do
|
||||
echo " WHEN $count THEN SET str=\"$count\";"
|
||||
count=`expr $count + 1`
|
||||
test $count -gt 5000 && break
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
ELSE SET str="unknown";
|
||||
END CASE;
|
||||
SELECT str;
|
||||
END|
|
||||
delimiter ;|
|
||||
--enable_query_log
|
||||
EOF
|
||||
|
||||
cat <<EOF
|
||||
echo 'Silently creating PROCEDURE bug_19194_b';
|
||||
--disable_query_log
|
||||
delimiter |;
|
||||
CREATE PROCEDURE bug_19194_b(i int)
|
||||
BEGIN
|
||||
DECLARE str CHAR(10);
|
||||
CASE
|
||||
EOF
|
||||
|
||||
count=1;
|
||||
while true; do
|
||||
echo " WHEN i=$count THEN SET str=\"$count\";"
|
||||
count=`expr $count + 1`
|
||||
test $count -gt 5000 && break
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
ELSE SET str="unknown";
|
||||
END CASE;
|
||||
SELECT str;
|
||||
END|
|
||||
delimiter ;|
|
||||
--enable_query_log
|
||||
EOF
|
||||
|
||||
@@ -1,35 +1,94 @@
|
||||
|
||||
# The production code tested is not platform specific,
|
||||
# but /bin/sh is needed to run the test case.
|
||||
|
||||
--source include/not_windows.inc
|
||||
|
||||
#
|
||||
# Bug#19194 (Right recursion in parser for CASE causes excessive stack
|
||||
# usage, limitation)
|
||||
#
|
||||
|
||||
# This test takes some time (8 min) in debug builds
|
||||
# It's provided as a separate file so that the next line can be uncommented
|
||||
# later if needed:
|
||||
# -- source include/big_test.inc
|
||||
|
||||
--disable_warnings
|
||||
DROP PROCEDURE IF EXISTS bug_19194_a;
|
||||
DROP PROCEDURE IF EXISTS bug_19194_b;
|
||||
DROP PROCEDURE IF EXISTS proc_19194_codegen;
|
||||
DROP PROCEDURE IF EXISTS bug_19194_simple;
|
||||
DROP PROCEDURE IF EXISTS bug_19194_searched;
|
||||
--enable_warnings
|
||||
|
||||
--exec $MYSQL_TEST_DIR/t/sp_stress_case.sh | $MYSQL_TEST 2>&1
|
||||
delimiter |;
|
||||
|
||||
CALL bug_19194_a(1);
|
||||
CALL bug_19194_a(2);
|
||||
CALL bug_19194_a(1000);
|
||||
CALL bug_19194_a(4998);
|
||||
CALL bug_19194_a(4999);
|
||||
CALL bug_19194_a(9999);
|
||||
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;
|
||||
|
||||
CALL bug_19194_b(1);
|
||||
CALL bug_19194_b(2);
|
||||
CALL bug_19194_b(1000);
|
||||
CALL bug_19194_b(4998);
|
||||
CALL bug_19194_b(4999);
|
||||
CALL bug_19194_b(9999);
|
||||
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");
|
||||
|
||||
DROP PROCEDURE bug_19194_a;
|
||||
DROP PROCEDURE bug_19194_b;
|
||||
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;
|
||||
|
||||
|
||||
@@ -100,6 +100,42 @@ void turn_parser_debug_on()
|
||||
/**
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -110,6 +146,7 @@ void case_stmt_action_case(LEX *lex)
|
||||
/*
|
||||
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());
|
||||
@@ -179,6 +216,7 @@ void case_stmt_action_when(LEX *lex, Item *when, bool simple)
|
||||
/*
|
||||
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));
|
||||
@@ -203,13 +241,15 @@ void case_stmt_action_then(LEX *lex)
|
||||
/*
|
||||
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_when" to "case_stmt_action_end_case"
|
||||
"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());
|
||||
@@ -227,6 +267,7 @@ 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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user