diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-09-09 08:40:24 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2016-09-09 08:40:24 +0400 |
commit | de7f87708a156659e518bf4ccdb051e92a0a7521 (patch) | |
tree | e204b4265b02a61b6ad6660ecc8cd20dd9d58c2c | |
parent | 8494039757a2f6353cc161e7824aab4fe2312d2a (diff) | |
download | mariadb-git-de7f87708a156659e518bf4ccdb051e92a0a7521.tar.gz |
MDEV-10702 Crash in SET STATEMENT FOR EXECUTE
-rw-r--r-- | mysql-test/r/ps_ddl.result | 29 | ||||
-rw-r--r-- | mysql-test/t/ps_ddl.test | 24 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 48 |
3 files changed, 92 insertions, 9 deletions
diff --git a/mysql-test/r/ps_ddl.result b/mysql-test/r/ps_ddl.result index dec0d12c455..fc1832260c5 100644 --- a/mysql-test/r/ps_ddl.result +++ b/mysql-test/r/ps_ddl.result @@ -2542,3 +2542,32 @@ EXECUTE stmt3; EXECUTE stmt3; DEALLOCATE PREPARE stmt3; DROP TEMPORARY TABLES tm, t1; +# +# Start of 10.1 tests +# +# +# MDEV-10702 Crash in SET STATEMENT FOR EXECUTE +# +CREATE TABLE t1 (a INT); +PREPARE stmt FROM 'INSERT INTO t1 VALUES (@@max_sort_length)'; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +a +2048 +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=NEW.a + 1; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +a +2048 +1025 +DROP TRIGGER tr1; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +a +2048 +1025 +1024 +DROP TABLE t1; +# +# End of 10.1 tests +# diff --git a/mysql-test/t/ps_ddl.test b/mysql-test/t/ps_ddl.test index 21355ca42b7..90226d379bf 100644 --- a/mysql-test/t/ps_ddl.test +++ b/mysql-test/t/ps_ddl.test @@ -2259,3 +2259,27 @@ EXECUTE stmt3; EXECUTE stmt3; DEALLOCATE PREPARE stmt3; DROP TEMPORARY TABLES tm, t1; + +--echo # +--echo # Start of 10.1 tests +--echo # + +--echo # +--echo # MDEV-10702 Crash in SET STATEMENT FOR EXECUTE +--echo # +CREATE TABLE t1 (a INT); +PREPARE stmt FROM 'INSERT INTO t1 VALUES (@@max_sort_length)'; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET NEW.a=NEW.a + 1; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +DROP TRIGGER tr1; +SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; +SELECT * FROM t1; +DROP TABLE t1; + + +--echo # +--echo # End of 10.1 tests +--echo # diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 0db1daa378e..d381825851d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3030,7 +3030,36 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt)); + /* + thd->free_list can already have some Items, + e.g. for a query like this: + PREPARE stmt FROM 'INSERT INTO t1 VALUES (@@max_sort_length)'; + SET STATEMENT max_sort_length=2048 FOR EXECUTE stmt; + thd->free_list contains a pointer to Item_int corresponding to 2048. + + If Prepared_statement::execute() notices that the table metadata for "t1" + has changed since PREPARE, it returns an error asking the calling + Prepared_statement::execute_loop() to re-prepare the statement. + Before returning the error, Prepared_statement::execute() + calls Prepared_statement::cleanup_stmt(), + which calls thd->cleanup_after_query(), + which calls Query_arena::free_items(). + + We hide "external" Items, e.g. those created while parsing the + "SET STATEMENT" part of the query, + so they don't get freed in case of re-prepare. + See MDEV-10702 Crash in SET STATEMENT FOR EXECUTE + */ + Item *free_list_backup= thd->free_list; + thd->free_list= NULL; // Hide the external (e.g. "SET STATEMENT") Items (void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL); + thd->free_items(); // Free items created by execute_loop() + /* + Now restore the "external" (e.g. "SET STATEMENT") Item list. + It will be freed normaly in THD::cleanup_after_query(). + */ + thd->free_list= free_list_backup; + stmt->lex->restore_set_statement_var(); DBUG_VOID_RETURN; } @@ -3853,9 +3882,14 @@ Prepared_statement::execute_loop(String *expanded_query, Reprepare_observer reprepare_observer; bool error; int reprepare_attempt= 0; -#ifndef DBUG_OFF - Item *free_list_state= thd->free_list; -#endif + + /* + - In mysql_sql_stmt_execute() we hide all "external" Items + e.g. those created in the "SET STATEMENT" part of the "EXECUTE" query. + - In case of mysqld_stmt_execute() there should not be "external" Items. + */ + DBUG_ASSERT(thd->free_list == NULL); + thd->select_number= select_number_after_prepare; /* Check if we got an error when sending long data */ if (state == Query_arena::STMT_ERROR) @@ -3877,12 +3911,8 @@ Prepared_statement::execute_loop(String *expanded_query, #endif reexecute: - /* - If the free_list is not empty, we'll wrongly free some externally - allocated items when cleaning up after validation of the prepared - statement. - */ - DBUG_ASSERT(thd->free_list == free_list_state); + // Make sure that reprepare() did not create any new Items. + DBUG_ASSERT(thd->free_list == NULL); /* Install the metadata observer. If some metadata version is |