diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index b28192c2e73..e9a8dbf7f7d 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1456,6 +1456,7 @@ i 1 DEALLOCATE PREPARE stmt; DROP TABLE t1, t2; +DROP PROCEDURE IF EXISTS p1; End of 5.0 tests. create procedure proc_1() reset query cache; call proc_1(); diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 95916c726a4..0239be9b6d9 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1510,6 +1510,26 @@ DEALLOCATE PREPARE stmt; DROP TABLE t1, t2; +# +# BUG#21856: Prepared Statments: crash if bad create +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +let $iterations= 100; +--disable_query_log +--disable_result_log +while ($iterations > 0) +{ + --error ER_PARSE_ERROR + PREPARE stmt FROM "CREATE PROCEDURE p1()"; + dec $iterations; +} +--enable_query_log +--enable_result_log + + --echo End of 5.0 tests. # diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fc7ed7ff673..09bf1737e39 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6088,14 +6088,19 @@ void mysql_parse(THD *thd, char *inBuf, uint length) DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); - lex->unit.cleanup(); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ if (lex->sphead) { /* Clean up after failed stored procedure/function */ delete lex->sphead; lex->sphead= NULL; } + query_cache_abort(&thd->net); + lex->unit.cleanup(); } thd->proc_info="freeing items"; thd->end_statement(); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 63a129918d0..314d4ed5631 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2811,7 +2811,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); + + /* + The first thing we do after parse error is freeing sp_head to + ensure that we have restored original memroot. + */ + if (error && lex->sphead) + { + delete lex->sphead; + lex->sphead= NULL; + } + lex->safe_to_cache_query= FALSE; + /* While doing context analysis of the query (in check_prepared_statement) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -2837,6 +2849,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) if (error == 0) error= check_prepared_statement(this, name.str != 0); + /* Free sp_head if check_prepared_statement() failed. */ if (error && lex->sphead) { delete lex->sphead;