From 6e668b4f5b44da5ed905b01800f4150956d96ee2 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 28 Sep 2007 18:25:47 -0300 Subject: [PATCH] Bug#21136 CREATE TABLE SELECT within CREATE TABLE SELECT causes server crash When CREATE TEMPORARY TABLE .. SELECT is invoked from a stored function which in turn is called from CREATE TABLE SELECT causes a memory leak because the inner create temporary table overrides the outter extra_lock reference when locking the table. The solution is to simply not overrride the extra_lock by only using the extra_lock for a non-temporary table lock. mysql-test/r/create.result: Add test case result for Bug#21136 mysql-test/t/create.test: Add test case for Bug#21136 sql/sql_insert.cc: For temporary tables, store the lock data within the select_create class since tmp tables contents are not replicated. For "real" tables, store the lock data in the thread extra_lock pointer. --- mysql-test/r/create.result | 14 ++++++++++++++ mysql-test/t/create.test | 25 +++++++++++++++++++++++++ sql/sql_insert.cc | 27 ++++++++++++++++++++------- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 82bcc68f319..800a3f9cece 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -1691,4 +1691,18 @@ ERROR 42000: Identifier name 'очень_очень_очень_очень_оче drop view имя_вью_кодировке_утф8_длиной_больше_чем_42; drop table имя_таблицы_в_кодировке_утф8_длиной_больше_чем_48; set names default; +drop table if exists t1,t2,t3; +drop function if exists f1; +create function f1() returns int +begin +declare res int; +create temporary table t3 select 1 i; +set res:= (select count(*) from t1); +drop temporary table t3; +return res; +end| +create table t1 as select 1; +create table t2 as select f1() from t1; +drop table t1,t2; +drop function f1; End of 5.1 tests diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index d4feeebe4b1..023e55ea418 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -1303,4 +1303,29 @@ return 0; drop view имя_вью_кодировке_утф8_длиной_больше_чем_42; drop table имя_таблицы_в_кодировке_утф8_длиной_больше_чем_48; set names default; + +# +# Bug#21136 CREATE TABLE SELECT within CREATE TABLE SELECT causes server crash +# + +--disable_warnings +drop table if exists t1,t2,t3; +drop function if exists f1; +--enable_warnings + +--delimiter | +create function f1() returns int +begin + declare res int; + create temporary table t3 select 1 i; + set res:= (select count(*) from t1); + drop temporary table t3; + return res; +end| +--delimiter ; +create table t1 as select 1; +create table t2 as select f1() from t1; +drop table t1,t2; +drop function f1; + --echo End of 5.1 tests diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7679401b459..fedda29c249 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3427,6 +3427,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, int select_create::prepare(List &values, SELECT_LEX_UNIT *u) { + MYSQL_LOCK *extra_lock= NULL; DBUG_ENTER("select_create::prepare"); TABLEOP_HOOKS *hook_ptr= NULL; @@ -3496,9 +3497,21 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) if (!(table= create_table_from_items(thd, create_info, create_table, alter_info, &values, - &thd->extra_lock, hook_ptr))) + &extra_lock, hook_ptr))) DBUG_RETURN(-1); // abort() deletes table + if (extra_lock) + { + DBUG_ASSERT(m_plock == NULL); + + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + m_plock= &m_lock; + else + m_plock= &thd->extra_lock; + + *m_plock= extra_lock; + } + if (table->s->fields < values.elements) { my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1); @@ -3637,10 +3650,10 @@ bool select_create::send_eof() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); - if (thd->extra_lock) + if (m_plock) { - mysql_unlock_tables(thd, thd->extra_lock); - thd->extra_lock=0; + mysql_unlock_tables(thd, *m_plock); + m_plock= 0; } } return tmp; @@ -3675,10 +3688,10 @@ void select_create::abort() if (thd->current_stmt_binlog_row_based) ha_rollback_stmt(thd); - if (thd->extra_lock) + if (m_plock) { - mysql_unlock_tables(thd, thd->extra_lock); - thd->extra_lock=0; + mysql_unlock_tables(thd, *m_plock); + m_plock= 0; } if (table)