From a06170c8d816df4650847470dba46d199f1bba25 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 15 Oct 2013 10:34:46 +0400 Subject: [PATCH] MDEV-3798: EXPLAIN UPDATE/DELETE - Fix a problem with EXPLAIN multi_table UPDATE: = Do use multi_update object, because multi_update::prepare() does various setup, e.g. it disables index-only for the tables to be updated. = Protect multi_update::prepare() from being invoked multiple times. If the query has subqueries, they may try to invoke it, for some reason. --- mysql-test/r/explain_non_select.result | 2 +- sql/sql_class.h | 4 ++- sql/sql_update.cc | 44 ++++++++------------------ 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/mysql-test/r/explain_non_select.result b/mysql-test/r/explain_non_select.result index 42ae7bd4ebd..4cc36a642a5 100644 --- a/mysql-test/r/explain_non_select.result +++ b/mysql-test/r/explain_non_select.result @@ -100,7 +100,7 @@ id select_type table type possible_keys key key_len ref rows Extra explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where -1 SIMPLE t1 ref a a 5 test.t0.a 4 Using index +1 SIMPLE t1 ref a a 5 test.t0.a 4 drop table t0, t1; # # Try DELETE ... RETURNING ... diff --git a/sql/sql_class.h b/sql/sql_class.h index ba14af850be..9cb32eaa54a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4181,7 +4181,9 @@ class multi_update :public select_result_interceptor so that afterward send_error() needs to find out that. */ bool error_handled; - + + /* Need this to protect against multiple prepare() calls */ + bool prepared; public: multi_update(TABLE_LIST *ut, List *leaves_list, List *fields, List *values, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6a58b4e8029..786e058c99d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1464,32 +1464,14 @@ bool mysql_multi_update(THD *thd, multi_update **result) { bool res; - select_result *output; - bool explain= test(thd->lex->describe); DBUG_ENTER("mysql_multi_update"); - if (explain) + if (!(*result= new multi_update(table_list, + &thd->lex->select_lex.leaf_tables, + fields, values, + handle_duplicates, ignore))) { - /* Handle EXPLAIN UPDATE */ - if (!(output= new select_send()) || - thd->send_explain_fields(output)) - { - delete output; - DBUG_RETURN(TRUE); - } - select_lex->set_explain_type(FALSE); - *result= NULL; /* no multi_update object */ - } - else - { - if (!(*result= new multi_update(table_list, - &thd->lex->select_lex.leaf_tables, - fields, values, - handle_duplicates, ignore))) - { - DBUG_RETURN(TRUE); - } - output= *result; + DBUG_RETURN(TRUE); } thd->abort_on_warning= test(thd->variables.sql_mode & @@ -1504,7 +1486,7 @@ bool mysql_multi_update(THD *thd, (ORDER *)NULL, options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, - output, unit, select_lex); + *result, unit, select_lex); DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); res|= thd->is_error(); @@ -1512,12 +1494,8 @@ bool mysql_multi_update(THD *thd, (*result)->abort_result_set(); else { - if (explain) - { - thd->lex->explain->print_explain(output, thd->lex->describe); - output->send_eof(); - delete output; - } + if (thd->lex->describe) + res= thd->lex->explain->send_explain(thd); } thd->abort_on_warning= 0; DBUG_RETURN(res); @@ -1533,7 +1511,7 @@ multi_update::multi_update(TABLE_LIST *table_list, tmp_tables(0), updated(0), found(0), fields(field_list), values(value_list), table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), - transactional_tables(0), ignore(ignore_arg), error_handled(0) + transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0) {} @@ -1556,6 +1534,10 @@ int multi_update::prepare(List ¬_used_values, List_iterator ti(*leaves); DBUG_ENTER("multi_update::prepare"); + if (prepared) + DBUG_RETURN(0); + prepared= true; + thd->count_cuted_fields= CHECK_FIELD_WARN; thd->cuted_fields=0L; thd_proc_info(thd, "updating main table");