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

Sergey Petrunya psergey at askmonty.org
Tue Feb 12 06:20:16 EET 2013


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

------------------------------------------------------------
revno: 3499
revision-id: psergey at askmonty.org-20130212042014-7i5i712qajnesbgc
parent: elenst at ubuntu11.home-20130207222123-ofulkpduv0jnleth
committer: Sergey Petrunya <psergey at askmonty.org>
branch nick: 10.0-base-explain-update-r2
timestamp: Tue 2013-02-12 08:20:14 +0400
message:
  EXPLAIN DELETE for MariaDB
  - Backported the code to 10.0-base
  - Removed incorrect assert
=== modified file 'mysql-test/r/show_explain.result'
--- a/mysql-test/r/show_explain.result	2013-01-23 15:16:14 +0000
+++ b/mysql-test/r/show_explain.result	2013-02-12 04:20:14 +0000
@@ -192,16 +192,24 @@
 drop table t2;
 set debug_dbug=@old_debug;
 #
-# Attempt SHOW EXPLAIN for a DELETE
+# Attempt SHOW EXPLAIN for a DELETE (UPD: now works)
 # 
 create table t2 as select a as a, a as dummy from t0 limit 2;
 set @show_explain_probe_select_id=2;
 set debug_dbug='+d,show_explain_probe_join_exec_start';
 delete from t2 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	delete from t2 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	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Query plan already deleted
+Warnings:
+Note	1003	delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3
 drop table t2;
 set debug_dbug=@old_debug;
 #

=== modified file 'mysql-test/t/show_explain.test'
--- a/mysql-test/t/show_explain.test	2013-01-24 16:24:03 +0000
+++ b/mysql-test/t/show_explain.test	2013-02-12 04:20:14 +0000
@@ -249,7 +249,7 @@
 set debug_dbug=@old_debug;
 
 --echo #
---echo # Attempt SHOW EXPLAIN for a DELETE
+--echo # Attempt SHOW EXPLAIN for a DELETE (UPD: now works)
 --echo # 
 create table t2 as select a as a, a as dummy from t0 limit 2;
 set @show_explain_probe_select_id=2;
@@ -257,10 +257,10 @@
 send delete from t2 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 'sql/my_apc.h'
--- a/sql/my_apc.h	2012-07-17 17:52:08 +0000
+++ b/sql/my_apc.h	2013-02-12 04:20:14 +0000
@@ -64,6 +64,8 @@
   {
     return test(apc_calls);
   }
+
+  inline bool is_enabled() { return enabled; }
   
   /* Functor class for calls you can schedule */
   class Apc_call

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2013-01-23 15:16:14 +0000
+++ b/sql/sql_delete.cc	2013-02-12 04:20:14 +0000
@@ -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          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 @@
   }
   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 @@
     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 @@
     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 @@
   {
     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 @@
     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 @@
 
   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 @@
       {
         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 @@
   {
     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 @@
   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 @@
     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<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);
+  //table->set_keyread(false);
+  DBUG_RETURN((err || thd->is_error() || thd->killed) ? 1 : 0);
 }
 
 

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2013-01-29 14:10:47 +0000
+++ b/sql/sql_lex.cc	2013-02-12 04:20:14 +0000
@@ -448,6 +448,7 @@
 
   lex->thd= lex->unit.thd= thd;
 
+  lex->delete_plan= NULL;
   lex->context_stack.empty();
   lex->unit.init_query();
   lex->unit.init_select();
@@ -2557,6 +2558,7 @@
                          INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
   reset_query_tables_list(TRUE);
   mi.init();
+  delete_plan= NULL;
 }
 
 
@@ -4166,12 +4168,17 @@
   return all_merged;
 }
 
-
-int print_explain_message_line(select_result_sink *result, 
-                               SELECT_LEX *select_lex,
-                               bool on_the_fly,
-                               uint8 options,
-                               const char *message);
+int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
+                       bool *printed_anything)
+{
+  if (delete_plan)
+  {
+    delete_plan->print_explain(output, explain_flags, printed_anything);
+    return 0;
+  }
+  int res= unit.print_explain(output, explain_flags, printed_anything);
+  return res;
+}
 
 
 int st_select_lex::print_explain(select_result_sink *output, 
@@ -4235,8 +4242,9 @@
       DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
       msg= "Query plan already deleted";
     }
-    res= print_explain_message_line(output, this, TRUE /* on_the_fly */,
-                                    0, msg);
+    set_explain_type(TRUE/* on_the_fly */);
+    res= print_explain_message_line(output, 0/*options*/, select_number, type,
+                                    msg);
   }
 err:
   return res;
@@ -4256,9 +4264,10 @@
       EXPLAIN state" error.
     */
     const char *msg="Query plan already deleted";
