summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2016-09-09 08:40:24 +0400
committerAlexander Barkov <bar@mariadb.org>2016-09-09 08:40:24 +0400
commitde7f87708a156659e518bf4ccdb051e92a0a7521 (patch)
treee204b4265b02a61b6ad6660ecc8cd20dd9d58c2c
parent8494039757a2f6353cc161e7824aab4fe2312d2a (diff)
downloadmariadb-git-de7f87708a156659e518bf4ccdb051e92a0a7521.tar.gz
MDEV-10702 Crash in SET STATEMENT FOR EXECUTE
-rw-r--r--mysql-test/r/ps_ddl.result29
-rw-r--r--mysql-test/t/ps_ddl.test24
-rw-r--r--sql/sql_prepare.cc48
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