From 18fec5128b6fd9712f63e306f03f16833f2599b2 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 12 Feb 2013 08:20:14 +0400 Subject: EXPLAIN DELETE for MariaDB - Backported the code to 10.0-base - Removed incorrect assert --- sql/sql_delete.cc | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 206 insertions(+), 15 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 4bcb62ef764..d7a612f3d56 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -40,6 +40,129 @@ #include "records.h" // init_read_record, #include "sql_derived.h" // mysql_handle_list_of_derived // end_read_record + + +/* + @brief + Print query plan of a single-table DELETE command + + @detail + This function is used by EXPLAIN DELETE and by SHOW EXPLAIN when it is + invoked on a running DELETE statement. +*/ + +int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, + bool *printed_anything) +{ + if (deleting_all_rows || impossible_where) + { + const char *msg= deleting_all_rows? "Deleting all rows": "Impossible where"; + if (print_explain_message_line(output, explain_flags, 1/*select number*/, + "SIMPLE", msg)) + { + return 1; + } + *printed_anything= true; + return 0; + } + + select_lex->set_explain_type(FALSE); + /* + Print an EXPLAIN line. We dont have join, so we can't directly use + JOIN::print_explain. + We do have a SELECT_LEX (TODO but how is it useful? it has select_type.. + and that's it?) + */ + + enum join_type jtype; + if (select && select->quick) + { + int quick_type= select->quick->get_type(); + if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) || + (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || + (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || + (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) + jtype= JT_INDEX_MERGE; + else + jtype= JT_RANGE; + } + else + { + if (index == MAX_KEY) + jtype= JT_ALL; + else + jtype= JT_NEXT; + } + + StringBuffer<128> possible_keys_line; + make_possible_keys_line(table, possible_keys, &possible_keys_line); + + const char *key_name; + const char *key_len; + + StringBuffer<128> key_str; + StringBuffer<128> key_len_str; + StringBuffer<128> extra_str; + + /* Calculate key_len */ + if (select && select->quick) + { + select->quick->add_keys_and_lengths(&key_str, &key_len_str); + key_name= key_str.c_ptr(); + key_len= key_len_str.c_ptr(); + } + else + { + key_name= (index == MAX_KEY)? NULL : table->key_info[index].name; + key_len= NULL; + } + + if (select && select->cond) + extra_str.append(STRING_WITH_LEN("Using where")); + if (select && select->quick && + select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) + { + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &extra_str); + } + + if (using_filesort) + { + if (extra_str.length() !=0) + extra_str.append(STRING_WITH_LEN("; ")); + extra_str.append(STRING_WITH_LEN("Using filesort")); + } + + /* + Single-table DELETE commands do not do "Using temporary". + "Using index condition" is also not possible (which is an unjustified limitation) + */ + + print_explain_row(output, explain_flags, + 1, /* id */ + select_lex->type, + table->pos_in_table_list->alias, + // partitions, + jtype, + possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, + key_name, + key_len, + NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ + select ? select->records : table_rows, + extra_str.c_ptr()); + + *printed_anything= true; + + for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (unit->print_explain(output, explain_flags, printed_anything)) + return 1; + } + return 0; +} + + /** Implement DELETE SQL word. @@ -61,12 +184,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool const_cond_result; ha_rows deleted= 0; bool reverse= FALSE; + bool err= true; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); - uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; + + Delete_plan query_plan; + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list, TRUE, 0)) @@ -90,6 +217,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } thd_proc_info(thd, "init"); table->map=1; + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -163,6 +292,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); ha_rows const maybe_deleted= table->file->stats.records; DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); + + query_plan.set_delete_all_rows(maybe_deleted); + if (thd->lex->describe) + goto exit_without_my_ok; + if (!(error=table->file->ha_delete_all_rows())) { /* @@ -187,7 +321,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, Item::cond_result result; conds= remove_eq_conds(thd, conds, &result); if (result == Item::COND_FALSE) // Impossible where + { limit= 0; + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + } } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -195,6 +334,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { free_underlaid_joins(thd, select_lex); // No matching record + //psergey-explain-todo: No-partitions used EXPLAIN here.. my_ok(thd, 0); DBUG_RETURN(0); } @@ -211,6 +351,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) { + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + delete select; free_underlaid_joins(thd, select_lex); /* @@ -243,26 +387,46 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (order) { - uint length= 0; - SORT_FIELD *sortorder; - ha_rows examined_rows; - ha_rows found_rows; - table->update_const_key_parts(conds); order= simple_remove_const(order, conds); - bool need_sort; if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered") - need_sort= FALSE; - usable_index= MAX_KEY; + query_plan.using_filesort= FALSE; + query_plan.index= MAX_KEY; } else - usable_index= get_index_for_order(order, table, select, limit, - &need_sort, &reverse); - if (need_sort) + query_plan.index= get_index_for_order(order, table, select, limit, + &query_plan.using_filesort, + &reverse); + } + + query_plan.select= select; + query_plan.possible_keys= table->quick_keys; + query_plan.table_rows= table->file->stats.records; + thd->lex->delete_plan= &query_plan; + + /* + Ok, we have generated a query plan for the DELETE. + - if we're running EXPLAIN DELETE, goto produce explain output + - otherwise, execute the query plan + */ + if (thd->lex->describe) + goto exit_without_my_ok; + + thd->apc_target.enable(); + DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", + dbug_serve_apcs(thd, 1);); + + if (query_plan.using_filesort) + { + ha_rows examined_rows; + ha_rows found_rows; + uint length= 0; + SORT_FIELD *sortorder; + { - DBUG_ASSERT(usable_index == MAX_KEY); + DBUG_ASSERT(query_plan.index == MAX_KEY); table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL | MY_THREAD_SPECIFIC)); @@ -276,6 +440,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { delete select; free_underlaid_joins(thd, &thd->lex->select_lex); + thd->apc_target.disable(); DBUG_RETURN(TRUE); } thd->examined_row_count+= examined_rows; @@ -294,19 +459,21 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { delete select; free_underlaid_joins(thd, select_lex); + thd->apc_target.disable(); DBUG_RETURN(TRUE); } - if (usable_index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 1, 1, FALSE)) { delete select; free_underlaid_joins(thd, select_lex); + thd->apc_target.disable(); DBUG_RETURN(TRUE); } } else - init_read_record_idx(&info, thd, table, 1, usable_index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); init_ftfuncs(thd, select_lex, 1); thd_proc_info(thd, "updating"); @@ -398,6 +565,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); + thd->apc_target.disable(); cleanup: /* Invalidate the table in the query cache if something changed. This must @@ -458,6 +626,29 @@ cleanup: DBUG_PRINT("info",("%ld records deleted",(long) deleted)); } DBUG_RETURN(error >= 0 || thd->is_error()); + + /* Special exits */ +exit_without_my_ok: + thd->lex->delete_plan= &query_plan; + + select_send *result; + bool printed_anything; + if (!(result= new select_send())) + return 1; /* purecov: inspected */ + List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ + result->prepare(dummy, &thd->lex->unit); + thd->send_explain_fields(result); + int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything); + + if (err2) + result->abort_result_set(); + else + result->send_eof(); + + delete select; + free_underlaid_joins(thd, select_lex); + //table->set_keyread(false); + DBUG_RETURN((err || thd->is_error() || thd->killed) ? 1 : 0); } -- cgit v1.2.1 From d2995031d9214206689660069024525808c8a683 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 12 Feb 2013 14:37:08 +0400 Subject: SHOW EXPLAIN for MariaDB - Support [SHOW] EXPLAIN UPDATE (needs code cleanup). --- sql/sql_delete.cc | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d7a612f3d56..df659871a64 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -54,9 +54,27 @@ int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, bool *printed_anything) { - if (deleting_all_rows || impossible_where) + if (deleting_all_rows) { - const char *msg= deleting_all_rows? "Deleting all rows": "Impossible where"; + const char *msg= "Deleting all rows"; + if (print_explain_message_line(output, explain_flags, 1/*select number*/, + "SIMPLE", msg)) + { + return 1; + } + *printed_anything= true; + return 0; + } + return Update_plan::print_explain(output, explain_flags, printed_anything); +} + + +int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, + bool *printed_anything) +{ + if (impossible_where) + { + const char *msg= "Impossible where"; if (print_explain_message_line(output, explain_flags, 1/*select number*/, "SIMPLE", msg)) { @@ -404,7 +422,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.select= select; query_plan.possible_keys= table->quick_keys; query_plan.table_rows= table->file->stats.records; - thd->lex->delete_plan= &query_plan; + thd->lex->upd_del_plan= &query_plan; /* Ok, we have generated a query plan for the DELETE. @@ -629,7 +647,7 @@ cleanup: /* Special exits */ exit_without_my_ok: - thd->lex->delete_plan= &query_plan; + thd->lex->upd_del_plan= &query_plan; select_send *result; bool printed_anything; -- cgit v1.2.1 From 03691a77718c781469b5675c6b03d6064255debb Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 17 Jun 2013 11:59:38 +0400 Subject: SHOW EXPLAIN UPDATE/DELETE - Introduce "Query Plan Footprints" (abbrev. QPFs) QPF is a part of query plan that is 1. sufficient to produce EXPLAIN output, 2. can be used to produce EXPLAIN output even after its subquery/union was executed and deleted 3. is cheap to save so that we can always save query plans - This patch doesn't fully address #2, we make/save strings for a number of EXPLAIN's columns. This will be fixed. --- sql/sql_delete.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index df659871a64..390d9e3945a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -169,7 +169,9 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, extra_str.c_ptr()); *printed_anything= true; - + /* + psergey-todo: handle all this through saving QPF. + for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit()) @@ -177,6 +179,7 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, if (unit->print_explain(output, explain_flags, printed_anything)) return 1; } + */ return 0; } -- cgit v1.2.1 From 1951d40a88e3b3fc0d2fac2ba3358d887e738485 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 18 Jun 2013 21:08:34 +0400 Subject: [SHOW] EXPLAIN UPDATE/DELETE, code re-structuring - Make EXPLAIN UPDATE/DELETE use "Query Plan Footprints", too. --- sql/sql_delete.cc | 152 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 73 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 390d9e3945a..f6719d3a91b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,8 +51,7 @@ invoked on a running DELETE statement. */ -int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything) +int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags) { if (deleting_all_rows) { @@ -62,37 +61,15 @@ int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags, { return 1; } - *printed_anything= true; return 0; } - return Update_plan::print_explain(output, explain_flags, printed_anything); + return Update_plan::print_explain(output, explain_flags); } - -int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, - bool *printed_anything) +void Update_plan::save_query_plan_footprint() { - if (impossible_where) - { - const char *msg= "Impossible where"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - *printed_anything= true; - return 0; - } + select_lex->set_explain_type(TRUE); - select_lex->set_explain_type(FALSE); - /* - Print an EXPLAIN line. We dont have join, so we can't directly use - JOIN::print_explain. - We do have a SELECT_LEX (TODO but how is it useful? it has select_type.. - and that's it?) - */ - - enum join_type jtype; if (select && select->quick) { int quick_type= select->quick->get_type(); @@ -112,35 +89,53 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, jtype= JT_NEXT; } - StringBuffer<128> possible_keys_line; + using_where= test(select && select->cond); + //using_filesort is already set make_possible_keys_line(table, possible_keys, &possible_keys_line); - const char *key_name; - const char *key_len; - - StringBuffer<128> key_str; - StringBuffer<128> key_len_str; - StringBuffer<128> extra_str; - /* Calculate key_len */ if (select && select->quick) { select->quick->add_keys_and_lengths(&key_str, &key_len_str); - key_name= key_str.c_ptr(); - key_len= key_len_str.c_ptr(); } else { - key_name= (index == MAX_KEY)? NULL : table->key_info[index].name; - key_len= NULL; + if (index != MAX_KEY) + { + key_str.append(table->key_info[index].name); + } + // key_len stays NULL } - - if (select && select->cond) - extra_str.append(STRING_WITH_LEN("Using where")); + if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { - explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &extra_str); + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &mrr_type); + } +} + +int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags) +{ + StringBuffer<64> extra_str; + if (impossible_where) + { + const char *msg= "Impossible where"; + if (print_explain_message_line(output, explain_flags, 1/*select number*/, + "SIMPLE", msg)) + { + return 1; + } + return 0; + } + + if (using_where) + extra_str.append(STRING_WITH_LEN("Using where")); + + if (mrr_type.length() != 0) + { + if (extra_str.length() !=0) + extra_str.append(STRING_WITH_LEN("; ")); + extra_str.append(mrr_type); } if (using_filesort) @@ -160,15 +155,14 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, select_lex->type, table->pos_in_table_list->alias, // partitions, - jtype, + (enum join_type) jtype, possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, - key_name, - key_len, + key_str.length()? key_str.c_ptr() : NULL, + key_len_str.length() ? key_len_str.c_ptr() : NULL, NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ select ? select->records : table_rows, extra_str.c_ptr()); - *printed_anything= true; /* psergey-todo: handle all this through saving QPF. @@ -211,10 +205,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - - Delete_plan query_plan; - query_plan.index= MAX_KEY; - query_plan.using_filesort= FALSE; + + Delete_plan *query_plan = new Delete_plan; + query_plan->index= MAX_KEY; + query_plan->using_filesort= FALSE; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list, TRUE, 0)) @@ -238,8 +232,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } thd_proc_info(thd, "init"); table->map=1; - query_plan.select_lex= &thd->lex->select_lex; - query_plan.table= table; + query_plan->select_lex= &thd->lex->select_lex; + query_plan->table= table; if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -314,7 +308,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows const maybe_deleted= table->file->stats.records; DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); - query_plan.set_delete_all_rows(maybe_deleted); + query_plan->set_delete_all_rows(maybe_deleted); if (thd->lex->describe) goto exit_without_my_ok; @@ -344,7 +338,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (result == Item::COND_FALSE) // Impossible where { limit= 0; - query_plan.set_impossible_where(); + query_plan->set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; } @@ -372,7 +366,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) { - query_plan.set_impossible_where(); + query_plan->set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; @@ -413,20 +407,22 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered") - query_plan.using_filesort= FALSE; - query_plan.index= MAX_KEY; + query_plan->using_filesort= FALSE; + query_plan->index= MAX_KEY; } else - query_plan.index= get_index_for_order(order, table, select, limit, - &query_plan.using_filesort, - &reverse); + query_plan->index= get_index_for_order(order, table, select, limit, + &query_plan->using_filesort, + &reverse); } - query_plan.select= select; - query_plan.possible_keys= table->quick_keys; - query_plan.table_rows= table->file->stats.records; - thd->lex->upd_del_plan= &query_plan; + query_plan->select= select; + query_plan->possible_keys= table->quick_keys; + query_plan->table_rows= table->file->stats.records; + thd->lex->query_plan_footprint= new QPF_query; + thd->lex->query_plan_footprint->upd_del_plan= query_plan; + /* Ok, we have generated a query plan for the DELETE. - if we're running EXPLAIN DELETE, goto produce explain output @@ -435,11 +431,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto exit_without_my_ok; + query_plan->save_query_plan_footprint(); thd->apc_target.enable(); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", dbug_serve_apcs(thd, 1);); - if (query_plan.using_filesort) + if (query_plan->using_filesort) { ha_rows examined_rows; ha_rows found_rows; @@ -447,7 +444,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SORT_FIELD *sortorder; { - DBUG_ASSERT(query_plan.index == MAX_KEY); + DBUG_ASSERT(query_plan->index == MAX_KEY); table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL | MY_THREAD_SPECIFIC)); @@ -483,7 +480,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, thd->apc_target.disable(); DBUG_RETURN(TRUE); } - if (query_plan.index == MAX_KEY || (select && select->quick)) + if (query_plan->index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 1, 1, FALSE)) { @@ -494,7 +491,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } else - init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan->index, reverse); init_ftfuncs(thd, select_lex, 1); thd_proc_info(thd, "updating"); @@ -587,6 +584,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, (void) table->file->extra(HA_EXTRA_NORMAL); thd->apc_target.disable(); + if (thd->lex->query_plan_footprint) + { + delete thd->lex->query_plan_footprint; + thd->lex->query_plan_footprint= NULL; + } cleanup: /* Invalidate the table in the query cache if something changed. This must @@ -650,21 +652,25 @@ cleanup: /* Special exits */ exit_without_my_ok: - thd->lex->upd_del_plan= &query_plan; - + query_plan->save_query_plan_footprint(); + thd->lex->query_plan_footprint->upd_del_plan= query_plan; + + select_send *result; - bool printed_anything; if (!(result= new select_send())) return 1; /* purecov: inspected */ List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ result->prepare(dummy, &thd->lex->unit); thd->send_explain_fields(result); - int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything); + int err2= thd->lex->query_plan_footprint->print_explain(result, 0); if (err2) result->abort_result_set(); else result->send_eof(); + + delete thd->lex->query_plan_footprint; + thd->lex->query_plan_footprint= NULL; delete select; free_underlaid_joins(thd, select_lex); -- cgit v1.2.1 From 0a560289aaa8c17e3f1930871088f43efce641e8 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 20 Jun 2013 15:15:24 +0400 Subject: [SHOW] EXPLAIN UPDATE/DELETE, code re-structuring - Introduce back QueryPlan/QueryPlanFootprint separation for single-table UPDATEs/DELETEs - Create an empty QueryPlanFootprint for all kinds of queries --- sql/sql_delete.cc | 185 +++++++++++++++++++----------------------------------- 1 file changed, 66 insertions(+), 119 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f6719d3a91b..602831829d7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,25 +51,44 @@ invoked on a running DELETE statement. */ -int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags) +void Delete_plan::save_query_plan_footprint(QPF_query *query) { + QPF_delete* qpf= new QPF_delete; + if (deleting_all_rows) { - const char *msg= "Deleting all rows"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - return 0; + qpf->deleting_all_rows= true; } - return Update_plan::print_explain(output, explain_flags); + else + { + Update_plan::save_query_plan_footprint_intern(qpf); + } + + query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint() + +void Update_plan::save_query_plan_footprint(QPF_query *query) { - select_lex->set_explain_type(TRUE); + QPF_update* qpf= new QPF_update; + save_query_plan_footprint_intern(qpf); + query->upd_del_plan= qpf; +} + +void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) +{ + qpf->table_name.append(table->pos_in_table_list->alias); + if (impossible_where) + { + qpf->impossible_where= true; + return; + } + + // TODO: do we need the following: select_type + //select_lex->set_explain_type(TRUE); + + /* Set jtype */ if (select && select->quick) { int quick_type= select->quick->get_type(); @@ -77,106 +96,46 @@ void Update_plan::save_query_plan_footprint() (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) - jtype= JT_INDEX_MERGE; + qpf->jtype= JT_INDEX_MERGE; else - jtype= JT_RANGE; + qpf->jtype= JT_RANGE; } else { if (index == MAX_KEY) - jtype= JT_ALL; + qpf->jtype= JT_ALL; else - jtype= JT_NEXT; + qpf->jtype= JT_NEXT; } - using_where= test(select && select->cond); + qpf->using_where= test(select && select->cond); + qpf->using_filesort= using_filesort; + //using_filesort is already set - make_possible_keys_line(table, possible_keys, &possible_keys_line); + make_possible_keys_line(table, possible_keys, &qpf->possible_keys_line); /* Calculate key_len */ if (select && select->quick) { - select->quick->add_keys_and_lengths(&key_str, &key_len_str); + select->quick->add_keys_and_lengths(&qpf->key_str, &qpf->key_len_str); } else { if (index != MAX_KEY) { - key_str.append(table->key_info[index].name); + qpf->key_str.append(table->key_info[index].name); } // key_len stays NULL } + qpf->rows= select ? select->records : table_rows; if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { - explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &mrr_type); + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &qpf->mrr_type); } } -int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags) -{ - StringBuffer<64> extra_str; - if (impossible_where) - { - const char *msg= "Impossible where"; - if (print_explain_message_line(output, explain_flags, 1/*select number*/, - "SIMPLE", msg)) - { - return 1; - } - return 0; - } - - if (using_where) - extra_str.append(STRING_WITH_LEN("Using where")); - - if (mrr_type.length() != 0) - { - if (extra_str.length() !=0) - extra_str.append(STRING_WITH_LEN("; ")); - extra_str.append(mrr_type); - } - - if (using_filesort) - { - if (extra_str.length() !=0) - extra_str.append(STRING_WITH_LEN("; ")); - extra_str.append(STRING_WITH_LEN("Using filesort")); - } - - /* - Single-table DELETE commands do not do "Using temporary". - "Using index condition" is also not possible (which is an unjustified limitation) - */ - - print_explain_row(output, explain_flags, - 1, /* id */ - select_lex->type, - table->pos_in_table_list->alias, - // partitions, - (enum join_type) jtype, - possible_keys_line.length()? possible_keys_line.c_ptr(): NULL, - key_str.length()? key_str.c_ptr() : NULL, - key_len_str.length() ? key_len_str.c_ptr() : NULL, - NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ - select ? select->records : table_rows, - extra_str.c_ptr()); - - /* - psergey-todo: handle all this through saving QPF. - - for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); - unit; - unit= unit->next_unit()) - { - if (unit->print_explain(output, explain_flags, printed_anything)) - return 1; - } - */ - return 0; -} - /** Implement DELETE SQL word. @@ -205,10 +164,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SELECT_LEX *select_lex= &thd->lex->select_lex; killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; - - Delete_plan *query_plan = new Delete_plan; - query_plan->index= MAX_KEY; - query_plan->using_filesort= FALSE; + + Delete_plan query_plan; + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list, TRUE, 0)) @@ -232,8 +191,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } thd_proc_info(thd, "init"); table->map=1; - query_plan->select_lex= &thd->lex->select_lex; - query_plan->table= table; + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -308,7 +267,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows const maybe_deleted= table->file->stats.records; DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); - query_plan->set_delete_all_rows(maybe_deleted); + query_plan.set_delete_all_rows(maybe_deleted); if (thd->lex->describe) goto exit_without_my_ok; @@ -338,7 +297,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (result == Item::COND_FALSE) // Impossible where { limit= 0; - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; } @@ -366,7 +325,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); if ((select && select->check_quick(thd, safe_update, limit)) || !limit) { - query_plan->set_impossible_where(); + query_plan.set_impossible_where(); if (thd->lex->describe) goto exit_without_my_ok; @@ -407,22 +366,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered") - query_plan->using_filesort= FALSE; - query_plan->index= MAX_KEY; + query_plan.using_filesort= FALSE; + query_plan.index= MAX_KEY; } else - query_plan->index= get_index_for_order(order, table, select, limit, - &query_plan->using_filesort, - &reverse); + query_plan.index= get_index_for_order(order, table, select, limit, + &query_plan.using_filesort, + &reverse); } - query_plan->select= select; - query_plan->possible_keys= table->quick_keys; - query_plan->table_rows= table->file->stats.records; + query_plan.select= select; + query_plan.possible_keys= table->quick_keys; + query_plan.table_rows= table->file->stats.records; - thd->lex->query_plan_footprint= new QPF_query; - thd->lex->query_plan_footprint->upd_del_plan= query_plan; - /* Ok, we have generated a query plan for the DELETE. - if we're running EXPLAIN DELETE, goto produce explain output @@ -431,12 +387,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto exit_without_my_ok; - query_plan->save_query_plan_footprint(); + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); thd->apc_target.enable(); + DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", dbug_serve_apcs(thd, 1);); - if (query_plan->using_filesort) + if (query_plan.using_filesort) { ha_rows examined_rows; ha_rows found_rows; @@ -444,7 +401,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SORT_FIELD *sortorder; { - DBUG_ASSERT(query_plan->index == MAX_KEY); + DBUG_ASSERT(query_plan.index == MAX_KEY); table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL | MY_THREAD_SPECIFIC)); @@ -480,7 +437,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, thd->apc_target.disable(); DBUG_RETURN(TRUE); } - if (query_plan->index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 1, 1, FALSE)) { @@ -491,7 +448,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } } else - init_read_record_idx(&info, thd, table, 1, query_plan->index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); init_ftfuncs(thd, select_lex, 1); thd_proc_info(thd, "updating"); @@ -584,11 +541,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, (void) table->file->extra(HA_EXTRA_NORMAL); thd->apc_target.disable(); - if (thd->lex->query_plan_footprint) - { - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - } cleanup: /* Invalidate the table in the query cache if something changed. This must @@ -652,9 +604,7 @@ cleanup: /* Special exits */ exit_without_my_ok: - query_plan->save_query_plan_footprint(); - thd->lex->query_plan_footprint->upd_del_plan= query_plan; - + query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); select_send *result; if (!(result= new select_send())) @@ -669,9 +619,6 @@ exit_without_my_ok: else result->send_eof(); - delete thd->lex->query_plan_footprint; - thd->lex->query_plan_footprint= NULL; - delete select; free_underlaid_joins(thd, select_lex); //table->set_keyread(false); -- cgit v1.2.1 From 52cfa54c1d211a17a9df7c38a4568ddc4d09e6d9 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 20 Jun 2013 20:58:26 +0400 Subject: [SHOW] EXPLAIN UPDATE/DELETE, code re-structuring Single table UPDATE/DELETE - Correctly print type=SIMPLE vs type=PRIMARY - Handle UPDATE/DELETE of mergeable VIEWs: we get the VIEW's select as the first subquery. (MySQL 5.6 doesn't print it because it finds that the subquery is not attached to any select) --- sql/sql_delete.cc | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 602831829d7..30ae60ddc43 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -58,10 +58,12 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query) if (deleting_all_rows) { qpf->deleting_all_rows= true; + qpf->select_type= "SIMPLE"; } else { - Update_plan::save_query_plan_footprint_intern(qpf); + qpf->deleting_all_rows= false; + Update_plan::save_query_plan_footprint_intern(query, qpf); } query->upd_del_plan= qpf; @@ -71,13 +73,14 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query) void Update_plan::save_query_plan_footprint(QPF_query *query) { QPF_update* qpf= new QPF_update; - save_query_plan_footprint_intern(qpf); + save_query_plan_footprint_intern(query, qpf); query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) +void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update *qpf) { + qpf->select_type= "SIMPLE"; qpf->table_name.append(table->pos_in_table_list->alias); if (impossible_where) { @@ -85,8 +88,10 @@ void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) return; } - // TODO: do we need the following: select_type - //select_lex->set_explain_type(TRUE); + qpf->impossible_where= false; + + select_lex->set_explain_type(TRUE); + qpf->select_type= select_lex->type; /* Set jtype */ if (select && select->quick) @@ -134,6 +139,28 @@ void Update_plan::save_query_plan_footprint_intern(QPF_update *qpf) { explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &qpf->mrr_type); } + + bool skip= updating_a_view; + /* Save subquery children */ + for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); + unit; + unit= unit->next_unit()) + { + if (skip) + { + skip= false; + continue; + } + /* + Display subqueries only if they are not parts of eliminated WHERE/ON + clauses. + */ + if (!(unit->item && unit->item->eliminated)) + qpf->add_child(unit->first_select()->select_number); + + //TODO: temporary?: + unit->save_qpf(query); + } } @@ -194,6 +221,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.select_lex= &thd->lex->select_lex; query_plan.table= table; + //psergey-todo: Ugly, discuss with Sanja + query_plan.updating_a_view= test(table_list->view); + if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); @@ -612,7 +642,7 @@ exit_without_my_ok: List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ result->prepare(dummy, &thd->lex->unit); thd->send_explain_fields(result); - int err2= thd->lex->query_plan_footprint->print_explain(result, 0); + int err2= thd->lex->query_plan_footprint->print_explain(result, 0 /* explain flags*/); if (err2) result->abort_result_set(); -- cgit v1.2.1 From 99a8bfe68cdd6411ed24b3fa465d5dd4b70be6dc Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 27 Jun 2013 01:00:22 +0400 Subject: [SHOW] EXPLAIN UPDATE/DELETE, code re-structuring - Update view.result (old EXPLAIN didn't match the execution) - Put in a stub code to work around the SELECT ... UNION SELECT ... ORDER BY (subuqery) problem --- sql/sql_delete.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 30ae60ddc43..50401e2af46 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -159,7 +159,9 @@ void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update qpf->add_child(unit->first_select()->select_number); //TODO: temporary?: - unit->save_qpf(query); + // A: yes. optimizing children subqueries has caused them to save QPFs, + // automatically. + //unit->save_qpf(query); } } -- cgit v1.2.1 From eeb671325723706bbd423e00982919333a9f54cc Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 26 Aug 2013 14:43:52 +0400 Subject: [SHOW] EXPLAIN UPDATE/DELETE - Post-merge fixes (conflict with DELETE .. RETURNING) - Add a testcase with EXPLAIN ... DELETE ... RETURNING --- sql/sql_delete.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 92ce69e5686..5655bb12886 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -658,18 +658,18 @@ cleanup: exit_without_my_ok: query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); - select_send *result; - if (!(result= new select_send())) + select_send *result2; + if (!(result2= new select_send())) return 1; /* purecov: inspected */ List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ - result->prepare(dummy, &thd->lex->unit); - thd->send_explain_fields(result); - int err2= thd->lex->query_plan_footprint->print_explain(result, 0 /* explain flags*/); + result2->prepare(dummy, &thd->lex->unit); + thd->send_explain_fields(result2); + int err2= thd->lex->query_plan_footprint->print_explain(result2, 0 /* explain flags*/); if (err2) - result->abort_result_set(); + result2->abort_result_set(); else - result->send_eof(); + result2->send_eof(); delete select; free_underlaid_joins(thd, select_lex); -- cgit v1.2.1 From ae6e95c498db4800e95fdd70fadaa608f6aa9f3f Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 17 Sep 2013 16:03:40 +0400 Subject: Code cleanup. --- sql/sql_delete.cc | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5655bb12886..93de4fccf5e 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,7 +51,7 @@ invoked on a running DELETE statement. */ -void Delete_plan::save_query_plan_footprint(QPF_query *query) +void Delete_plan::save_qpf(QPF_query *query) { QPF_delete* qpf= new QPF_delete; @@ -63,22 +63,22 @@ void Delete_plan::save_query_plan_footprint(QPF_query *query) else { qpf->deleting_all_rows= false; - Update_plan::save_query_plan_footprint_intern(query, qpf); + Update_plan::save_qpf_intern(query, qpf); } query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint(QPF_query *query) +void Update_plan::save_qpf(QPF_query *query) { QPF_update* qpf= new QPF_update; - save_query_plan_footprint_intern(query, qpf); + save_qpf_intern(query, qpf); query->upd_del_plan= qpf; } -void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update *qpf) +void Update_plan::save_qpf_intern(QPF_query *query, QPF_update *qpf) { qpf->select_type= "SIMPLE"; qpf->table_name.append(table->pos_in_table_list->alias); @@ -116,7 +116,6 @@ void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update qpf->using_where= test(select && select->cond); qpf->using_filesort= using_filesort; - //using_filesort is already set make_possible_keys_line(table, possible_keys, &qpf->possible_keys_line); /* Calculate key_len */ @@ -141,6 +140,7 @@ void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update } bool skip= updating_a_view; + /* Save subquery children */ for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); unit; @@ -157,11 +157,6 @@ void Update_plan::save_query_plan_footprint_intern(QPF_query *query, QPF_update */ if (!(unit->item && unit->item->eliminated)) qpf->add_child(unit->first_select()->select_number); - - //TODO: temporary?: - // A: yes. optimizing children subqueries has caused them to save QPFs, - // automatically. - //unit->save_qpf(query); } } @@ -423,7 +418,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto exit_without_my_ok; - query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); + query_plan.save_qpf(thd->lex->query_plan_footprint); thd->apc_target.enable(); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", @@ -656,7 +651,7 @@ cleanup: /* Special exits */ exit_without_my_ok: - query_plan.save_query_plan_footprint(thd->lex->query_plan_footprint); + query_plan.save_qpf(thd->lex->query_plan_footprint); select_send *result2; if (!(result2= new select_send())) -- cgit v1.2.1 From 6519ca51dd8815dc1f2d88708e540bd4032cb45e Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 1 Oct 2013 17:49:03 +0400 Subject: EXPLAIN UPDATE/DELETE - Make EXPLAIN UPDATE/DELETE work inside SPs - Return correct error code from mysql_delete() - EXPLAIN will create a multi_delete object (as it affects the optimization). select_result will be only used for producing EXPLAIN output. --- sql/sql_delete.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 93de4fccf5e..25fe56d2e71 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -183,7 +183,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool const_cond_result; ha_rows deleted= 0; bool reverse= FALSE; - bool err= true; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); SELECT_LEX *select_lex= &thd->lex->select_lex; @@ -659,7 +658,7 @@ exit_without_my_ok: List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ result2->prepare(dummy, &thd->lex->unit); thd->send_explain_fields(result2); - int err2= thd->lex->query_plan_footprint->print_explain(result2, 0 /* explain flags*/); + int err2= thd->lex->query_plan_footprint->print_explain(result2, thd->lex->describe); if (err2) result2->abort_result_set(); @@ -669,7 +668,7 @@ exit_without_my_ok: delete select; free_underlaid_joins(thd, select_lex); //table->set_keyread(false); - DBUG_RETURN((err || thd->is_error() || thd->killed) ? 1 : 0); + DBUG_RETURN((err2 || thd->is_error() || thd->killed) ? 1 : 0); } -- cgit v1.2.1 From 5e4044e92c748c69965bd9b22f231815c9fa8e51 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 4 Oct 2013 18:50:47 +0400 Subject: MDEV-5093, MDEV-5094: - Make EXPLAIN {PARTITIONS,EXTENDED} {UPDATE,DELETE} work. --- sql/sql_delete.cc | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 25fe56d2e71..adc998be3a1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -40,7 +40,7 @@ #include "records.h" // init_read_record, #include "sql_derived.h" // mysql_handle_list_of_derived // end_read_record - +#include "sql_partition.h" // make_used_partitions_str /* @brief @@ -92,7 +92,24 @@ void Update_plan::save_qpf_intern(QPF_query *query, QPF_update *qpf) select_lex->set_explain_type(TRUE); qpf->select_type= select_lex->type; - + /* Partitions */ + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *part_info; + if ((part_info= table->part_info)) + { + make_used_partitions_str(part_info, &qpf->used_partitions); + qpf->used_partitions_set= true; + } + else + qpf->used_partitions_set= false; +#else + /* just produce empty column if partitioning is not compiled in */ + qpf->used_partitions_set= false; +#endif + } + + /* Set jtype */ if (select && select->quick) { -- cgit v1.2.1 From fedf769f0b2001f8294c2b44dbcaca1e562f82a9 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 5 Oct 2013 09:58:22 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Address review feedback: rename nearly any name used by the new EXPLAIN code. --- sql/sql_delete.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index adc998be3a1..58deae4162a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -51,7 +51,7 @@ invoked on a running DELETE statement. */ -void Delete_plan::save_qpf(QPF_query *query) +void Delete_plan::save_explain_data(Explain_query *query) { QPF_delete* qpf= new QPF_delete; @@ -63,22 +63,22 @@ void Delete_plan::save_qpf(QPF_query *query) else { qpf->deleting_all_rows= false; - Update_plan::save_qpf_intern(query, qpf); + Update_plan::save_explain_data_intern(query, qpf); } query->upd_del_plan= qpf; } -void Update_plan::save_qpf(QPF_query *query) +void Update_plan::save_explain_data(Explain_query *query) { QPF_update* qpf= new QPF_update; - save_qpf_intern(query, qpf); + save_explain_data_intern(query, qpf); query->upd_del_plan= qpf; } -void Update_plan::save_qpf_intern(QPF_query *query, QPF_update *qpf) +void Update_plan::save_explain_data_intern(Explain_query *query, QPF_update *qpf) { qpf->select_type= "SIMPLE"; qpf->table_name.append(table->pos_in_table_list->alias); @@ -434,7 +434,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe) goto exit_without_my_ok; - query_plan.save_qpf(thd->lex->query_plan_footprint); + query_plan.save_explain_data(thd->lex->explain); thd->apc_target.enable(); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", @@ -667,7 +667,7 @@ cleanup: /* Special exits */ exit_without_my_ok: - query_plan.save_qpf(thd->lex->query_plan_footprint); + query_plan.save_explain_data(thd->lex->explain); select_send *result2; if (!(result2= new select_send())) @@ -675,7 +675,7 @@ exit_without_my_ok: List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ result2->prepare(dummy, &thd->lex->unit); thd->send_explain_fields(result2); - int err2= thd->lex->query_plan_footprint->print_explain(result2, thd->lex->describe); + int err2= thd->lex->explain->print_explain(result2, thd->lex->describe); if (err2) result2->abort_result_set(); -- cgit v1.2.1 From 72bc6d7364f7cf6cae49c74cf1e56832053f91eb Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 5 Oct 2013 13:19:45 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Address review feedback: more renames --- sql/sql_delete.cc | 64 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 31 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 58deae4162a..518bdc6ab43 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -53,59 +53,60 @@ void Delete_plan::save_explain_data(Explain_query *query) { - QPF_delete* qpf= new QPF_delete; + Explain_delete* explain= new Explain_delete; if (deleting_all_rows) { - qpf->deleting_all_rows= true; - qpf->select_type= "SIMPLE"; + explain->deleting_all_rows= true; + explain->select_type= "SIMPLE"; } else { - qpf->deleting_all_rows= false; - Update_plan::save_explain_data_intern(query, qpf); + explain->deleting_all_rows= false; + Update_plan::save_explain_data_intern(query, explain); } - query->upd_del_plan= qpf; + query->upd_del_plan= explain; } void Update_plan::save_explain_data(Explain_query *query) { - QPF_update* qpf= new QPF_update; - save_explain_data_intern(query, qpf); - query->upd_del_plan= qpf; + Explain_update* explain= new Explain_update; + save_explain_data_intern(query, explain); + query->upd_del_plan= explain; } -void Update_plan::save_explain_data_intern(Explain_query *query, QPF_update *qpf) +void Update_plan::save_explain_data_intern(Explain_query *query, + Explain_update *explain) { - qpf->select_type= "SIMPLE"; - qpf->table_name.append(table->pos_in_table_list->alias); + explain->select_type= "SIMPLE"; + explain->table_name.append(table->pos_in_table_list->alias); if (impossible_where) { - qpf->impossible_where= true; + explain->impossible_where= true; return; } - qpf->impossible_where= false; + explain->impossible_where= false; select_lex->set_explain_type(TRUE); - qpf->select_type= select_lex->type; + explain->select_type= select_lex->type; /* Partitions */ { #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info; if ((part_info= table->part_info)) { - make_used_partitions_str(part_info, &qpf->used_partitions); - qpf->used_partitions_set= true; + make_used_partitions_str(part_info, &explain->used_partitions); + explain->used_partitions_set= true; } else - qpf->used_partitions_set= false; + explain->used_partitions_set= false; #else /* just produce empty column if partitioning is not compiled in */ - qpf->used_partitions_set= false; + explain->used_partitions_set= false; #endif } @@ -118,42 +119,43 @@ void Update_plan::save_explain_data_intern(Explain_query *query, QPF_update *qpf (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) - qpf->jtype= JT_INDEX_MERGE; + explain->jtype= JT_INDEX_MERGE; else - qpf->jtype= JT_RANGE; + explain->jtype= JT_RANGE; } else { if (index == MAX_KEY) - qpf->jtype= JT_ALL; + explain->jtype= JT_ALL; else - qpf->jtype= JT_NEXT; + explain->jtype= JT_NEXT; } - qpf->using_where= test(select && select->cond); - qpf->using_filesort= using_filesort; + explain->using_where= test(select && select->cond); + explain->using_filesort= using_filesort; - make_possible_keys_line(table, possible_keys, &qpf->possible_keys_line); + make_possible_keys_line(table, possible_keys, &explain->possible_keys_line); /* Calculate key_len */ if (select && select->quick) { - select->quick->add_keys_and_lengths(&qpf->key_str, &qpf->key_len_str); + select->quick->add_keys_and_lengths(&explain->key_str, &explain->key_len_str); } else { if (index != MAX_KEY) { - qpf->key_str.append(table->key_info[index].name); + explain->key_str.append(table->key_info[index].name); } // key_len stays NULL } - qpf->rows= select ? select->records : table_rows; + explain->rows= select ? select->records : table_rows; if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) { - explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, &qpf->mrr_type); + explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick, + &explain->mrr_type); } bool skip= updating_a_view; @@ -173,7 +175,7 @@ void Update_plan::save_explain_data_intern(Explain_query *query, QPF_update *qpf clauses. */ if (!(unit->item && unit->item->eliminated)) - qpf->add_child(unit->first_select()->select_number); + explain->add_child(unit->first_select()->select_number); } } -- cgit v1.2.1 From abcf14e595d35ebacb1d23cd4a6ced20080f8d4b Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 5 Oct 2013 13:44:01 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Handle the case when EXPLAIN UPDATE/DELETE has pruned away all partitions. --- sql/sql_delete.cc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 518bdc6ab43..a84d6795603 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -83,14 +83,22 @@ void Update_plan::save_explain_data_intern(Explain_query *query, { explain->select_type= "SIMPLE"; explain->table_name.append(table->pos_in_table_list->alias); + + explain->impossible_where= false; + explain->no_partitions= false; + if (impossible_where) { explain->impossible_where= true; return; } - - explain->impossible_where= false; + if (no_partitions) + { + explain->no_partitions= true; + return; + } + select_lex->set_explain_type(TRUE); explain->select_type= select_lex->type; /* Partitions */ @@ -139,7 +147,8 @@ void Update_plan::save_explain_data_intern(Explain_query *query, /* Calculate key_len */ if (select && select->quick) { - select->quick->add_keys_and_lengths(&explain->key_str, &explain->key_len_str); + select->quick->add_keys_and_lengths(&explain->key_str, + &explain->key_len_str); } else { @@ -356,8 +365,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (prune_partitions(thd, table, conds)) { free_underlaid_joins(thd, select_lex); - // No matching record - //psergey-explain-todo: No-partitions used EXPLAIN here.. + + query_plan.set_no_partitions(); + if (thd->lex->describe) + goto exit_without_my_ok; + my_ok(thd, 0); DBUG_RETURN(0); } -- cgit v1.2.1 From 062b7bfa39ae418f1da85bef3d8968fc9d53569e Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 5 Oct 2013 13:48:45 +0400 Subject: Better comments --- sql/sql_delete.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index a84d6795603..a9d97a6debc 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -245,8 +245,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->map=1; query_plan.select_lex= &thd->lex->select_lex; query_plan.table= table; - - //psergey-todo: Ugly, discuss with Sanja query_plan.updating_a_view= test(table_list->view); if (mysql_prepare_delete(thd, table_list, select_lex->with_wild, -- cgit v1.2.1 From 98a8642fe827fd9ac16bdfaf556599fa509d4180 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 7 Oct 2013 17:29:51 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Add support for EXPLAIN INSERT. --- sql/sql_delete.cc | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index a9d97a6debc..c023a1eebf4 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -680,20 +680,8 @@ cleanup: /* Special exits */ exit_without_my_ok: query_plan.save_explain_data(thd->lex->explain); + int err2= thd->lex->explain->send_explain(thd); - select_send *result2; - if (!(result2= new select_send())) - return 1; /* purecov: inspected */ - List dummy; /* note: looked in 5.6 and they too use a dummy list like this */ - result2->prepare(dummy, &thd->lex->unit); - thd->send_explain_fields(result2); - int err2= thd->lex->explain->print_explain(result2, thd->lex->describe); - - if (err2) - result2->abort_result_set(); - else - result2->send_eof(); - delete select; free_underlaid_joins(thd, select_lex); //table->set_keyread(false); -- cgit v1.2.1 From fda46df62071f54ebc4d806c6d9caf031d801150 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 8 Oct 2013 14:26:14 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - if EXPLAIN DELETE prints "Deleting all rows", it should show the expected number of rows in the rows column. --- sql/sql_delete.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c023a1eebf4..50a5ec79166 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -59,6 +59,7 @@ void Delete_plan::save_explain_data(Explain_query *query) { explain->deleting_all_rows= true; explain->select_type= "SIMPLE"; + explain->rows= table_rows; } else { -- cgit v1.2.1 From 69e6a2bb22434d94d96312ba8a0540195273dfdd Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 8 Oct 2013 16:13:49 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Update test results after last few csets - Generate correct value for `possible_keys` column for single table UPDATE/DELETE. --- sql/sql_delete.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 50a5ec79166..19401496a74 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -436,7 +436,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } query_plan.select= select; - query_plan.possible_keys= table->quick_keys; + query_plan.possible_keys= select? select->possible_keys: key_map(0); query_plan.table_rows= table->file->stats.records; /* -- cgit v1.2.1 From 161d68759433b0315a6b95209d3db86be411a686 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 9 Oct 2013 09:40:33 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Generate correct contents of `Extra` column for UPDATEs/DELETEs that use quick selects - UPDATEs with used_key_is_modified=true will show "Using buffer" --- sql/sql_delete.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 19401496a74..15dfe3e6c7c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -142,14 +142,16 @@ void Update_plan::save_explain_data_intern(Explain_query *query, explain->using_where= test(select && select->cond); explain->using_filesort= using_filesort; + explain->using_io_buffer= using_io_buffer; make_possible_keys_line(table, possible_keys, &explain->possible_keys_line); + explain->quick_info= NULL; + /* Calculate key_len */ if (select && select->quick) { - select->quick->add_keys_and_lengths(&explain->key_str, - &explain->key_len_str); + explain->quick_info= select->quick->get_explain(mem_root); } else { @@ -218,7 +220,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; bool with_select= !select_lex->item_list.is_empty(); - Delete_plan query_plan; + Delete_plan query_plan(thd->mem_root); query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_delete"); -- cgit v1.2.1 From 3c6ac6694d291dc454af6f9042c9217afd7fff9b Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 9 Oct 2013 13:07:46 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Produce correct #rows for ORDER BY ... LIMIT N queries that take advantage of ordered index read to read only N rows. --- sql/sql_delete.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 15dfe3e6c7c..00193800b93 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -59,7 +59,7 @@ void Delete_plan::save_explain_data(Explain_query *query) { explain->deleting_all_rows= true; explain->select_type= "SIMPLE"; - explain->rows= table_rows; + explain->rows= scanned_rows; } else { @@ -161,7 +161,7 @@ void Update_plan::save_explain_data_intern(Explain_query *query, } // key_len stays NULL } - explain->rows= select ? select->records : table_rows; + explain->rows= scanned_rows; if (select && select->quick && select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) @@ -421,6 +421,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_QUICK); + query_plan.scanned_rows= select? select->records: table->file->stats.records; if (order) { table->update_const_key_parts(conds); @@ -432,14 +433,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.index= MAX_KEY; } else + { + ha_rows scanned_limit= query_plan.scanned_rows; query_plan.index= get_index_for_order(order, table, select, limit, + &scanned_limit, &query_plan.using_filesort, &reverse); + if (!query_plan.using_filesort) + query_plan.scanned_rows= scanned_limit; + } } query_plan.select= select; query_plan.possible_keys= select? select->possible_keys: key_map(0); - query_plan.table_rows= table->file->stats.records; /* Ok, we have generated a query plan for the DELETE. -- cgit v1.2.1 From 7e919c52a52d372d68b930b1cd5b763364264629 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 9 Oct 2013 17:15:34 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE - Produce correct `key_len` when type=index. --- sql/sql_delete.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 00193800b93..7f58526d174 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -158,8 +158,11 @@ void Update_plan::save_explain_data_intern(Explain_query *query, if (index != MAX_KEY) { explain->key_str.append(table->key_info[index].name); + char buf[64]; + size_t length; + length= longlong10_to_str(table->key_info[index].key_length, buf, 10) - buf; + explain->key_len_str.append(buf, length); } - // key_len stays NULL } explain->rows= scanned_rows; -- cgit v1.2.1 From 105e3ae6c93f57498fa6c504dfbb92203b0d1056 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 14 Oct 2013 20:09:33 +0400 Subject: MDEV-3798: EXPLAIN UPDATE/DELETE Update the SHOW EXPLAIN code to work with the new architecture (part#1): Before, SHOW EXPLAIN operated on real query plan structures, which meant it had to check when SELECTs are created/deleted. SELECTs would call apc_target->enable() when they got a query plan and disable() when their query plan was deleted. Now, Explain data structure becomes available at once (and we call apc_target->enable()) and then it stays until it is deleted (when that happens, we call apc_target->disable()). --- sql/sql_delete.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 7f58526d174..fae44a72205 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -66,8 +66,8 @@ void Delete_plan::save_explain_data(Explain_query *query) explain->deleting_all_rows= false; Update_plan::save_explain_data_intern(query, explain); } - - query->upd_del_plan= explain; + + query->add_upd_del_plan(explain); } @@ -75,7 +75,7 @@ void Update_plan::save_explain_data(Explain_query *query) { Explain_update* explain= new Explain_update; save_explain_data_intern(query, explain); - query->upd_del_plan= explain; + query->add_upd_del_plan(explain); } @@ -459,7 +459,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, goto exit_without_my_ok; query_plan.save_explain_data(thd->lex->explain); - thd->apc_target.enable(); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", dbug_serve_apcs(thd, 1);); @@ -486,7 +485,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { delete select; free_underlaid_joins(thd, &thd->lex->select_lex); - thd->apc_target.disable(); DBUG_RETURN(TRUE); } thd->examined_row_count+= examined_rows; @@ -505,7 +503,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { delete select; free_underlaid_joins(thd, select_lex); - thd->apc_target.disable(); DBUG_RETURN(TRUE); } if (query_plan.index == MAX_KEY || (select && select->quick)) @@ -514,7 +511,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { delete select; free_underlaid_joins(thd, select_lex); - thd->apc_target.disable(); DBUG_RETURN(TRUE); } } @@ -624,7 +620,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); - thd->apc_target.disable(); cleanup: /* Invalidate the table in the query cache if something changed. This must -- cgit v1.2.1 From 4bed7aa858e7946471fb37ce30f3273ec28867ce Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 16 Oct 2013 12:13:51 +0400 Subject: MDEV-3798: [SHOW] EXPLAIN UPDATE/DELETE, Memory leak in binlog.binlog_base64_flag: - It turns out, there are statements that will call lex_start(thd->lex) after parsing has been finished. lex_start() will set lex->explain=NULL, which will lose the pointer to already allocated Explain_plan object. - To get rid of this, switch to lazy creation of lex->explain. Now, it is created only when we get a part ot query plan. --- sql/sql_delete.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql/sql_delete.cc') diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index fae44a72205..e1cd8bed44c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -228,6 +228,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_delete"); + create_explain_query(thd->lex, thd->mem_root); if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); @@ -457,7 +458,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, */ if (thd->lex->describe) goto exit_without_my_ok; - + query_plan.save_explain_data(thd->lex->explain); DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start", -- cgit v1.2.1