[Commits] Rev 3501: SHOW EXPLAIN for MariaDB in file:///data0/psergey/dev2/10.0-base-explain-update-r2/

Sergey Petrunya psergey at askmonty.org
Tue Feb 12 12:37:09 EET 2013


At file:///data0/psergey/dev2/10.0-base-explain-update-r2/

------------------------------------------------------------
revno: 3501
revision-id: psergey at askmonty.org-20130212103708-cs0anflhpw4weqgx
parent: psergey at askmonty.org-20130212042448-zwl8hexailedt6pv
committer: Sergey Petrunya <psergey at askmonty.org>
branch nick: 10.0-base-explain-update-r2
timestamp: Tue 2013-02-12 14:37:08 +0400
message:
  SHOW EXPLAIN for MariaDB
  - Support [SHOW] EXPLAIN UPDATE (needs code cleanup).
=== modified file 'mysql-test/r/explain_non_select.result'
--- a/mysql-test/r/explain_non_select.result	2013-02-12 04:24:48 +0000
+++ b/mysql-test/r/explain_non_select.result	2013-02-12 10:37:08 +0000
@@ -38,7 +38,7 @@
 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 @@
 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;

=== modified file 'mysql-test/r/show_explain.result'
--- a/mysql-test/r/show_explain.result	2013-02-12 04:20:14 +0000
+++ b/mysql-test/r/show_explain.result	2013-02-12 10:37:08 +0000
@@ -186,9 +186,17 @@
 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 @@
 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 @@
 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

=== modified file 'mysql-test/r/show_explain_non_select.result'
--- a/mysql-test/r/show_explain_non_select.result	2013-02-12 04:24:48 +0000
+++ b/mysql-test/r/show_explain_non_select.result	2013-02-12 10:37:08 +0000
@@ -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 @@
 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';

=== modified file 'mysql-test/t/explain_non_select.test'
--- a/mysql-test/t/explain_non_select.test	2013-02-12 04:24:48 +0000
+++ b/mysql-test/t/explain_non_select.test	2013-02-12 10:37:08 +0000
@@ -43,5 +43,44 @@
 --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;

=== modified file 'mysql-test/t/show_explain.test'
--- a/mysql-test/t/show_explain.test	2013-02-12 04:20:14 +0000
+++ b/mysql-test/t/show_explain.test	2013-02-12 10:37:08 +0000
@@ -238,10 +238,10 @@
 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;

=== modified file 'mysql-test/t/show_explain_non_select.test'
--- a/mysql-test/t/show_explain_non_select.test	2013-02-12 04:24:48 +0000
+++ b/mysql-test/t/show_explain_non_select.test	2013-02-12 10:37:08 +0000
@@ -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 @@
 
 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 @@
 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 @@
 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';

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2013-02-12 04:20:14 +0000
+++ b/sql/sql_delete.cc	2013-02-12 10:37:08 +0000
@@ -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)
-  {
-    const char *msg= deleting_all_rows? "Deleting all rows": "Impossible where";
+  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;
+    }
+    *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 @@
   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 @@
   
   /* 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;

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2013-02-12 04:20:14 +0000
+++ b/sql/sql_lex.cc	2013-02-12 10:37:08 +0000
@@ -448,7 +448,7 @@
 
   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 @@
                          INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
   reset_query_tables_list(TRUE);
   mi.init();
-  delete_plan= NULL;
+  upd_del_plan= NULL;
 }
 
 
@@ -4171,9 +4171,9 @@
 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);

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2013-02-12 04:20:14 +0000
+++ b/sql/sql_lex.h	2013-02-12 10:37:08 +0000
@@ -2347,29 +2347,48 @@
 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 @@
     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 @@
   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;

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2013-02-12 04:20:14 +0000
+++ b/sql/sql_select.cc	2013-02-12 10:37:08 +0000
@@ -10756,7 +10756,8 @@
   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)
   {

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2013-02-12 04:20:14 +0000
+++ b/sql/sql_update.cc	2013-02-12 10:37:08 +0000
@@ -260,7 +260,7 @@
   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 @@
   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 @@
   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 @@
     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 @@
   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 @@
   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 @@
     {
       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 @@
     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 @@
 
       /*
         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 @@
 	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 @@
   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 @@
   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 @@
                         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)))
-  {
-    DBUG_RETURN(TRUE);
+  
+  if (explain)
+  {
+    /* 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 @@
                     (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);
 }



More information about the commits mailing list