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
parent ce5a3fcc
DROP PROCEDURE IF EXISTS bug_19194_a; DROP PROCEDURE IF EXISTS proc_19194_codegen;
DROP PROCEDURE IF EXISTS bug_19194_b; DROP PROCEDURE IF EXISTS bug_19194_simple;
'Silently creating PROCEDURE bug_19194_a' DROP PROCEDURE IF EXISTS bug_19194_searched;
'Silently creating PROCEDURE bug_19194_b' CREATE PROCEDURE proc_19194_codegen(
CALL bug_19194_a(1); 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 str
1 1
CALL bug_19194_a(2); CALL bug_19194_simple(2);
str str
2 2
CALL bug_19194_a(1000); CALL bug_19194_simple(1000);
str str
1000 1000
CALL bug_19194_a(4998); CALL bug_19194_simple(4998);
str str
4998 4998
CALL bug_19194_a(4999); CALL bug_19194_simple(4999);
str str
4999 4999
CALL bug_19194_a(9999); CALL bug_19194_simple(9999);
str str
unknown unknown
CALL bug_19194_b(1); CALL bug_19194_searched(1);
str str
1 1
CALL bug_19194_b(2); CALL bug_19194_searched(2);
str str
2 2
CALL bug_19194_b(1000); CALL bug_19194_searched(1000);
str str
1000 1000
CALL bug_19194_b(4998); CALL bug_19194_searched(4998);
str str
4998 4998
CALL bug_19194_b(4999); CALL bug_19194_searched(4999);
str str
4999 4999
CALL bug_19194_b(9999); CALL bug_19194_searched(9999);
str str
unknown unknown
DROP PROCEDURE bug_19194_a; DROP PROCEDURE proc_19194_codegen;
DROP PROCEDURE bug_19194_b; DROP PROCEDURE bug_19194_simple;
DROP PROCEDURE bug_19194_searched;
#!/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
# 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 # Bug#19194 (Right recursion in parser for CASE causes excessive stack
# usage, limitation) # 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 --disable_warnings
DROP PROCEDURE IF EXISTS bug_19194_a; DROP PROCEDURE IF EXISTS proc_19194_codegen;
DROP PROCEDURE IF EXISTS bug_19194_b; DROP PROCEDURE IF EXISTS bug_19194_simple;
DROP PROCEDURE IF EXISTS bug_19194_searched;
--enable_warnings --enable_warnings
--exec $MYSQL_TEST_DIR/t/sp_stress_case.sh | $MYSQL_TEST 2>&1 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_a(1); CALL bug_19194_simple(1);
CALL bug_19194_a(2); CALL bug_19194_simple(2);
CALL bug_19194_a(1000); CALL bug_19194_simple(1000);
CALL bug_19194_a(4998); CALL bug_19194_simple(4998);
CALL bug_19194_a(4999); CALL bug_19194_simple(4999);
CALL bug_19194_a(9999); CALL bug_19194_simple(9999);
CALL bug_19194_b(1); CALL bug_19194_searched(1);
CALL bug_19194_b(2); CALL bug_19194_searched(2);
CALL bug_19194_b(1000); CALL bug_19194_searched(1000);
CALL bug_19194_b(4998); CALL bug_19194_searched(4998);
CALL bug_19194_b(4999); CALL bug_19194_searched(4999);
CALL bug_19194_b(9999); CALL bug_19194_searched(9999);
DROP PROCEDURE bug_19194_a; DROP PROCEDURE proc_19194_codegen;
DROP PROCEDURE bug_19194_b; DROP PROCEDURE bug_19194_simple;
DROP PROCEDURE bug_19194_searched;
...@@ -100,6 +100,42 @@ void turn_parser_debug_on() ...@@ -100,6 +100,42 @@ void turn_parser_debug_on()
/** /**
Helper action for a case statement (entering the CASE). Helper action for a case statement (entering the CASE).
This helper is used for both 'simple' and 'searched' cases. 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 @param lex the parser lex context
*/ */
...@@ -110,6 +146,7 @@ void case_stmt_action_case(LEX *lex) ...@@ -110,6 +146,7 @@ void case_stmt_action_case(LEX *lex)
/* /*
BACKPATCH: Creating target label for the jump to BACKPATCH: Creating target label for the jump to
"case_stmt_action_end_case" "case_stmt_action_end_case"
(Instruction 12 in the example)
*/ */
lex->spcont->push_label((char *)"", lex->sphead->instructions()); lex->spcont->push_label((char *)"", lex->sphead->instructions());
...@@ -179,6 +216,7 @@ void case_stmt_action_when(LEX *lex, Item *when, bool simple) ...@@ -179,6 +216,7 @@ void case_stmt_action_when(LEX *lex, Item *when, bool simple)
/* /*
BACKPATCH: Registering forward jump from BACKPATCH: Registering forward jump from
"case_stmt_action_when" to "case_stmt_action_then" "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->push_backpatch(i, ctx->push_label((char *)"", 0));
...@@ -203,13 +241,15 @@ void case_stmt_action_then(LEX *lex) ...@@ -203,13 +241,15 @@ void case_stmt_action_then(LEX *lex)
/* /*
BACKPATCH: Resolving forward jump from BACKPATCH: Resolving forward jump from
"case_stmt_action_when" to "case_stmt_action_then" "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()); sp->backpatch(ctx->pop_label());
/* /*
BACKPATCH: Registering forward jump from 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()); sp->push_backpatch(i, ctx->last_label());
...@@ -227,6 +267,7 @@ void case_stmt_action_end_case(LEX *lex, bool simple) ...@@ -227,6 +267,7 @@ void case_stmt_action_end_case(LEX *lex, bool simple)
/* /*
BACKPATCH: Resolving forward jump from BACKPATCH: Resolving forward jump from
"case_stmt_action_then" 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)
*/ */
lex->sphead->backpatch(lex->spcont->pop_label()); lex->sphead->backpatch(lex->spcont->pop_label());
......
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