-    res= print_explain_message_line(output, first, TRUE /* on_the_fly */,
-                                    0, msg);
-    return 0;
+    first->set_explain_type(TRUE/* on_the_fly */);
+    res= print_explain_message_line(output, 0/*options*/, first->select_number,
+                                    first->type, msg);
+    return res;
   }
 
   for (SELECT_LEX *sl= first; sl; sl= sl->next_select())

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2013-01-23 15:18:09 +0000
+++ b/sql/sql_lex.h	2013-02-12 04:20:14 +0000
@@ -2344,6 +2344,48 @@
   LEX *m_lex;
 };
 
+class Delete_plan;
+class SQL_SELECT;
+
+/* Query plan of a single-table DELETE */
+class Delete_plan
+{
+  bool deleting_all_rows;
+  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;
+
+  /* Construction functions */
+  Delete_plan() : 
+    deleting_all_rows(false), impossible_where(false), using_filesort(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) 
+  { 
+    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);
+};
+
+
 /* The state of the lex parsing. This is saved in the THD struct */
 
 struct LEX: public Query_tables_list
@@ -2355,6 +2397,9 @@
   /* list of all SELECT_LEX */
   SELECT_LEX *all_selects_list;
 
+  /* For single-table DELETE: its query plan */
+  Delete_plan *delete_plan;
+
   char *length,*dec,*change;
   LEX_STRING name;
   char *help_arg;
@@ -2769,6 +2814,9 @@
     }
     return FALSE;
   }
+
+  int print_explain(select_result_sink *output, uint8 explain_flags,
+                    bool *printed_anything);
 };
 
 

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2013-01-30 14:11:36 +0000
+++ b/sql/sql_parse.cc	2013-02-12 04:20:14 +0000
@@ -3212,7 +3212,8 @@
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
-    multi_delete *del_result;
+    bool explain= test(lex->describe);
+    select_result *result;
 
     if ((res= multi_delete_precheck(thd, all_tables)))
       break;
@@ -3227,37 +3228,72 @@
     if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
       break;
 
-    MYSQL_MULTI_DELETE_START(thd->query());
+    if (!explain)
+    {
+      MYSQL_MULTI_DELETE_START(thd->query());
+    }
+
     if ((res= mysql_multi_delete_prepare(thd)))
     {
-      MYSQL_MULTI_DELETE_DONE(1, 0);
+      if (!explain)
+      {
+        MYSQL_MULTI_DELETE_DONE(1, 0);
+      }
       goto error;
     }
 
