From f48b42e77657dd2e27380201631fd0f137863b85 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Wed, 8 Oct 2008 02:34:00 +0500 Subject: [PATCH 1/8] Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' Concurrent execution of 1) multitable update with a NATURAL/USING join and 2) a such query as "FLUSH TABLES WITH READ LOCK" or "ALTER TABLE" of updating table led to a server crash. The mysql_multi_update_prepare() function call is optimized to lock updating tables only, so it postpones locking to the last, and if locking fails, it does cleanup of modified syntax structures and repeats a query analysis. However, that cleanup procedure was incomplete for NATURAL/USING join syntax data: 1) some Field_item items pointed into freed table structures, and 2) the TABLE_LIST::join_columns fields was not reset. Major change: short-living Field *Natural_join_column::table_field has been replaced with long-living Item*. mysql-test/r/lock_multi.result: Added test case for bug #38691. mysql-test/t/lock_multi.test: Added test case for bug #38691. sql/item.cc: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' The Item_field constructor has been modified to allocate and copy original database/table/field names always (not during PS preparation/1st execution only), because an initialization of Item_field items with a pointer to short-living Field structures is a common practice. sql/sql_base.cc: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' 1) Type adjustment for Natural_join_column::table_field (Field to Item_field); 2) The setup_natural_join_row_types function has been updated to take into account new first_natural_join_processing flag to skip unnecessary reinitialization of Natural_join_column::join_columns during table reopening after lock_tables() failure (like the 'first_execution' flag for PS). sql/sql_lex.cc: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' Initialization of the new st_select_lex::first_natural_join_processing flag has been added. sql/sql_lex.h: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' The st_select_lex::first_natural_join_processing flag has been added to skip unnecessary rebuilding of NATURAL/USING JOIN structures during table reopening after lock_tables failure. sql/sql_update.cc: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' Extra cleanup calls have been added to reset Natural_join_column::table_field items. sql/table.cc: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' Type adjustment for Natural_join_column::table_field (Field to Item_field). sql/table.h: Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while ``FLUSH TABLES WITH READ LOCK'' Type of the Natural_join_column::table_field field has been changed from Field that points into short-living TABLE memory to long-living Item_field that can be linked to (fixed) reopened table. --- mysql-test/r/lock_multi.result | 16 +++++ mysql-test/t/lock_multi.test | 119 +++++++++++++++++++++++++++++++++ sql/item.cc | 10 +-- sql/sql_base.cc | 34 ++++++++-- sql/sql_lex.cc | 1 + sql/sql_lex.h | 1 + sql/sql_update.cc | 13 +++- sql/table.cc | 24 ++++--- sql/table.h | 7 +- 9 files changed, 201 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 079f92ca420..d46aea24ac3 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -99,3 +99,19 @@ kill query ERROR 70100: Query execution was interrupted unlock tables; drop table t1; +CREATE TABLE t1 ( +a int(11) unsigned default NULL, +b varchar(255) default NULL, +UNIQUE KEY a (a), +KEY b (b) +); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3); +CREATE TABLE t2 SELECT * FROM t1; +CREATE TABLE t3 SELECT * FROM t1; +# test altering of columns that multiupdate doesn't use +# normal mode +# PS mode +# test altering of columns that multiupdate uses +# normal mode +# PS mode +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 649c1a4efbd..fe22efeb8c2 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -281,4 +281,123 @@ unlock tables; connection default; drop table t1; +# +# Bug #38691: segfault/abort in ``UPDATE ...JOIN'' while +# ``FLUSH TABLES WITH READ LOCK'' +# + +--connection default +CREATE TABLE t1 ( + a int(11) unsigned default NULL, + b varchar(255) default NULL, + UNIQUE KEY a (a), + KEY b (b) +); + +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3); +CREATE TABLE t2 SELECT * FROM t1; +CREATE TABLE t3 SELECT * FROM t1; + +--echo # test altering of columns that multiupdate doesn't use + +--echo # normal mode + +--disable_query_log +let $i = 100; +while ($i) { +--dec $i + +--connection writer + send UPDATE t2 INNER JOIN (t1 JOIN t3 USING(a)) USING(a) + SET a = NULL WHERE t1.b <> t2.b; + +--connection locker + ALTER TABLE t2 ADD COLUMN (c INT); + ALTER TABLE t2 DROP COLUMN c; + +--connection writer +--reap +} + +--echo # PS mode + +--connection writer +PREPARE stmt FROM 'UPDATE t2 INNER JOIN (t1 JOIN t3 USING(a)) USING(a) + SET a = NULL WHERE t1.b <> t2.b'; + +let $i = 100; +while ($i) { +--dec $i + +--connection writer +--send EXECUTE stmt + +--connection locker + ALTER TABLE t2 ADD COLUMN (c INT); + ALTER TABLE t2 DROP COLUMN c; + +--connection writer +--reap +} +--enable_query_log + + +--echo # test altering of columns that multiupdate uses + +--echo # normal mode + +--connection default + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t2 ADD COLUMN a int(11) unsigned default NULL; + UPDATE t2 SET a=b; + +--connection writer +--send UPDATE t2 INNER JOIN (t1 JOIN t3 USING(a)) USING(a) SET a = NULL WHERE t1.b <> t2.b + +--connection locker +--error 0,1091 + ALTER TABLE t2 DROP COLUMN a; + +--connection writer +--error 0,1054 +--reap +} +--enable_query_log + +--echo # PS mode + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t2 ADD COLUMN a int(11) unsigned default NULL; + UPDATE t2 SET a=b; + +--connection writer + PREPARE stmt FROM 'UPDATE t2 INNER JOIN (t1 JOIN t3 USING(a)) USING(a) SET a = NULL WHERE t1.b <> t2.b'; +--send EXECUTE stmt + +--connection locker +--error 0,1091 + ALTER TABLE t2 DROP COLUMN a; + +--connection writer +--error 0,1054 +--reap + +} +--enable_query_log +--connection default +DROP TABLE t1, t2, t3; + # End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index f247f49293d..e49de88cea7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1758,14 +1758,16 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, We need to copy db_name, table_name and field_name because they must be allocated in the statement memory, not in table memory (the table structure can go away and pop up again between subsequent executions - of a prepared statement). + of a prepared statement or after the close_tables_for_reopen() call + in mysql_multi_update_prepare()). */ - if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) { if (db_name) orig_db_name= thd->strdup(db_name); - orig_table_name= thd->strdup(table_name); - orig_field_name= thd->strdup(field_name); + if (table_name) + orig_table_name= thd->strdup(table_name); + if (field_name) + orig_field_name= thd->strdup(field_name); /* We don't restore 'name' in cleanup because it's not changed during execution. Still we need it to point to persistent diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 533b0070fee..d969c837891 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3617,8 +3617,21 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, { /* This is a base table. */ DBUG_ASSERT(nj_col->view_field == NULL); - DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->table); - found_field= nj_col->table_field; + /* + This fix_fields is not necessary (initially this item is fixed by + the Item_field constructor; after reopen_tables the Item_func_eq + calls fix_fields on that item), it's just a check during table + reopening for columns that was dropped by the concurrent connection. + */ + if (!nj_col->table_field->fixed && + nj_col->table_field->fix_fields(thd, (Item **)&nj_col->table_field)) + { + DBUG_PRINT("info", ("column '%s' was dropped by the concurrent connection", + nj_col->table_field->name)); + DBUG_RETURN(NULL); + } + DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->field->table); + found_field= nj_col->table_field->field; update_field_dependencies(thd, found_field, nj_col->table_ref->table); } @@ -4450,7 +4463,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, const char *field_name_1; /* true if field_name_1 is a member of using_fields */ bool is_using_column_1; - if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1))) + if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1))) goto err; field_name_1= nj_col_1->name(); is_using_column_1= using_fields && @@ -4471,7 +4484,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { Natural_join_column *cur_nj_col_2; const char *cur_field_name_2; - if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2))) + if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, leaf_2))) goto err; cur_field_name_2= cur_nj_col_2->name(); DBUG_PRINT ("info", ("cur_field_name_2=%s.%s", @@ -4957,15 +4970,24 @@ static bool setup_natural_join_row_types(THD *thd, TABLE_LIST *left_neighbor; /* Table reference to the right of the current. */ TABLE_LIST *right_neighbor= NULL; + bool save_first_natural_join_processing= + context->select_lex->first_natural_join_processing; + + context->select_lex->first_natural_join_processing= FALSE; /* Note that tables in the list are in reversed order */ for (left_neighbor= table_ref_it++; left_neighbor ; ) { table_ref= left_neighbor; left_neighbor= table_ref_it++; - /* For stored procedures do not redo work if already done. */ - if (context->select_lex->first_execution) + /* + Do not redo work if already done: + 1) for stored procedures, + 2) for multitable update after lock failure and table reopening. + */ + if (save_first_natural_join_processing) { + context->select_lex->first_natural_join_processing= FALSE; if (store_top_level_join_columns(thd, table_ref, left_neighbor, right_neighbor)) return TRUE; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 0cf7c11b447..ba9c0e93134 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1205,6 +1205,7 @@ void st_select_lex::init_query() subquery_in_having= explicit_limit= 0; is_item_list_lookup= 0; first_execution= 1; + first_natural_join_processing= 1; first_cond_optimization= 1; parsing_place= NO_MATTER; exclude_from_table_unique_test= no_wrap_view_item= FALSE; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 563172594d2..9f020f4adc5 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -586,6 +586,7 @@ public: case of an error during prepare the PS is not created. */ bool first_execution; + bool first_natural_join_processing; bool first_cond_optimization; /* do not wrap view fields with Item_ref */ bool no_wrap_view_item; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7d47659fbcc..109786df7de 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -852,11 +852,14 @@ reopen_tables: } /* now lock and fill tables */ - if (lock_tables(thd, table_list, table_count, &need_reopen)) + if (!thd->stmt_arena->is_stmt_prepare() && + lock_tables(thd, table_list, table_count, &need_reopen)) { if (!need_reopen) DBUG_RETURN(TRUE); + DBUG_PRINT("info", ("lock_tables failed, reopening")); + /* We have to reopen tables since some of them were altered or dropped during lock_tables() or something was done with their triggers. @@ -872,6 +875,14 @@ reopen_tables: for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) tbl->cleanup_items(); + /* + Also we need to cleanup Natural_join_column::table_field items. + To not to traverse a join tree we will cleanup whole + thd->free_list (in PS execution mode that list may not contain + items from 'fields' list, so the cleanup above is necessary to. + */ + cleanup_items(thd->free_list); + close_tables_for_reopen(thd, &table_list); goto reopen_tables; } diff --git a/sql/table.cc b/sql/table.cc index 7fe9aa774f3..3abd2c24c94 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2191,7 +2191,7 @@ TABLE_LIST *TABLE_LIST::find_underlying_table(TABLE *table_to_find) } /* - cleunup items belonged to view fields translation table + cleanup items belonged to view fields translation table SYNOPSIS TABLE_LIST::cleanup_items() @@ -2637,10 +2637,10 @@ Natural_join_column::Natural_join_column(Field_translator *field_param, } -Natural_join_column::Natural_join_column(Field *field_param, +Natural_join_column::Natural_join_column(Item_field *field_param, TABLE_LIST *tab) { - DBUG_ASSERT(tab->table == field_param->table); + DBUG_ASSERT(tab->table == field_param->field->table); table_field= field_param; view_field= NULL; table_ref= tab; @@ -2668,7 +2668,7 @@ Item *Natural_join_column::create_item(THD *thd) return create_view_field(thd, table_ref, &view_field->item, view_field->name); } - return new Item_field(thd, &thd->lex->current_select->context, table_field); + return table_field; } @@ -2679,7 +2679,7 @@ Field *Natural_join_column::field() DBUG_ASSERT(table_field == NULL); return NULL; } - return table_field; + return table_field->field; } @@ -2811,7 +2811,7 @@ void Field_iterator_natural_join::next() cur_column_ref= column_ref_it++; DBUG_ASSERT(!cur_column_ref || ! cur_column_ref->table_field || cur_column_ref->table_ref->table == - cur_column_ref->table_field->table); + cur_column_ref->table_field->field->table); } @@ -2975,7 +2975,7 @@ GRANT_INFO *Field_iterator_table_ref::grant() */ Natural_join_column * -Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) +Field_iterator_table_ref::get_or_create_column_ref(THD *thd, TABLE_LIST *parent_table_ref) { Natural_join_column *nj_col; bool is_created= TRUE; @@ -2988,7 +2988,11 @@ Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) { /* The field belongs to a stored table. */ Field *tmp_field= table_field_it.field(); - nj_col= new Natural_join_column(tmp_field, table_ref); + Item_field *tmp_item= + new Item_field(thd, &thd->lex->current_select->context, tmp_field); + if (!tmp_item) + return NULL; + nj_col= new Natural_join_column(tmp_item, table_ref); field_count= table_ref->table->s->fields; } else if (field_it == &view_field_it) @@ -3012,7 +3016,7 @@ Field_iterator_table_ref::get_or_create_column_ref(TABLE_LIST *parent_table_ref) DBUG_ASSERT(nj_col); } DBUG_ASSERT(!nj_col->table_field || - nj_col->table_ref->table == nj_col->table_field->table); + nj_col->table_ref->table == nj_col->table_field->field->table); /* If the natural join column was just created add it to the list of @@ -3077,7 +3081,7 @@ Field_iterator_table_ref::get_natural_column_ref() nj_col= natural_join_it.column_ref(); DBUG_ASSERT(nj_col && (!nj_col->table_field || - nj_col->table_ref->table == nj_col->table_field->table)); + nj_col->table_ref->table == nj_col->table_field->field->table)); return nj_col; } diff --git a/sql/table.h b/sql/table.h index cff4be630e4..08326e2fbe0 100644 --- a/sql/table.h +++ b/sql/table.h @@ -18,6 +18,7 @@ class Item; /* Needed by ORDER */ class Item_subselect; +class Item_field; class GRANT_TABLE; class st_select_lex_unit; class st_select_lex; @@ -469,7 +470,7 @@ class Natural_join_column: public Sql_alloc { public: Field_translator *view_field; /* Column reference of merge view. */ - Field *table_field; /* Column reference of table or temp view. */ + Item_field *table_field; /* Column reference of table or temp view. */ TABLE_LIST *table_ref; /* Original base table/view reference. */ /* True if a common join column of two NATURAL/USING join operands. Notice @@ -481,7 +482,7 @@ public: bool is_common; public: Natural_join_column(Field_translator *field_param, TABLE_LIST *tab); - Natural_join_column(Field *field_param, TABLE_LIST *tab); + Natural_join_column(Item_field *field_param, TABLE_LIST *tab); const char *name(); Item *create_item(THD *thd); Field *field(); @@ -899,7 +900,7 @@ public: GRANT_INFO *grant(); Item *create_item(THD *thd) { return field_it->create_item(thd); } Field *field() { return field_it->field(); } - Natural_join_column *get_or_create_column_ref(TABLE_LIST *parent_table_ref); + Natural_join_column *get_or_create_column_ref(THD *thd, TABLE_LIST *parent_table_ref); Natural_join_column *get_natural_column_ref(); }; From a83f5b18efaa54df3b56c86c1ab9244280447f24 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Thu, 9 Oct 2008 20:24:31 +0500 Subject: [PATCH 2/8] Bug#38499: flush tables and multitable table update with derived table cause crash When a multi-UPDATE command fails to lock some table, and subsequently succeeds, the tables need to be reopened if they were altered. But the reopening procedure failed for derived tables. Extra cleanup has been added. mysql-test/r/lock_multi.result: Added test case for bug #38499. mysql-test/t/lock_multi.test: Added test case for bug #38499. sql/sql_union.cc: Bug#38499: flush tables and multitable table update with derived table cause crash Obsolete assertion has been removed. sql/sql_update.cc: Bug#38499: flush tables and multitable table update with derived table cause crash Extra cleanup for derived tables has been added: 1) unit.cleanup(), 2) unit->reinit_exec_mechanism(). --- mysql-test/r/lock_multi.result | 18 +++ mysql-test/t/lock_multi.test | 202 +++++++++++++++++++++++++++++++++ sql/sql_union.cc | 1 - sql/sql_update.cc | 20 ++++ 4 files changed, 240 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index d46aea24ac3..0430d560a7a 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -115,3 +115,21 @@ CREATE TABLE t3 SELECT * FROM t1; # normal mode # PS mode DROP TABLE t1, t2, t3; +CREATE TABLE t1( a INT, b INT ); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4); +# 1. test regular tables +# 1.1. test altering of columns that multiupdate doesn't use +# 1.1.1. normal mode +# 1.1.2. PS mode +# 1.2. test altering of columns that multiupdate uses +# 1.2.1. normal mode +# 1.2.2. PS mode +ALTER TABLE t1 ADD COLUMN a INT; +# 2. test UNIONs +# 2.1. test altering of columns that multiupdate doesn't use +# 2.1.1. normal mode +# 2.1.2. PS mode +# 2.2. test altering of columns that multiupdate uses +# 2.2.1. normal mode +# 2.2.2. PS mode +DROP TABLE t1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index fe22efeb8c2..089a60edb3d 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -400,4 +400,206 @@ while ($i) { --connection default DROP TABLE t1, t2, t3; +# +# Bug#38499: flush tables and multitable table update with derived table cause +# crash +# + +CREATE TABLE t1( a INT, b INT ); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3), (4, 4); + +--echo # 1. test regular tables +--echo # 1.1. test altering of columns that multiupdate doesn't use +--echo # 1.1.1. normal mode + +--disable_query_log +let $i = 100; +while ($i) { +--dec $i + +--connection writer + send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0; + +--connection locker + ALTER TABLE t1 ADD COLUMN (c INT); + ALTER TABLE t1 DROP COLUMN c; + +--connection writer +--reap +} + +--echo # 1.1.2. PS mode + +--connection writer +PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0'; + +let $i = 100; +while ($i) { +--dec $i + +--connection writer +--send EXECUTE stmt + +--connection locker + ALTER TABLE t1 ADD COLUMN (c INT); + ALTER TABLE t1 DROP COLUMN c; + +--connection writer +--reap +} +--enable_query_log + +--echo # 1.2. test altering of columns that multiupdate uses +--echo # 1.2.1. normal mode + +--connection default + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t1 ADD COLUMN a int(11) unsigned default NULL; + UPDATE t1 SET a=b; + +--connection writer +--send UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0; + +--connection locker +--error 0,1091 + ALTER TABLE t1 DROP COLUMN a; + +--connection writer +--error 0,1054 # unknown column error +--reap +} +--enable_query_log + +--echo # 1.2.2. PS mode + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t1 ADD COLUMN a INT; + UPDATE t1 SET a=b; + +--connection writer + PREPARE stmt FROM 'UPDATE t1, (SELECT 1 FROM t1 t1i) d SET a = 0 WHERE 1=0'; +--send EXECUTE stmt + +--connection locker +--error 0,1091 + ALTER TABLE t1 DROP COLUMN a; + +--connection writer +--error 0,1054 # Unknown column 'a' in 'field list' +--reap +} +--enable_query_log +--connection default +ALTER TABLE t1 ADD COLUMN a INT; + +--echo # 2. test UNIONs +--echo # 2.1. test altering of columns that multiupdate doesn't use +--echo # 2.1.1. normal mode + +--disable_query_log +let $i = 100; +while ($i) { +--dec $i + +--connection writer + send UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0; + +--connection locker + ALTER TABLE t1 ADD COLUMN (c INT); + ALTER TABLE t1 DROP COLUMN c; + +--connection writer +--reap +} + +--echo # 2.1.2. PS mode + +--connection writer +PREPARE stmt FROM 'UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0'; + +let $i = 100; +while ($i) { +--dec $i + +--connection writer +--send EXECUTE stmt + +--connection locker + ALTER TABLE t1 ADD COLUMN (c INT); + ALTER TABLE t1 DROP COLUMN c; + +--connection writer +--reap +} +--enable_query_log + +--echo # 2.2. test altering of columns that multiupdate uses +--echo # 2.2.1. normal mode + +--connection default + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t1 ADD COLUMN a int(11) unsigned default NULL; + UPDATE t1 SET a=b; + +--connection writer +--send UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0; + +--connection locker +--error 0,1091 + ALTER TABLE t1 DROP COLUMN a; + +--connection writer +--error 0,1054 # Unknown column 'a' in 'field list' +--reap +} +--enable_query_log + +--echo # 2.2.2. PS mode + +--disable_query_log +let $i = 100; +while ($i) { + dec $i; + +--connection locker +--error 0,1060 + ALTER TABLE t1 ADD COLUMN a INT; + UPDATE t1 SET a=b; + +--connection writer + PREPARE stmt FROM 'UPDATE t1, ((SELECT 1 FROM t1 t1i) UNION (SELECT 2 FROM t1 t1ii)) e SET a = 0 WHERE 1=0'; +--send EXECUTE stmt + +--connection locker +--error 0,1091 + ALTER TABLE t1 DROP COLUMN a; + +--connection writer +--error 0,1054 # Unknown column 'a' in 'field list' +--reap +} +--enable_query_log +--connection default +DROP TABLE t1; + # End of 5.0 tests diff --git a/sql/sql_union.cc b/sql/sql_union.cc index da5e118b069..2875aefbd97 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -399,7 +399,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } else { - DBUG_ASSERT(!thd->stmt_arena->is_conventional()); /* We're in execution of a prepared statement or stored procedure: reset field items to point at fields from the created temporary table. diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 109786df7de..eb4e9b7ed73 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -875,6 +875,26 @@ reopen_tables: for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global) tbl->cleanup_items(); + /* + To not to hog memory (as a result of the + unit->reinit_exec_mechanism() call below): + */ + lex->unit.cleanup(); + + for (SELECT_LEX *sl= lex->all_selects_list; + sl; + sl= sl->next_select_in_list()) + { + SELECT_LEX_UNIT *unit= sl->master_unit(); + unit->reinit_exec_mechanism(); // reset unit->prepared flags + /* + Reset 'clean' flag back to force normal execution of + unit->cleanup() in Prepared_statement::cleanup_stmt() + (call to lex->unit.cleanup() above sets this flag to TRUE). + */ + unit->unclean(); + } + /* Also we need to cleanup Natural_join_column::table_field items. To not to traverse a join tree we will cleanup whole From 8bfbcbd98172ac936ff13ef97f2662cd6bc9a2c7 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Fri, 10 Oct 2008 15:13:12 +0500 Subject: [PATCH 3/8] Bug #39283: Date returned as VARBINARY to client for queries with COALESCE and JOIN The server returned to a client the VARBINARY column type instead of the DATE type for a result of the COALESCE, IFNULL, IF, CASE, GREATEST or LEAST functions if that result was filesorted in an anonymous temporary table during the query execution. For example: SELECT COALESCE(t1.date1, t2.date2) AS result FROM t1 JOIN t2 ON t1.id = t2.id ORDER BY result; To create a column of various date/time types in a temporary table the create_tmp_field_from_item() function uses the Item::tmp_table_field_from_field_type() method call. However, fields of the MYSQL_TYPE_NEWDATE type were missed there, and the VARBINARY columns were created by default. Necessary condition has been added. mysql-test/r/metadata.result: Added test case for bug #39283. mysql-test/t/metadata.test: Added test case for bug #39283. sql/sql_select.cc: Bug #39283: Date returned as VARBINARY to client for queries with COALESCE and JOIN To create a column of various date/time types in a temporary table the create_tmp_field_from_item() function uses the Item::tmp_table_field_from_field_type() method call. However, fields of the MYSQL_TYPE_NEWDATE type were missed there, and the VARBINARY columns were created by default. Necessary condition has been added. --- mysql-test/r/metadata.result | 17 +++++++++++++++++ mysql-test/t/metadata.test | 17 +++++++++++++++++ sql/sql_select.cc | 1 + 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/metadata.result b/mysql-test/r/metadata.result index a4b6fd2ce8f..5aad5874ffc 100644 --- a/mysql-test/r/metadata.result +++ b/mysql-test/r/metadata.result @@ -181,4 +181,21 @@ c1 c2 3 3 DROP VIEW v1,v2; DROP TABLE t1,t2; +CREATE TABLE t1 (i INT, d DATE); +INSERT INTO t1 VALUES (1, '2008-01-01'), (2, '2008-01-02'), (3, '2008-01-03'); +SELECT COALESCE(d, d), IFNULL(d, d), IF(i, d, d), +CASE i WHEN i THEN d ELSE d END, GREATEST(d, d), LEAST(d, d) +FROM t1 ORDER BY RAND(); +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +2008-01-01 2008-01-01 2008-01-01 2008-01-01 2008-01-01 2008-01-01 +2008-01-02 2008-01-02 2008-01-02 2008-01-02 2008-01-02 2008-01-02 +2008-01-03 2008-01-03 2008-01-03 2008-01-03 2008-01-03 2008-01-03 +COALESCE(d, d) IFNULL(d, d) IF(i, d, d) CASE i WHEN i THEN d ELSE d END GREATEST(d, d) LEAST(d, d) +def CASE i WHEN i THEN d ELSE d END CASE i WHEN i THEN d ELSE d END 10 10 10 Y 128 0 63 +def COALESCE(d, d) COALESCE(d, d) 10 10 10 Y 128 0 63 +def GREATEST(d, d) GREATEST(d, d) 10 10 10 Y 128 0 63 +def IF(i, d, d) IF(i, d, d) 10 10 10 Y 128 0 63 +def IFNULL(d, d) IFNULL(d, d) 10 10 10 Y 128 0 63 +def LEAST(d, d) LEAST(d, d) 10 10 10 Y 128 0 63 +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/metadata.test b/mysql-test/t/metadata.test index 65c062399b7..a10767579fb 100644 --- a/mysql-test/t/metadata.test +++ b/mysql-test/t/metadata.test @@ -112,4 +112,21 @@ SELECT v1.c1, v2.c2 FROM v1 JOIN v2 ON c1=c2 GROUP BY v1.c1 ORDER BY v2.c2; DROP VIEW v1,v2; DROP TABLE t1,t2; +# +# Bug #39283: Date returned as VARBINARY to client for queries +# with COALESCE and JOIN +# + +CREATE TABLE t1 (i INT, d DATE); +INSERT INTO t1 VALUES (1, '2008-01-01'), (2, '2008-01-02'), (3, '2008-01-03'); + +--enable_metadata +--sorted_result +SELECT COALESCE(d, d), IFNULL(d, d), IF(i, d, d), + CASE i WHEN i THEN d ELSE d END, GREATEST(d, d), LEAST(d, d) + FROM t1 ORDER BY RAND(); # force filesort +--disable_metadata + +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6928effc1a5..7ff069f0996 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8964,6 +8964,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, */ if ((type= item->field_type()) == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATE || + type == MYSQL_TYPE_NEWDATE || type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_GEOMETRY) new_field= item->tmp_table_field_from_field_type(table); /* From 097b60bd06e8c9dd741df30389849a17971d5052 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Fri, 10 Oct 2008 15:27:58 +0500 Subject: [PATCH 4/8] Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 Select with a "NULL NOT IN" condition containing complex subselect from the same table as in the outer select failed with an assertion. The failure was caused by a concatenation of circumstances: 1) an inner select was optimized by make_join_statistics to use the QUICK_RANGE_SELECT access method (that implies an index scan of the table); 2) a subselect was independent (constant) from the outer select; 3) a condition was pushed down into inner select. During the evaluation of a constant IN expression an optimizer temporary changed the access method from index scan to table scan, but an engine handler was already initialized for index access by make_join_statistics. That caused an assertion. Unnecessary index initialization has been removed from the QUICK_RANGE_SELECT::init method (QUICK_RANGE_SELECT::reset reinvokes this initialization). mysql-test/r/subselect3.result: Added test case for bug #37894. mysql-test/t/subselect3.test: Added test case for bug #37894. sql/opt_range.cc: Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 Unnecessary index initialization has been removed from the QUICK_RANGE_SELECT::init method (QUICK_RANGE_SELECT::reset reinvokes this initialization). --- mysql-test/r/subselect3.result | 16 ++++++++++++++++ mysql-test/t/subselect3.test | 22 ++++++++++++++++++++++ sql/opt_range.cc | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 5221fa09744..97479418bec 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -779,4 +779,20 @@ SELECT 1 FROM t1 WHERE t1.a NOT IN (SELECT 1 FROM t1, t2 WHERE 0); 1 1 DROP TABLE t1, t2; +CREATE TABLE t1 ( +pk INT PRIMARY KEY, +int_key INT, +varchar_key VARCHAR(5) UNIQUE, +varchar_nokey VARCHAR(5) +); +INSERT INTO t1 VALUES (9, 7,NULL,NULL), (10,8,'p' ,'p'); +SELECT varchar_nokey +FROM t1 +WHERE NULL NOT IN ( +SELECT INNR.pk FROM t1 AS INNR2 +LEFT JOIN t1 AS INNR ON ( INNR2.int_key = INNR.int_key ) +WHERE INNR.varchar_key > 'n{' +); +varchar_nokey +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index d7bb1f7186a..7e9aa1554c0 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -618,4 +618,26 @@ SELECT 1 FROM t1 WHERE t1.a NOT IN (SELECT 1 FROM t1, t2 WHERE 0); DROP TABLE t1, t2; +# +# Bug #37894: Assertion in init_read_record_seq in handler.h line 1444 +# + +CREATE TABLE t1 ( + pk INT PRIMARY KEY, + int_key INT, + varchar_key VARCHAR(5) UNIQUE, + varchar_nokey VARCHAR(5) +); +INSERT INTO t1 VALUES (9, 7,NULL,NULL), (10,8,'p' ,'p'); + +SELECT varchar_nokey +FROM t1 +WHERE NULL NOT IN ( + SELECT INNR.pk FROM t1 AS INNR2 + LEFT JOIN t1 AS INNR ON ( INNR2.int_key = INNR.int_key ) + WHERE INNR.varchar_key > 'n{' +); + +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2ae86b876d2..204ebdb6f33 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1015,7 +1015,7 @@ int QUICK_RANGE_SELECT::init() if (file->inited != handler::NONE) file->ha_index_or_rnd_end(); - DBUG_RETURN(error= file->ha_index_init(index)); + DBUG_RETURN(FALSE); } From 017307f2163a068520374b3083895e246b7a4a60 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 14 Oct 2008 11:04:36 -0300 Subject: [PATCH 5/8] Bug#38823: Invalid memory access when a SP statement does wildcard expansion The problem is that field names constructed due to wild-card expansion done inside a stored procedure could point to freed memory if the expansion was performed after the first call to the stored procedure. The problem was solved by patch for Bug#38691. The solution was to allocate the database, table and field names in the in the statement memory instead of table memory. mysql-test/r/sp.result: Add test case result for Bug#38823 mysql-test/t/sp.test: Add test case for Bug#38823 sql/item.cc: Remark that this also impacts wildcard expansion inside SPs. --- mysql-test/r/sp.result | 13 +++++++++++++ mysql-test/t/sp.test | 22 ++++++++++++++++++++++ sql/item.cc | 3 ++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index e788d30a14b..dea51bb4c2c 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6672,6 +6672,19 @@ select substr(`str`, `pos`+ 1 ) into `str`; end $ call `p2`('s s s s s s'); drop procedure `p2`; +drop table if exists t1; +drop procedure if exists p1; +create procedure p1() begin select * from t1; end$ +call p1$ +ERROR 42S02: Table 'test.t1' doesn't exist +create table t1 (a integer)$ +call p1$ +a +alter table t1 add b integer; +call p1$ +a +drop table t1; +drop procedure p1; # ------------------------------------------------------------------ # -- End of 5.0 tests # ------------------------------------------------------------------ diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 21ca2528e4f..6d7a4b96167 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7836,6 +7836,28 @@ delimiter ;$ call `p2`('s s s s s s'); drop procedure `p2`; +# +# Bug#38823: Invalid memory access when a SP statement does wildcard expansion +# + +--disable_warnings +drop table if exists t1; +drop procedure if exists p1; +--enable_warnings + +delimiter $; +create procedure p1() begin select * from t1; end$ +--error ER_NO_SUCH_TABLE +call p1$ +create table t1 (a integer)$ +call p1$ +alter table t1 add b integer; +call p1$ +delimiter ;$ + +drop table t1; +drop procedure p1; + --echo # ------------------------------------------------------------------ --echo # -- End of 5.0 tests --echo # ------------------------------------------------------------------ diff --git a/sql/item.cc b/sql/item.cc index e49de88cea7..182f4abdfe6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1759,7 +1759,8 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, be allocated in the statement memory, not in table memory (the table structure can go away and pop up again between subsequent executions of a prepared statement or after the close_tables_for_reopen() call - in mysql_multi_update_prepare()). + in mysql_multi_update_prepare() or due to wildcard expansion in stored + procedures). */ { if (db_name) From 3ad228d7fba6fa2e5b98569f798583b8f8b90db9 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Wed, 15 Oct 2008 18:34:51 -0300 Subject: [PATCH 6/8] Bug#37075: offset of limit clause might be truncated on 32-bits server w/o big tables The problem is that the offset argument of the limit clause might be truncated on a 32-bits server built without big tables support. The truncation was happening because the original 64-bits long argument was being cast to a 32-bits (ha_rows) offset counter. The solution is to check if the conversing resulted in value truncation and if so, the offset is set to the maximum possible value that can fit on the type. mysql-test/r/limit.result: Add test case result for Bug#37075 mysql-test/t/limit.test: Add test case for Bug#37075 sql/sql_lex.cc: Check for truncation of the offset value. If value was truncated, set to the maximum possible value. --- mysql-test/r/limit.result | 3 +++ mysql-test/t/limit.test | 8 ++++++++ sql/sql_lex.cc | 22 ++++++++++++++++++---- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/limit.result b/mysql-test/r/limit.result index 2acf74162a4..caed588acdb 100644 --- a/mysql-test/r/limit.result +++ b/mysql-test/r/limit.result @@ -111,3 +111,6 @@ set @a=-14632475938453979136; execute s using @a, @a; ERROR HY000: Incorrect arguments to EXECUTE End of 5.0 tests +select 1 as a limit 4294967296,10; +a +End of 5.1 tests diff --git a/mysql-test/t/limit.test b/mysql-test/t/limit.test index 9cccca1adc3..5847b90367a 100644 --- a/mysql-test/t/limit.test +++ b/mysql-test/t/limit.test @@ -95,3 +95,11 @@ set @a=-14632475938453979136; execute s using @a, @a; --echo End of 5.0 tests + +# +# Bug#37075: offset of limit clause might be truncated to 0 on 32-bits server w/o big tables +# + +select 1 as a limit 4294967296,10; + +--echo End of 5.1 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ba9c0e93134..71aa80b8170 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2041,12 +2041,26 @@ st_lex::copy_db_to(char **p_db, uint *p_db_length) const void st_select_lex_unit::set_limit(SELECT_LEX *sl) { ha_rows select_limit_val; + ulonglong val; DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare()); - select_limit_val= (ha_rows)(sl->select_limit ? sl->select_limit->val_uint() : - HA_POS_ERROR); - offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : - ULL(0)); + val= sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR; + select_limit_val= (ha_rows)val; +#ifndef BIG_TABLES + /* + Check for overflow : ha_rows can be smaller then ulonglong if + BIG_TABLES is off. + */ + if (val != (ulonglong)select_limit_val) + select_limit_val= HA_POS_ERROR; +#endif + val= sl->offset_limit ? sl->offset_limit->val_uint() : ULL(0); + offset_limit_cnt= (ha_rows)val; +#ifndef BIG_TABLES + /* Check for truncation. */ + if (val != (ulonglong)offset_limit_cnt) + offset_limit_cnt= HA_POS_ERROR; +#endif select_limit_cnt= select_limit_val + offset_limit_cnt; if (select_limit_cnt < select_limit_val) select_limit_cnt= HA_POS_ERROR; // no limit From ca6e05765c7a404377dd84924a816e654b20618d Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 16 Oct 2008 14:16:27 +0300 Subject: [PATCH 7/8] Bug #39958: Test "windows" lacks a cleanup Added the missing DROP TABLE mysql-test/r/windows.result: Bug #39958: added the missing DROP TABLE mysql-test/t/windows.test: Bug #39958: added the missing DROP TABLE --- mysql-test/r/windows.result | 1 + mysql-test/t/windows.test | 1 + 2 files changed, 2 insertions(+) mode change 100644 => 100755 mysql-test/t/windows.test diff --git a/mysql-test/r/windows.result b/mysql-test/r/windows.result index b5f9a48d805..5a54db8bb84 100644 --- a/mysql-test/r/windows.result +++ b/mysql-test/r/windows.result @@ -18,4 +18,5 @@ EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +DROP TABLE t1; End of 5.0 tests. diff --git a/mysql-test/t/windows.test b/mysql-test/t/windows.test old mode 100644 new mode 100755 index 6976ee98750..6e6a7ec93a3 --- a/mysql-test/t/windows.test +++ b/mysql-test/t/windows.test @@ -33,5 +33,6 @@ drop table t1; # CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (1,1); EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); +DROP TABLE t1; --echo End of 5.0 tests. From b591793496f4eb4e9953383c09ce4033bd85850f Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Thu, 16 Oct 2008 21:37:17 +0500 Subject: [PATCH 8/8] Bug #39844: Query Crash Mysql Server 5.0.67 Server crashed during a sort order optimization of a dependent subquery: SELECT (SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) FROM t3; Bitmap of tables, that the reference to outer table column uses, in addition to the regular table bit has the OUTER_REF_TABLE_BIT bit set. The only_eq_ref_tables function traverses this map bit by bit simultaneously with join->map2table list. Obviously join->map2table never contains an entry for the OUTER_REF_TABLE_BIT pseudo-table, so the server crashed there. The only_eq_ref_tables function has been modified to traverse regular table bits only like the update_depend_map function (resetting of the OUTER_REF_TABLE_BIT there is enough, but resetting of the whole set of PSEUDO_TABLE_BITS is used there for sure). mysql-test/r/order_by.result: Added test case for bug #39844. mysql-test/t/order_by.test: Added test case for bug #39844. sql/sql_select.cc: Bug #39844: Query Crash Mysql Server 5.0.67 The only_eq_ref_tables function has been modified to traverse regular table bits only like the update_depend_map function (resetting of the OUTER_REF_TABLE_BIT there is enough, but resetting of the whole set of PSEUDO_TABLE_BITS is used there for sure). --- mysql-test/r/order_by.result | 16 ++++++++++++++++ mysql-test/t/order_by.test | 18 ++++++++++++++++++ sql/sql_select.cc | 1 + 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 9f6a1b3932c..f64bbc79cbd 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -1076,3 +1076,19 @@ set session max_sort_length= 2180; select * from t1 order by b; ERROR HY001: Out of sort memory; increase server sort buffer size drop table t1; +# +# Bug #39844: Query Crash Mysql Server 5.0.67 +# +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT PRIMARY KEY, b INT); +CREATE TABLE t3 (c INT); +INSERT INTO t1 (a) VALUES (1), (2); +INSERT INTO t2 (a,b) VALUES (1,2), (2,3); +INSERT INTO t3 (c) VALUES (1), (2); +SELECT +(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) +FROM t3; +(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) +2 +NULL +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 9a55c27df99..6d7ee1c1ca7 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -738,3 +738,21 @@ set session max_sort_length= 2180; select * from t1 order by b; drop table t1; + +--echo # +--echo # Bug #39844: Query Crash Mysql Server 5.0.67 +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT PRIMARY KEY, b INT); +CREATE TABLE t3 (c INT); + +INSERT INTO t1 (a) VALUES (1), (2); +INSERT INTO t2 (a,b) VALUES (1,2), (2,3); +INSERT INTO t3 (c) VALUES (1), (2); + +SELECT + (SELECT t1.a FROM t1, t2 WHERE t1.a = t2.b AND t2.a = t3.c ORDER BY t1.a) + FROM t3; + +DROP TABLE t1, t2, t3; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7ff069f0996..9723dd8c4e4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6574,6 +6574,7 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) { if (specialflag & SPECIAL_SAFE_MODE) return 0; // skip this optimize /* purecov: inspected */ + tables&= ~PSEUDO_TABLE_BITS; for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1) { if (tables & 1 && !eq_ref_table(join, order, *tab))