diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2013-02-12 14:37:08 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2013-02-12 14:37:08 +0400 |
commit | d2995031d9214206689660069024525808c8a683 (patch) | |
tree | 2d08506cf8da9753867bdc81bdc22f03c3106683 | |
parent | b38b44cfe1c62b4c2211aec7c3f1407f14a2aa21 (diff) | |
download | mariadb-git-d2995031d9214206689660069024525808c8a683.tar.gz |
SHOW EXPLAIN for MariaDB
- Support [SHOW] EXPLAIN UPDATE (needs code cleanup).
-rw-r--r-- | mysql-test/r/explain_non_select.result | 53 | ||||
-rw-r--r-- | mysql-test/r/show_explain.result | 18 | ||||
-rw-r--r-- | mysql-test/r/show_explain_non_select.result | 24 | ||||
-rw-r--r-- | mysql-test/t/explain_non_select.test | 39 | ||||
-rw-r--r-- | mysql-test/t/show_explain.test | 4 | ||||
-rw-r--r-- | mysql-test/t/show_explain_non_select.test | 31 | ||||
-rw-r--r-- | sql/sql_delete.cc | 26 | ||||
-rw-r--r-- | sql/sql_lex.cc | 8 | ||||
-rw-r--r-- | sql/sql_lex.h | 41 | ||||
-rw-r--r-- | sql/sql_select.cc | 3 | ||||
-rw-r--r-- | sql/sql_update.cc | 134 |
11 files changed, 318 insertions, 63 deletions
diff --git a/mysql-test/r/explain_non_select.result b/mysql-test/r/explain_non_select.result index aa592f0b198..1c8e444c4b9 100644 --- a/mysql-test/r/explain_non_select.result +++ b/mysql-test/r/explain_non_select.result @@ -38,7 +38,7 @@ id select_type table type possible_keys key key_len ref rows Extra explain delete from t1 where a < (select max(a) from t0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 range a a 5 NULL 1 Using where -2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted +2 SUBQUERY t0 ALL NULL NULL NULL NULL 8 explain delete from t1 where a < (select max(a) from t0 where a < t1.b); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where @@ -51,3 +51,54 @@ 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 drop table t0, t1; +# ################################################################### +# ## EXPLAIN UPDATE tests +# ################################################################### +create table t0 (a int) engine=myisam; +insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8); +explain update t0 set a=3 where a=4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where +create table t1 (a int, b int, filler char(100), key(a), key(b)); +insert into t1 +select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler' +from t0 A, t0 B, t0 C; +explain update t1 set a=a+1 where 3>4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible where +explain update t1 set a=a+1 where a=3 and a=4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible where +# This should use an index, possible_keys=NULL because there is no WHERE +explain update t1 set a=a+1 order by a limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 512 +# This should use range, possible_keys={a,b} +explain update t1 set filler='fooo' where a<20 and b < 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a,b a 5 NULL 1 Using where +# This should use ALL + filesort +explain update t1 set filler='fooo' order by a+1 limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 512 +# This should use range + using filesort +explain update t1 set filler='fooo' where a<20 order by b limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 1 Using where +# Try some subqueries: +explain update t1 set filler='fooo' where a < (select max(a) from t0); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 range a a 5 NULL 1 Using where +2 SUBQUERY t0 ALL NULL NULL NULL NULL 8 +explain update t1 set filler='fooo' where a < (select max(a) from t0 where a < t1.b); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 8 Using where +# +# Tests for multi-table UPDATE +# +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 +drop table t0, t1; diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index 8baf4d40e7c..1152baaf835 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -186,9 +186,17 @@ set @show_explain_probe_select_id=2; set debug_dbug='+d,show_explain_probe_join_exec_start'; update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; show explain for $thr2; -ERROR HY000: Target is not running an EXPLAINable command +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 show explain for $thr2; -ERROR HY000: Target is not running an EXPLAINable command +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where +Warnings: +Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 drop table t2; set debug_dbug=@old_debug; # @@ -207,7 +215,7 @@ Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where Warnings: Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 drop table t2; @@ -228,13 +236,13 @@ Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where Warnings: Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 3 -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted +2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where Warnings: Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2 a SUBQ diff --git a/mysql-test/r/show_explain_non_select.result b/mysql-test/r/show_explain_non_select.result index 5358981e6f2..0bd1e959405 100644 --- a/mysql-test/r/show_explain_non_select.result +++ b/mysql-test/r/show_explain_non_select.result @@ -1,19 +1,27 @@ drop table if exists t0, t1; +SET @old_debug= @@session.debug; +set debug_sync='RESET'; create table t0 (a int) engine=myisam; insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8); create table t1 (a int, b int, filler char(100), key(a), key(b)); insert into t1 select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler' from t0 A, t0 B, t0 C; -set debug_dbug='d,show_explain_probe_delete_exec_start'; +# +# Test SHOW EXPLAIN for single-table DELETE +# +set debug_dbug='+d,show_explain_probe_delete_exec_start'; delete from t1 where a<10 and b+1>1000; show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range a a 5 NULL 1 Using where Warnings: Note 1003 delete from t1 where a<10 and b+1>1000 +# +# Test SHOW EXPLAIN for multi-table DELETE +# set @show_explain_probe_select_id=1; -set debug_dbug='d,show_explain_probe_do_select'; +set debug_dbug='+d,show_explain_probe_do_select'; delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000; show explain for $thr2; id select_type table type possible_keys key key_len ref rows Extra @@ -21,4 +29,16 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref a a 5 test.t0.a 4 Using where Warnings: Note 1003 delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000 +# +# Test SHOW EXPLAIN for single-table UPDATE +# +set debug_dbug='+d,show_explain_probe_update_exec_start'; +update t1 set filler='filler-data-2' where a<10 and b+1>1000; +show explain for $thr2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range a a 5 NULL 1 Using where +Warnings: +Note 1003 update t1 set filler='filler-data-2' where a<10 and b+1>1000 drop table t0,t1; +set debug_dbug=@old_debug; +set debug_sync='RESET'; diff --git a/mysql-test/t/explain_non_select.test b/mysql-test/t/explain_non_select.test index bc3c2bb6c16..66432a8e4a5 100644 --- a/mysql-test/t/explain_non_select.test +++ b/mysql-test/t/explain_non_select.test @@ -43,5 +43,44 @@ explain delete from t1 where a < (select max(a) from t0 where a < t1.b); --echo # Tests for multi-table DELETE --echo # explain delete t1 from t0, t1 where t0.a = t1.a; +drop table t0, t1; + +--echo # ################################################################### +--echo # ## EXPLAIN UPDATE tests +--echo # ################################################################### +create table t0 (a int) engine=myisam; +insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8); + +explain update t0 set a=3 where a=4; + +create table t1 (a int, b int, filler char(100), key(a), key(b)); +insert into t1 +select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler' +from t0 A, t0 B, t0 C; + +explain update t1 set a=a+1 where 3>4; +explain update t1 set a=a+1 where a=3 and a=4; + +--echo # This should use an index, possible_keys=NULL because there is no WHERE +explain update t1 set a=a+1 order by a limit 2; + +--echo # This should use range, possible_keys={a,b} +explain update t1 set filler='fooo' where a<20 and b < 10; + +--echo # This should use ALL + filesort +explain update t1 set filler='fooo' order by a+1 limit 2; + +--echo # This should use range + using filesort +explain update t1 set filler='fooo' where a<20 order by b limit 2; + +--echo # Try some subqueries: +explain update t1 set filler='fooo' where a < (select max(a) from t0); +explain update t1 set filler='fooo' where a < (select max(a) from t0 where a < t1.b); + +--echo # +--echo # Tests for multi-table UPDATE +--echo # +explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a; + drop table t0, t1; diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index d87bf2f14d4..0694160b151 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -238,10 +238,10 @@ set debug_dbug='+d,show_explain_probe_join_exec_start'; send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ; connection default; --source include/wait_condition.inc ---error ER_TARGET_NOT_EXPLAINABLE +#--error ER_TARGET_NOT_EXPLAINABLE evalp show explain for $thr2; --source include/wait_condition.inc ---error ER_TARGET_NOT_EXPLAINABLE +#--error ER_TARGET_NOT_EXPLAINABLE evalp show explain for $thr2; connection con1; reap; diff --git a/mysql-test/t/show_explain_non_select.test b/mysql-test/t/show_explain_non_select.test index da788779626..f71affd8a6e 100644 --- a/mysql-test/t/show_explain_non_select.test +++ b/mysql-test/t/show_explain_non_select.test @@ -9,6 +9,9 @@ drop table if exists t0, t1; --enable_warnings +SET @old_debug= @@session.debug; +set debug_sync='RESET'; + # # Setup two threads and their ids # @@ -31,11 +34,11 @@ from t0 A, t0 B, t0 C; let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2; -# -# Test SHOW EXPLAIN for single-table DELETEs -# +--echo # +--echo # Test SHOW EXPLAIN for single-table DELETE +--echo # connection con2; -set debug_dbug='d,show_explain_probe_delete_exec_start'; +set debug_dbug='+d,show_explain_probe_delete_exec_start'; send delete from t1 where a<10 and b+1>1000; connection default; @@ -44,8 +47,11 @@ evalp show explain for $thr2; connection con2; reap; +--echo # +--echo # Test SHOW EXPLAIN for multi-table DELETE +--echo # set @show_explain_probe_select_id=1; -set debug_dbug='d,show_explain_probe_do_select'; +set debug_dbug='+d,show_explain_probe_do_select'; send delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000; connection default; --source include/wait_condition.inc @@ -53,5 +59,20 @@ evalp show explain for $thr2; connection con2; reap; +--echo # +--echo # Test SHOW EXPLAIN for single-table UPDATE +--echo # +connection con2; +set debug_dbug='+d,show_explain_probe_update_exec_start'; +send update t1 set filler='filler-data-2' where a<10 and b+1>1000; + +connection default; +--source include/wait_condition.inc +evalp show explain for $thr2; +connection con2; +reap; drop table t0,t1; + +set debug_dbug=@old_debug; +set debug_sync='RESET'; 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; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 74e4b3e1162..714ab373b17 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -448,7 +448,7 @@ void lex_start(THD *thd) lex->thd= lex->unit.thd= thd; - lex->delete_plan= NULL; + lex->upd_del_plan= NULL; lex->context_stack.empty(); lex->unit.init_query(); lex->unit.init_select(); @@ -2558,7 +2558,7 @@ LEX::LEX() INITIAL_LEX_PLUGIN_LIST_SIZE, 0); reset_query_tables_list(TRUE); mi.init(); - delete_plan= NULL; + upd_del_plan= NULL; } @@ -4171,9 +4171,9 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) int LEX::print_explain(select_result_sink *output, uint8 explain_flags, bool *printed_anything) { - if (delete_plan) + if (upd_del_plan) { - delete_plan->print_explain(output, explain_flags, printed_anything); + upd_del_plan->print_explain(output, explain_flags, printed_anything); return 0; } int res= unit.print_explain(output, explain_flags, printed_anything); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index af35ccb01b0..588ad172a3c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2347,29 +2347,48 @@ protected: class Delete_plan; class SQL_SELECT; -/* Query plan of a single-table DELETE */ -class Delete_plan +/* + Query plan of a single-table UPDATE. + (This is actually a plan for single-table DELETE also) +*/ +class Update_plan { - bool deleting_all_rows; +protected: bool impossible_where; public: - TABLE *table; SQL_SELECT *select; uint index; ha_rows table_rows; /* Use if select==NULL */ - bool using_filesort; - key_map possible_keys; - /* Top-level select_lex. Most of its fields are not used, we need it only to get to the subqueries. */ SELECT_LEX *select_lex; + + key_map possible_keys; + bool using_filesort; + + /* Set this plan to be a plan to do nothing because of impossible WHRE*/ + void set_impossible_where() { impossible_where= true; } + + virtual int print_explain(select_result_sink *output, uint8 explain_flags, + bool *printed_anything); + virtual ~Update_plan() {} + + Update_plan() : impossible_where(false), using_filesort(false) {} +}; + + +/* Query plan of a single-table DELETE */ +class Delete_plan : public Update_plan +{ + bool deleting_all_rows; +public: /* Construction functions */ Delete_plan() : - deleting_all_rows(false), impossible_where(false), using_filesort(false) {} + deleting_all_rows(false) {} /* Set this query plan to be a plan to make a call to h->delete_all_rows() */ void set_delete_all_rows(ha_rows rows_arg) @@ -2377,10 +2396,6 @@ public: deleting_all_rows= true; table_rows= rows_arg; } - - /* Set this plan to be a plan to do nothing because of impossible WHRE*/ - void set_impossible_where() { impossible_where= true; } - int print_explain(select_result_sink *output, uint8 explain_flags, bool *printed_anything); }; @@ -2398,7 +2413,7 @@ struct LEX: public Query_tables_list SELECT_LEX *all_selects_list; /* For single-table DELETE: its query plan */ - Delete_plan *delete_plan; + Update_plan *upd_del_plan; char *length,*dec,*change; LEX_STRING name; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 230355c550e..d9177b9240e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10756,7 +10756,8 @@ void JOIN::cleanup(bool full) DBUG_ENTER("JOIN::cleanup"); DBUG_PRINT("enter", ("full %u", (uint) full)); - have_query_plan= QEP_DELETED; + if (full) + have_query_plan= QEP_DELETED; //psergey: this is a problem! if (table) { diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 84adfc2c3f5..11cb493da97 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -260,7 +260,7 @@ int mysql_update(THD *thd, bool can_compare_record; int res; int error, loc_error; - uint used_index, dup_key_found; + uint dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -270,12 +270,15 @@ int mysql_update(THD *thd, ha_rows updated, found; key_map old_covering_keys; TABLE *table; - SQL_SELECT *select; + SQL_SELECT *select= NULL; READ_RECORD info; SELECT_LEX *select_lex= &thd->lex->select_lex; ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; + Update_plan query_plan; + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_update"); if (open_tables(thd, &table_list, &table_count, 0)) @@ -314,6 +317,8 @@ int mysql_update(THD *thd, table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -370,7 +375,12 @@ int mysql_update(THD *thd, Item::cond_result cond_value; conds= remove_eq_conds(thd, conds, &cond_value); if (cond_value == Item::COND_FALSE) + { limit= 0; // Impossible WHERE + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + } } /* @@ -400,6 +410,10 @@ int mysql_update(THD *thd, if (error || !limit || thd->is_error() || (select && select->check_quick(thd, safe_update, limit))) { + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + delete select; free_underlaid_joins(thd, select_lex); /* @@ -438,16 +452,16 @@ int mysql_update(THD *thd, if (select && select->quick && select->quick->unique_key_range()) { // Single row select (always "ordered"): Ok to use with key field UPDATE need_sort= FALSE; - used_index= MAX_KEY; + query_plan.index= MAX_KEY; used_key_is_modified= FALSE; } else { - used_index= get_index_for_order(order, table, select, limit, - &need_sort, &reverse); + query_plan.index= get_index_for_order(order, table, select, limit, + &need_sort, &reverse); if (select && select->quick) { - DBUG_ASSERT(need_sort || used_index == select->quick->index); + DBUG_ASSERT(need_sort || query_plan.index == select->quick->index); used_key_is_modified= (!select->quick->unique_key_range() && select->quick->is_keys_used(table->write_set)); } @@ -455,14 +469,37 @@ int mysql_update(THD *thd, { if (need_sort) { // Assign table scan index to check below for modified key fields: - used_index= table->file->key_used_on_scan; + query_plan.index= table->file->key_used_on_scan; } - if (used_index != MAX_KEY) + if (query_plan.index != MAX_KEY) { // Check if we are modifying a key that we are used to search with: - used_key_is_modified= is_key_used(table, used_index, table->write_set); + used_key_is_modified= is_key_used(table, query_plan.index, table->write_set); } } } + + /* + Query optimization is finished at this point. + - Save the decisions in the query plan + - if we're running EXPLAIN UPDATE, get out + */ + 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; + + /* + Ok, we have generated a query plan for the UPDATE. + - if we're running EXPLAIN UPDATE, 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_update_exec_start", + dbug_serve_apcs(thd, 1);); + if (used_key_is_modified || order || partition_key_modified(table, table->write_set)) @@ -476,8 +513,8 @@ int mysql_update(THD *thd, DBUG_ASSERT(table->read_set == &table->def_read_set); DBUG_ASSERT(table->write_set == &table->def_write_set); - if (used_index < MAX_KEY && old_covering_keys.is_set(used_index)) - table->add_read_columns_used_by_index(used_index); + if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index)) + table->add_read_columns_used_by_index(query_plan.index); else table->use_all_columns(); @@ -534,22 +571,22 @@ int mysql_update(THD *thd, /* When we get here, we have one of the following options: - A. used_index == MAX_KEY + A. query_plan.index == MAX_KEY This means we should use full table scan, and start it with init_read_record call - B. used_index != MAX_KEY + B. query_plan.index != MAX_KEY B.1 quick select is used, start the scan with init_read_record B.2 quick select is not used, this is full index scan (with LIMIT) Full index scan must be started with init_read_record_idx */ - if (used_index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) { if (init_read_record(&info, thd, table, select, 0, 1, FALSE)) goto err; } else - init_read_record_idx(&info, thd, table, 1, used_index, reverse); + init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse); thd_proc_info(thd, "Searching rows for update"); ha_rows tmp_limit= limit; @@ -610,6 +647,7 @@ int mysql_update(THD *thd, select= new SQL_SELECT; select->head=table; } + //psergey-todo: disable SHOW EXPLAIN because the plan was deleted? if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)) error=1; /* purecov: inspected */ select->file=tempfile; // Read row ptrs from this file @@ -884,6 +922,7 @@ int mysql_update(THD *thd, if (!transactional_table && updated > 0) thd->transaction.stmt.modified_non_trans_table= TRUE; + thd->apc_target.disable(); //psergey-todo. end_read_record(&info); delete select; thd_proc_info(thd, "end"); @@ -962,6 +1001,27 @@ err: table->disable_keyread(); thd->abort_on_warning= 0; DBUG_RETURN(1); + +exit_without_my_ok: + thd->lex->upd_del_plan= &query_plan; + + select_send *result; + bool printed_anything; + if (!(result= new select_send())) + return 1; /* purecov: inspected */ + List<Item> 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); + DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0); } /* @@ -1381,23 +1441,37 @@ 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 (!(*result= new multi_update(table_list, - &thd->lex->select_lex.leaf_tables, - fields, values, - handle_duplicates, ignore))) + + if (explain) { - DBUG_RETURN(TRUE); + /* 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; } thd->abort_on_warning= test(thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); -/* - psergey-explain-todo: - This is the place where EXPLAIN <multi-table-update> should be handled. -*/ List<Item> total_list; res= mysql_select(thd, &select_lex->ref_pointer_array, @@ -1407,12 +1481,20 @@ bool mysql_multi_update(THD *thd, (ORDER *)NULL, options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); + output, unit, select_lex); DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); res|= thd->is_error(); if (unlikely(res)) (*result)->abort_result_set(); + else + { + if (explain) + { + output->send_eof(); + delete output; + } + } thd->abort_on_warning= 0; DBUG_RETURN(res); } |