-    if (!thd->is_fatal_error &&
-        (del_result= new multi_delete(aux_tables, lex->table_count)))
+    if (!thd->is_fatal_error)
     {
-      res= mysql_select(thd, &select_lex->ref_pointer_array,
-			select_lex->get_table_list(),
-			select_lex->with_wild,
-			select_lex->item_list,
-			select_lex->where,
-			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
-			(ORDER *)NULL,
-			(select_lex->options | thd->variables.option_bits |
-			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
-                        OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
-			del_result, unit, select_lex);
-      res|= thd->is_error();
-      MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
-      if (res)
-        del_result->abort_result_set();
-      delete del_result;
+      if (explain)
+      {
+        result= new select_send();
+        if (thd->send_explain_fields(result))
+        {
+          delete result;
+          result= NULL;
+        }
+        select_lex->set_explain_type(FALSE);
+      }
+      else
+        result= new multi_delete(aux_tables, lex->table_count);
+
+      if (result)
+      {
+        res= mysql_select(thd, &select_lex->ref_pointer_array,
+                          select_lex->get_table_list(),
+                          select_lex->with_wild,
+                          select_lex->item_list,
+                          select_lex->where,
+                          0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
+                          (ORDER *)NULL,
+                          (select_lex->options | thd->variables.option_bits |
+                          SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
+                          OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
+                          result, unit, select_lex);
+        res|= thd->is_error();
+
+        if (!explain)
+        {
+          MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
+        }
+      
+        if (res)
+          result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
+        else
+        {
+          if (explain)
+            result->send_eof(); 
+        }
+        delete result;
+      }
     }
     else
     {
       res= TRUE;                                // Error
-      MYSQL_MULTI_DELETE_DONE(1, 0);
+      if (!explain)
+      {
+        MYSQL_MULTI_DELETE_DONE(1, 0);
+      }
     }
     break;
   }

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2013-01-29 14:10:47 +0000
+++ b/sql/sql_select.cc	2013-02-12 04:20:14 +0000
@@ -21594,23 +21594,20 @@
 /*
   Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
 */
+
 int print_explain_message_line(select_result_sink *result, 
-                               SELECT_LEX *select_lex,
-                               bool on_the_fly,
                                uint8 options,
+                               uint select_number,
+                               const char *select_type,
                                const char *message)
 {
   const CHARSET_INFO *cs= system_charset_info;
   Item *item_null= new Item_null();
   List<Item> item_list;
 
-  if (on_the_fly)
-    select_lex->set_explain_type(on_the_fly);
-
-  item_list.push_back(new Item_int((int32)
-                                   select_lex->select_number));
-  item_list.push_back(new Item_string(select_lex->type,
-                                      strlen(select_lex->type), cs));
+  item_list.push_back(new Item_int((int32) select_number));
+  item_list.push_back(new Item_string(select_type,
+                                      strlen(select_type), cs));
   for (uint i=0 ; i < 7; i++)
     item_list.push_back(item_null);
   if (options & DESCRIBE_PARTITIONS)
@@ -21626,6 +21623,104 @@
 }
 
 
+/*
+  Make a comma-separated list of possible_keys names and add it into the string
+*/ 
+
+void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line)
+{
+  if (!possible_keys.is_clear_all())
+  {
+    uint j;
+    for (j=0 ; j < table->s->keys ; j++)
+    {
+      if (possible_keys.is_set(j))
+      {
+        if (line->length())
+          line->append(',');
+        line->append(table->key_info[j].name, 
+                     strlen(table->key_info[j].name),
+                     system_charset_info);
+      }
+    }
+  }
+}
+
+/*
+  Print an EXPLAIN output row, based on information provided in the parameters
+
+  @note
+    Parameters that may have NULL value in EXPLAIN output, should be passed
+    (char*)NULL.
+
+  @return 
+    0  - OK
+    1  - OOM Error
+*/
+
+int print_explain_row(select_result_sink *result,
+                      uint8 options,
+                      uint select_number,
+                      const char *select_type,
+                      const char *table_name,
+                      //const char *partitions, (todo)
+                      enum join_type jtype,
+                      const char *possible_keys,
+                      const char *index,
+                      const char *key_len,
+                      const char *ref,
+                      ha_rows rows,
+                      const char *extra)
+{
+  const CHARSET_INFO *cs= system_charset_info;
+  Item *item_null= new Item_null();
+  List<Item> item_list;
+  Item *item;
+
+  item_list.push_back(new Item_int((int32) select_number));
+  item_list.push_back(new Item_string(select_type,
+                                      strlen(select_type), cs));
+  item_list.push_back(new Item_string(table_name,
+                                      strlen(table_name), cs));
+  if (options & DESCRIBE_PARTITIONS)
+    item_list.push_back(item_null); // psergey-todo: produce proper value
+  
+  const char *jtype_str= join_type_str[jtype];
+  item_list.push_back(new Item_string(jtype_str,
+                                      strlen(jtype_str), cs));
+  
+  item= possible_keys? new Item_string(possible_keys, strlen(possible_keys),
+                                      cs) : item_null;
+  item_list.push_back(item);
+  
+  /* 'index */
+  item= index ? new Item_string(index, strlen(index), cs) : item_null;
+  item_list.push_back(item);
+  
+  /* 'key_len */
+  item= key_len ? new Item_string(key_len, strlen(key_len), cs) : item_null;
+  item_list.push_back(item);
+  
+  /* 'ref' */
+  item= ref ? new Item_string(ref, strlen(ref), cs) : item_null;
+  item_list.push_back(item);
+
+  /* 'rows' */
+  item_list.push_back(new Item_int(rows, 
+                                   MY_INT64_NUM_DECIMAL_DIGITS));
+  /* 'filtered' */
+  if (options & DESCRIBE_EXTENDED)
+    item_list.push_back(item_null);
+  
+  /* 'Extra' */
+  item_list.push_back(new Item_string(extra, strlen(extra), cs));
+
+  if (result->send_data(item_list))
+    return 1;
+  return 0;
+}
+
+
 int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
                                SELECT_LEX *select_lex, uint8 explain_flags)
 {
@@ -21705,6 +21800,26 @@
 }
 
 
+/*
+  Append MRR information from quick select to the given string
+*/
+
+void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
+{
+  char mrr_str_buf[128];
+  mrr_str_buf[0]=0;
+  int len;
+  handler *h= quick->head->file;
+  len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf,
+                                        sizeof(mrr_str_buf));
+  if (len > 0)
+  {
+    res->append(STRING_WITH_LEN("; "));
+    res->append(mrr_str_buf, len);
+  }
+}
+
+
 /**
   EXPLAIN handling.
 
@@ -21746,8 +21861,12 @@
   */
   if (message)
   {
-    if (print_explain_message_line(result, join->select_lex, on_the_fly, 
-                                   explain_flags, message))
+    if (on_the_fly)
+      join->select_lex->set_explain_type(on_the_fly);
+
+    if (print_explain_message_line(result, explain_flags,
+                                   join->select_lex->select_number,
+                                   join->select_lex->type, message))
       error= 1;
 
   }
@@ -21762,7 +21881,7 @@
            join->select_lex->master_unit()->derived->is_materialized_derived())
   {
     table_map used_tables=0;
-    //if (!join->select_lex->type)
+
     if (on_the_fly)
       join->select_lex->set_explain_type(on_the_fly);
 
@@ -22171,19 +22290,8 @@
         */
         if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
         {
-          char mrr_str_buf[128];
-          mrr_str_buf[0]=0;
-          int len;
-          uint mrr_flags= 
-            ((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags;
-          len= table->file->multi_range_read_explain_info(mrr_flags,
-                                                          mrr_str_buf,
-                                                          sizeof(mrr_str_buf));
-          if (len > 0)
-          {
-            extra.append(STRING_WITH_LEN("; "));
-            extra.append(mrr_str_buf, len);
-          }
+          explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
+                                  &extra);
         }
 
 	if (need_tmp_table)

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2012-12-17 00:49:19 +0000
+++ b/sql/sql_select.h	2013-02-12 04:20:14 +0000
@@ -1804,6 +1804,28 @@
 /* Index Condition Pushdown entry point function */
 void push_index_cond(JOIN_TAB *tab, uint keyno);
 
+/* EXPLAIN-related utility functions */
+int print_explain_message_line(select_result_sink *result, 
+                               uint8 options,
+                               uint select_number,
+                               const char *select_type,
+                               const char *message);
+void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
+int print_explain_row(select_result_sink *result,
+                      uint8 options,
+                      uint select_number,
+                      const char *select_type,
+                      const char *table_name,
+                      //const char *partitions, (todo)
+                      enum join_type jtype,
+                      const char *possible_keys,
+                      const char *index,
+                      const char *key_len,
+                      const char *ref,
+                      ha_rows rows,
+                      const char *extra);
+void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
+
 /****************************************************************************
   Temporary table support for SQL Runtime
  ***************************************************************************/

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2013-01-29 14:10:47 +0000
+++ b/sql/sql_show.cc	2013-02-12 04:20:14 +0000
@@ -2337,8 +2337,8 @@
 
   DBUG_ASSERT(current_thd == target_thd);
   set_current_thd(request_thd);
-  if (target_thd->lex->unit.print_explain(explain_buf, 0 /* explain flags*/,
-                                          &printed_anything))
+  if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
+                                     &printed_anything))
   {
     failed_to_produce= TRUE;
   }

=== modified file 'sql/sql_string.h'
--- a/sql/sql_string.h	2013-01-23 15:16:14 +0000
+++ b/sql/sql_string.h	2013-02-12 04:20:14 +0000
@@ -500,6 +500,38 @@
   }
 };
 
+
+// The following class is a backport from MySQL 5.6:
+/**
+  String class wrapper with a preallocated buffer of size buff_sz
+
+  This class allows to replace sequences of:
+     char buff[12345];
+     String str(buff, sizeof(buff));
+     str.length(0);
+  with a simple equivalent declaration:
+     StringBuffer<12345> str;
+*/
+
+template<size_t buff_sz>
+class StringBuffer : public String
+{
+  char buff[buff_sz];
+
+public:
+  StringBuffer() : String(buff, buff_sz, &my_charset_bin) { length(0); }
+  explicit StringBuffer(const CHARSET_INFO *cs) : String(buff, buff_sz, cs)
+  {
+    length(0);
+  }
+  StringBuffer(const char *str, size_t length, const CHARSET_INFO *cs)
+    : String(buff, buff_sz, cs)
+  {
+    set(str, length, cs);
+  }
+};
+
+
 static inline bool check_if_only_end_space(CHARSET_INFO *cs,
                                            const char *str, 
                                            const char *end)

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2013-01-29 14:10:47 +0000
+++ b/sql/sql_update.cc	2013-02-12 04:20:14 +0000
@@ -1394,7 +1394,10 @@
   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,

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2013-01-29 14:10:47 +0000
+++ b/sql/sql_yacc.yy	2013-02-12 04:20:14 +0000
@@ -11881,13 +11881,21 @@
           opt_describe_column {}
         | describe_command opt_extended_describe
           { Lex->describe|= DESCRIBE_NORMAL; }
-          select
+          explanable_command
           {
             LEX *lex=Lex;
             lex->select_lex.options|= SELECT_DESCRIBE;
           }
         ;
 
+explanable_command:
+          select
+        | insert
+        | replace
+        | update
+        | delete
+        ;
+
 describe_command:
           DESC
         | DESCRIBE



More information about the commits mailing list