List:Commits« Previous MessageNext Message »
From:Sergey Petrunia Date:March 21 2009 6:47pm
Subject:bzr commit into mysql-6.0-opt branch (sergefp:2731) WL#4800
View as plain text  
#At file:///home/spetrunia/dev/mysql-6.0-optimizer-trace-r2/ based on revid:sergefp@stripped

 2731 Sergey Petrunia	2009-03-21
      WL#4800: Optimizer trace
      - more trace statements (trace/DBUG distinction still a mess)
      - moved trace code into a separate file
      - fixed INFORMATION_SCHEMA support
      - Siccint WHERE clause printout
added:
  sql/opt_trace.cc
  sql/opt_trace.h
modified:
  .bzrignore
  libmysqld/Makefile.am
  sql/Makefile.am
  sql/handler.h
  sql/item.cc
  sql/item_cmpfunc.cc
  sql/item_cmpfunc.h
  sql/item_subselect.cc
  sql/mysql_priv.h
  sql/opt_range.cc
  sql/sql_parse.cc
  sql/sql_select.cc
  sql/sql_select.h
  sql/sql_test.cc

per-file messages:
  .bzrignore
    WL#4800: Optimizer trace
    - moved trace code into a separate file
  libmysqld/Makefile.am
    WL#4800: Optimizer trace
    - moved trace code into a separate file
  sql/Makefile.am
    WL#4800: Optimizer trace
    - moved trace code into a separate file
  sql/handler.h
    WL#4800: Optimizer trace
    - Fix INFORMATION_SCHEMA support
  sql/item.cc
    WL#4800: Optimizer trace
    - Siccint WHERE clause printout
  sql/item_cmpfunc.cc
    WL#4800: Optimizer trace
    - Siccint WHERE clause printout
  sql/item_cmpfunc.h
    WL#4800: Optimizer trace
    - Siccint WHERE clause printout
  sql/item_subselect.cc
    - Siccint WHERE clause printout
  sql/mysql_priv.h
    WL#4800: Optimizer trace
    - Siccint WHERE clause printout
    - moved trace code into a separate file
  sql/opt_range.cc
    WL#4800: Optimizer trace
    - more trace statements (trace/DBUG distinction still a mess)
  sql/opt_trace.cc
    WL#4800: Optimizer trace
    - moved trace code into a separate file
  sql/opt_trace.h
    WL#4800: Optimizer trace
    - moved trace code into a separate file
  sql/sql_parse.cc
    WL#4800: Optimizer trace
    - fixed INFORMATION_SCHEMA support
  sql/sql_select.cc
    WL#4800: Optimizer trace
    - more trace statements (trace/DBUG distinction still a mess)
    - moved trace code into a separate file
  sql/sql_select.h
    WL#4800: Optimizer trace
    - Siccint WHERE clause printout
  sql/sql_test.cc
    WL#4800: Optimizer trace
    - more trace statements (trace/DBUG distinction still a mess)
=== modified file '.bzrignore'
--- a/.bzrignore	2009-03-09 12:17:41 +0000
+++ b/.bzrignore	2009-03-21 18:46:45 +0000
@@ -2016,3 +2016,4 @@ unittest/tmp
 libmysqld/sql_join_cache.cc
 storage/maria/maria_non_trans_log
 libmysqld/examples/mysqltest.cc
+libmysqld/opt_trace.cc

=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am	2009-02-23 14:53:18 +0000
+++ b/libmysqld/Makefile.am	2009-03-21 18:46:45 +0000
@@ -61,7 +61,7 @@ sqlsources = derror.cc field.cc field_co
 	key.cc lock.cc log.cc sql_state.c \
 	log_event.cc rpl_record.cc \
 	log_event_old.cc rpl_record_old.cc \
-	protocol.cc net_serv.cc opt_range.cc \
+	protocol.cc net_serv.cc opt_range.cc opt_trace.cc \
 	opt_sum.cc procedure.cc records.cc sql_acl.cc \
 	sql_load.cc discover.cc sql_locale.cc \
 	sql_profile.cc \

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2009-02-23 14:53:18 +0000
+++ b/sql/Makefile.am	2009-03-21 18:46:45 +0000
@@ -81,7 +81,7 @@ noinst_HEADERS =	item.h item_func.h item
 			ha_ndbcluster_connection.h ha_ndbcluster_connection.h \
 			ha_ndbcluster_lock_ext.h \
 			ha_partition.h rpl_constants.h \
-			opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
+			opt_range.h opt_trace.h protocol.h rpl_tblmap.h rpl_utility.h \
 			rpl_reporting.h \
 			log.h sql_show.h rpl_rli.h rpl_mi.h \
 			sql_select.h structs.h table.h sql_udf.h hash_filo.h \
@@ -127,8 +127,8 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
 			unireg.cc des_key_file.cc \
 			log_event.cc rpl_record.cc \
 			log_event_old.cc rpl_record_old.cc \
-			discover.cc time.cc opt_range.cc opt_sum.cc \
-		   	records.cc filesort.cc handler.cc \
+			discover.cc time.cc opt_range.cc opt_trace.cc \
+                        opt_sum.cc records.cc filesort.cc handler.cc \
 		        ha_partition.cc \
 			debug_sync.cc \
 			sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \

=== modified file 'sql/handler.h'
--- a/sql/handler.h	2009-02-13 16:30:54 +0000
+++ b/sql/handler.h	2009-03-21 18:46:45 +0000
@@ -598,6 +598,7 @@ enum enum_schema_tables
   SCH_GLOBAL_VARIABLES,
   SCH_KEY_COLUMN_USAGE,
   SCH_OPEN_TABLES,
+  SCH_OPTIMIZER_TRACE,
   SCH_PARAMETERS,
   SCH_PARTITIONS,
   SCH_PLUGINS,

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-02-20 12:37:37 +0000
+++ b/sql/item.cc	2009-03-21 18:46:45 +0000
@@ -1963,7 +1963,14 @@ void Item_ident::print(String *str, enum
     append_identifier(thd, str, nm, (uint) strlen(nm));
     return;
   }
-  if (db_name && db_name[0] && !alias_name_used)
+  
+  ulonglong save_options= thd->options;
+  if (query_type == QT_SICCINT)
+    thd->options&= ~OPTION_QUOTE_SHOW_CREATE;
+
+  if (db_name && db_name[0] && !alias_name_used &&
+      !((query_type == QT_SICCINT) && !my_strcasecmp(system_charset_info,
+                                                     db_name, thd->db)))
   {
     if (!(cached_table && cached_table->belong_to_view &&
           cached_table->belong_to_view->compact_view_format))
@@ -1986,6 +1993,7 @@ void Item_ident::print(String *str, enum
     else
       append_identifier(thd, str, field_name, (uint) strlen(field_name));
   }
+  thd->options= save_options;
 }
 
 /* ARGSUSED */

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2009-02-19 14:48:28 +0000
+++ b/sql/item_cmpfunc.cc	2009-03-21 18:46:45 +0000
@@ -5448,3 +5448,26 @@ void Item_equal::print(String *str, enum
   str->append(')');
 }
 
+void Item_func_trig_cond::print(String *str, enum_query_type query_type)
+{
+  if (query_type == QT_SICCINT && trig_tab)
+  {
+    /*
+      Print "IF(have_match(t1..tN), cond)" where t1, tN are the first and the 
+      last inner tables.
+    */
+    str->append("IF(have_match(");
+    str->append(trig_tab->table->alias);
+    if (trig_tab->last_inner != trig_tab)
+    {
+      str->append("..");
+      str->append(trig_tab->last_inner->table->alias);
+    }
+    str->append(", ");
+    args[0]->print(str, query_type);
+    str->append(')');
+  }
+  else
+    Item_bool_func::print(str, query_type);
+}
+

=== modified file 'sql/item_cmpfunc.h'
--- a/sql/item_cmpfunc.h	2009-01-26 16:03:39 +0000
+++ b/sql/item_cmpfunc.h	2009-03-21 18:46:45 +0000
@@ -378,7 +378,7 @@ public:
 };
 
 class Item_maxmin_subselect;
-
+struct st_join_table;
 /*
   trigcond<param>(arg) ::= param? arg : TRUE
 
@@ -412,7 +412,16 @@ class Item_func_trig_cond: public Item_b
 {
   bool *trig_var;
 public:
-  Item_func_trig_cond(Item *a, bool *f) : Item_bool_func(a) { trig_var= f; }
+  Item_func_trig_cond(Item *a, bool *f, struct st_join_table *tab) : 
+    Item_bool_func(a), trig_var(f), trig_tab(tab), is_on_expr(FALSE)
+  {}
+  Item_func_trig_cond(Item *a, bool *f, bool is_on_expr_arg) : 
+    Item_bool_func(a), trig_var(f), trig_tab(NULL),
+    is_on_expr(is_on_expr_arg)
+  {}
+  Item_func_trig_cond(Item *a, bool *f) : 
+    Item_bool_func(a), trig_var(f), trig_tab(NULL), is_on_expr(FALSE)
+  {}
   longlong val_int() { return *trig_var ? args[0]->val_int() : 1; }
   enum Functype functype() const { return TRIG_COND_FUNC; };
   const char *func_name() const { return "trigcond"; };
@@ -420,8 +429,17 @@ public:
   bool *get_trig_var() { return trig_var; }
   /* The following is needed for ICP: */
   table_map used_tables() const { return args[0]->used_tables(); }
+  void print(String *str, enum_query_type query_type);
+
+  /* The following is for EXPLAIN CONDS. */
+  /* != NULL means this is an outer-join-triggered WHERE clause */
+  struct st_join_table *trig_tab;
+  bool is_on_expr;
+  bool is_outer_join_on_expr() { return is_on_expr; }
+  bool is_outer_join_where_cond() { return test(trig_tab); }
 };
 
+
 class Item_func_not_all :public Item_func_not
 {
   /* allow to check presence of values in max/min optimization */

=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc	2009-01-08 19:06:44 +0000
+++ b/sql/item_subselect.cc	2009-03-21 18:46:45 +0000
@@ -2780,13 +2780,29 @@ table_map subselect_union_engine::upper_
 void subselect_single_select_engine::print(String *str,
                                            enum_query_type query_type)
 {
-  select_lex->print(thd, str, query_type);
+  if (query_type == QT_SICCINT)
+  {
+    char buf[32]; // enough for uint
+    my_snprintf(buf, sizeof(buf), "%u", select_lex->select_number);
+    str->append(STRING_WITH_LEN("select #"));
+    str->append(buf, strlen(buf));
+  }
+  else
+    select_lex->print(thd, str, query_type);
 }
 
 
 void subselect_union_engine::print(String *str, enum_query_type query_type)
 {
-  unit->print(str, query_type);
+  if (query_type == QT_SICCINT)
+  {
+    char union_name[NAME_LEN];
+    uint len= create_union_table_name(unit, union_name);
+    str->append(STRING_WITH_LEN("select #"));
+    str->append(union_name, len);
+  }
+  else
+    unit->print(str, query_type);
 }
 
 

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2009-03-20 14:01:11 +0000
+++ b/sql/mysql_priv.h	2009-03-21 18:46:45 +0000
@@ -70,7 +70,8 @@ class Parser_state;
 enum enum_query_type
 {
   QT_ORDINARY,
-  QT_IS
+  QT_IS,
+  QT_SICCINT
 };
 
 /* TODO convert all these three maps to Bitmap classes */
@@ -1814,12 +1815,13 @@ bool mysql_manager_submit(void (*action)
 
 /* sql_test.cc */
 #ifndef DBUG_OFF
+class Optimizer_trace;
 void print_where(COND *cond,const char *info, enum_query_type query_type);
 void print_cached_tables(void);
 void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
 void print_plan(JOIN* join,uint idx, double record_count, double read_time,
                 double current_read_time, const char *info);
-void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array);
+void print_keyuse_array(Optimizer_trace*, DYNAMIC_ARRAY *keyuse_array);
 void print_sjm(SJ_MATERIALIZATION_INFO *sjm);
 #define EXTRA_DEBUG_DUMP_TABLE_LISTS
 #ifdef EXTRA_DEBUG_DUMP_TABLE_LISTS
@@ -2577,115 +2579,7 @@ bool load_collation(MEM_ROOT *mem_root,
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
-/**************************************************************************
- * Optimizer trace starts 
- **************************************************************************/
-//psergey-trace:
-#define OPTIMIZER_TRACE 1
-
-#ifdef OPTIMIZER_TRACE
-
-/*
-  A macro to be use 
-    OPT_TRACE(thd, start_list("select"));
-    OPT_TRACE(thd, pop("select").);
-
-    OPT_TRACE(thd, print("AAA").);
-  
-
-  if (thd->opt_trace) 
-  {
-    thd->opt_trace->add("name", value);
-  }
-
-  if (thd->opt_trace) 
-  {
-    thd->opt_trace->add("name", value).add("othername", value2);
-  }
-*/
-
-// TODO : only have one check within unlikely(...)
-# define OPT_TRACE_HERE(thd, x) if (unlikely(thd_opt_trace && \
-                                        thd_opt_trace->is_started())) \
-                           { \
-                             thd->opt_trace->x; \
-                           }
-
-# define OPT_TRACE(thd, x) if (unlikely(thd->opt_trace && \
-                                        thd->opt_trace->is_started())) \
-                           { \
-                             thd->opt_trace->x; \
-                           }
-
-# define OPT_IF_TRACE(thd, x) if (unlikely(thd->opt_trace && \
-                                           thd->opt_trace->is_started())) \
-                              { \
-                                x \
-                              }
-
-#define OPT_TRACE_DEBUG 1
-
-bool opt_trace_start(THD *thd);
-void opt_trace_end(THD *thd, bool started);
-
-/*
-  All optimizer trace elements here.
-*/
-class Optimizer_trace 
-{
-public:
-  Optimizer_trace(): depth(0), started(FALSE) {}
-
-  Optimizer_trace& start_list(const char *name);
-  Optimizer_trace& end_list(const char *name);
-
-  Optimizer_trace& start_object(const char *name);
-  Optimizer_trace& end_object(const char *name);
-  
-  /* Methods to add named scalar objects */
-  Optimizer_trace& add(const char *name, const char *val);
-  Optimizer_trace& add(const char *name, longlong val);
-  Optimizer_trace& add(const char *name, ulonglong val);
-
-  /* Methods to add unnamed scalar objects */
-  Optimizer_trace& add(const char *val);
-  Optimizer_trace& add(longlong val);
-  Optimizer_trace& add(ulonglong val);
-  
-  void clear();
-  void start();
-  void end();
-
-  bool is_started() { return started; }
-
-  String& get_str() { return trace; }
-
-//supposed-to-be-private:
-  void push();
-  void pop();
-  void separator();
-  void next_line();
-
-private:
-  /* Yes we're that simple for now */
-  String trace;
-  Dynamic_array<int> first;
-
-  int depth;
-  bool started;
-  
-  //TODO: if debug is used, a stack of tags
-};
-
-
-#else
-
-# define OPT_TRACE(thd, x) 
-# define OPT_IF_TRACE(thd, x) 
-
-#endif
-/**************************************************************************/
-
+#include "opt_trace.h"
 
 #endif /* MYSQL_SERVER */
 extern "C" int test_if_data_home_dir(const char *dir);

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2009-03-20 14:01:11 +0000
+++ b/sql/opt_range.cc	2009-03-21 18:46:45 +0000
@@ -771,6 +771,10 @@ static void print_ror_scans_arr(TABLE *t
                                 struct st_ror_scan_info **start,
                                 struct st_ror_scan_info **end);
 static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg);
+static void print_range(Optimizer_trace *out, KEY_PART_INFO *key_parts, 
+                        const uchar *min_key, uint16 min_length,
+                        const uchar *max_key, uint16 max_length, 
+                        uint flag);
 #endif
 
 static SEL_TREE *tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
@@ -2276,7 +2280,7 @@ int SQL_SELECT::test_quick_select(THD *t
   DBUG_PRINT("enter",("keys_to_use: %lu  prev_tables: %lu  const_tables: %lu",
 		      (ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
 		      (ulong) const_tables));
-  DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records));
+  //DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records));
   delete quick;
   quick=0;
   needed_reg.clear_all();
@@ -2295,7 +2299,12 @@ int SQL_SELECT::test_quick_select(THD *t
   else if (read_time <= 2.0 && !force_quick_range)
     DBUG_RETURN(0);				/* No need for quick select */
 
-  DBUG_PRINT("info",("Time to scan table: %g", read_time));
+  //DBUG_PRINT("info",("Time to scan table: %g", read_time));
+  OPT_TRACE(thd, 
+    start_object("table_scan").
+    add("records", head->file->stats.records).
+    add("cost", read_time).
+    end_object("table_scan"));
   
   keys_to_use.intersect(head->keys_in_use_for_query);
   if (!keys_to_use.is_clear_all())
@@ -2390,10 +2399,20 @@ int SQL_SELECT::test_quick_select(THD *t
         param.table->file->index_only_read_time(key_for_use, 
                                                 rows2double(records)) +
         (double) records / TIME_FOR_COMPARE;
-      DBUG_PRINT("info",  ("'all'+'using index' scan will be using key %d, "
-                           "read time %g", key_for_use, key_read_time));
+
+      bool picked= FALSE;
       if (key_read_time < read_time)
+      {
         read_time= key_read_time;
+        picked= TRUE;
+      }
+
+      OPT_TRACE(thd, 
+        start_object("best_covering_scan").
+        add_str("index", head->key_info[key_for_use].name).
+        add("cost", key_read_time).
+        add("picked", picked).
+        end_object("best_covering_scan"));
     }
 
     TABLE_READ_PLAN *best_trp= NULL;
@@ -4824,6 +4843,8 @@ static TRP_RANGE *get_key_scans_params(P
   */
   DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map,
                                       "tree scans"););
+  OPT_TRACE(param->thd, start_object("get_key_scans_params"));
+  
   tree->ror_scans_map.clear_all();
   tree->n_ror_scans= 0;
   for (idx= 0,key=tree->keys, end=key+param->keys; key != end; key++,idx++)
@@ -4842,23 +4863,37 @@ static TRP_RANGE *get_key_scans_params(P
       bool read_index_only= index_read_must_be_used || 
                             param->table->covering_keys.is_set(keynr);
 
+      OPT_TRACE(param->thd, start_object("range_scan").
+                     add_str("key", param->table->key_info[keynr].name).
+                     start_list("ranges"));
       found_records= check_quick_select(param, idx, read_index_only, *key,
                                         update_tbl_stats, &mrr_flags,
                                         &buf_size, &cost);
       found_read_time= cost.total_cost();
+      OPT_TRACE(param->thd, end_list("ranges").
+                             add_ll("index_only", read_index_only).
+                            add("rows", found_records).
+                            add("cost", found_read_time));
+
       if ((found_records != HA_POS_ERROR) && param->is_ror_scan)
       {
+        OPT_TRACE(param->thd, add("rowid_ordered", 1));
         tree->n_ror_scans++;
         tree->ror_scans_map.set_bit(idx);
       }
+      else
+        OPT_TRACE(param->thd, add("rowid_ordered", 0));
+
       if (found_records != HA_POS_ERROR && read_time > found_read_time)
       {
+        OPT_TRACE(param->thd, add("best_so_far", 1));
         read_time=    found_read_time;
         best_records= found_records;
         key_to_read=  key;
         best_mrr_flags= mrr_flags;
         best_buf_size=  buf_size;
       }
+      OPT_TRACE(param->thd, end_object("range_scan"));
     }
   }
 
@@ -4874,15 +4909,18 @@ static TRP_RANGE *get_key_scans_params(P
       read_plan->is_ror= tree->ror_scans_map.is_set(idx);
       read_plan->read_cost= read_time;
       read_plan->mrr_buf_size= best_buf_size;
-      DBUG_PRINT("info",
-                 ("Returning range plan for key %s, cost %g, records %lu",
-                  param->table->key_info[param->real_keynr[idx]].name,
-                  read_plan->read_cost, (ulong) read_plan->records));
+      OPT_TRACE(param->thd, add_str("best_range_access", 
+                                     param->table->key_info[param->real_keynr[idx]].name));
+      //DBUG_PRINT("info",
+      //           ("Returning range plan for key %s, cost %g, records %lu",
+      //            param->table->key_info[param->real_keynr[idx]].name,
+      //            read_plan->read_cost, (ulong) read_plan->records));
     }
   }
   else
     DBUG_PRINT("info", ("No 'range' table read plan found"));
 
+  OPT_TRACE(param->thd, end_object("get_key_scans_params"));
   DBUG_RETURN(read_plan);
 }
 
@@ -7443,7 +7481,6 @@ walk_up_n_right:
   /* Ok got a tuple */
   RANGE_SEQ_ENTRY *cur= &seq->stack[seq->i];
   uint min_key_length= cur->min_key - seq->param->min_key;
-  
   range->ptr= (char*)(int)(key_tree->part);
   if (cur->min_key_flag & GEOM_FLAG)
   {
@@ -7496,17 +7533,21 @@ walk_up_n_right:
         seq->param->is_ror_scan= FALSE;
     }
   }
+  OPT_IF_TRACE(seq->param->thd,
+               print_range(trace,
+                           seq->param->table->key_info[seq->real_keyno].key_part,
+                           range->start_key.key,
+                           range->start_key.length,
+                           range->end_key.key,
+                           range->end_key.length,
+                           cur->min_key_flag | cur->max_key_flag));
+
   seq->param->range_count++;
   seq->param->max_key_part=max(seq->param->max_key_part,key_tree->part);
   return 0;
 }
 
-#ifdef OPTIMIZER_TRACE
-static void dump_range()
-{
-  ;
-}
-#endif
+
 /*
   Calculate cost and E(#rows) for a given index and intervals tree 
 
@@ -11271,27 +11312,23 @@ static void print_ror_scans_arr(TABLE *t
 ** This should be changed to use a String to store each row instead
 ** of locking the DEBUG stream !
 *****************************************************************************/
-class Append_dbug
-{
-public:
-  void write(const char *str, uint len)
-  {
-    fwrite(str, sizeof(char), len, DBUG_FILE);
-  }
-};
+
+
 
 /*
-  Print a key into debug stream
+  Print a key into a stream
 
   SYNOPSIS
     key_part     Index components description
     key          Key tuple
     used_length  Key tuple length
+
+  psergey-todo: change out to be an 'appender'.
 */
 
-template <typename T>
-void
-print_key_inner(T *out, KEY_PART *key_part, const uchar *key, uint used_length)
+static void
+print_key2(Optimizer_trace *out, KEY_PART_INFO *key_part, const uchar *key, 
+           uint used_length)
 {
   char buff[1024];
   const uchar *key_end= key+used_length;
@@ -11330,10 +11367,32 @@ print_key_inner(T *out, KEY_PART *key_pa
 }
 
 
-static void print_key(KEY_PART *key_part, const uchar *key, uint used_length)
-{;
- // Append_dbug dummy;
- // print_key_inner<Append_dbug>(&dummy, key_part, key, used_length);
+static void print_range(Optimizer_trace *out, KEY_PART_INFO *key_parts, 
+                        const uchar *min_key, uint16 min_length,
+                        const uchar *max_key, uint16 max_length, 
+                        uint flag)
+{
+  out->start_write(NULL);
+  if (!(flag & NO_MIN_RANGE))
+  {
+    print_key2(out, key_parts, min_key, min_length);
+    if (flag & NEAR_MIN)
+      out->write(" < ");
+    else
+      out->write(" <= ");
+  }
+
+  out->write("X");
+
+  if (!(flag & NO_MAX_RANGE))
+  {
+    if (flag & NEAR_MAX)
+      out->write(" < ");
+    else
+      out->write(" <= ");
+    print_key2(out, key_parts, max_key, max_length);
+  }
+  out->end_write();
 }
 
 
@@ -11361,85 +11420,76 @@ static void print_quick(QUICK_SELECT_I *
 
 void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
 {
+  Optimizer_trace *out= current_thd->opt_trace;
   /* purecov: begin inspected */
-  fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n",
-	  indent, "", head->key_info[index].name, max_used_key_length);
+  out->start_object("range_select");
+  out->add_str("index", head->key_info[index].name);
+  out->add_ull("key_length", max_used_key_length);
 
-  if (verbose)
+  out->start_list("ranges");
+  if (TRUE)
   {
     QUICK_RANGE *range;
     QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer;
     QUICK_RANGE **end_range= pr + ranges.elements;
     for (; pr != end_range; ++pr)
     {
-      fprintf(DBUG_FILE, "%*s", indent + 2, "");
       range= *pr;
-      if (!(range->flag & NO_MIN_RANGE))
-      {
-        print_key(key_parts, range->min_key, range->min_length);
-        if (range->flag & NEAR_MIN)
-	  fputs(" < ",DBUG_FILE);
-        else
-	  fputs(" <= ",DBUG_FILE);
-      }
-      fputs("X",DBUG_FILE);
-
-      if (!(range->flag & NO_MAX_RANGE))
-      {
-        if (range->flag & NEAR_MAX)
-	  fputs(" < ",DBUG_FILE);
-        else
-	  fputs(" <= ",DBUG_FILE);
-        print_key(key_parts, range->max_key, range->max_length);
-      }
-      fputs("\n",DBUG_FILE);
+      print_range(out, head->key_info[index].key_part,
+                  range->min_key, range->min_length, 
+                  range->max_key, range->max_length,
+                  range->flag);
     }
   }
+  out->end_list("ranges");
+  out->end_object("range_select");
   /* purecov: end */    
 }
 
 void QUICK_INDEX_MERGE_SELECT::dbug_dump(int indent, bool verbose)
 {
+  Optimizer_trace *out= current_thd->opt_trace;
   List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
   QUICK_RANGE_SELECT *quick;
-  fprintf(DBUG_FILE, "%*squick index_merge select\n", indent, "");
-  fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+  /* purecov: begin inspected */
+  out->start_object("index_merge-sort-union");
   while ((quick= it++))
     quick->dbug_dump(indent+2, verbose);
   if (pk_quick_select)
   {
-    fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, "");
+    out->start_object("clustered-pk-scan");
     pk_quick_select->dbug_dump(indent+2, verbose);
+    out->end_object("clustered-pk-scan");
   }
-  fprintf(DBUG_FILE, "%*s}\n", indent, "");
+  out->end_object("index_merge-sort-union");
 }
 
 void QUICK_ROR_INTERSECT_SELECT::dbug_dump(int indent, bool verbose)
 {
+  Optimizer_trace *out= current_thd->opt_trace;
   List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
   QUICK_RANGE_SELECT *quick;
-  fprintf(DBUG_FILE, "%*squick ROR-intersect select, %scovering\n",
-          indent, "", need_to_fetch_row? "":"non-");
-  fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+  out->start_object("index_merge-intersect");
   while ((quick= it++))
     quick->dbug_dump(indent+2, verbose);
   if (cpk_quick)
   {
-    fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, "");
+    out->start_object("clustered-pk-scan");
     cpk_quick->dbug_dump(indent+2, verbose);
+    out->end_object("clustered-pk-scan");
   }
-  fprintf(DBUG_FILE, "%*s}\n", indent, "");
+  out->end_object("index_merge-intersect");
 }
 
 void QUICK_ROR_UNION_SELECT::dbug_dump(int indent, bool verbose)
 {
+  Optimizer_trace *out= current_thd->opt_trace;
   List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
   QUICK_SELECT_I *quick;
-  fprintf(DBUG_FILE, "%*squick ROR-union select\n", indent, "");
-  fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+  out->start_object("sort_union-intersect");
   while ((quick= it++))
     quick->dbug_dump(indent+2, verbose);
-  fprintf(DBUG_FILE, "%*s}\n", indent, "");
+  out->end_object("sort_union-intersect");
 }
 
 

=== added file 'sql/opt_trace.cc'
--- a/sql/opt_trace.cc	1970-01-01 00:00:00 +0000
+++ b/sql/opt_trace.cc	2009-03-21 18:46:45 +0000
@@ -0,0 +1,393 @@
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation				// gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+#include "sql_cursor.h"
+
+#include <m_ctype.h>
+#include <my_bit.h>
+
+
+#ifdef OPTIMIZER_TRACE
+
+static const int ident_size=2;
+
+/*
+  General idea about how we make output: we want to produce formatting like
+  this:
+
+   "trace": {
+     "select" : {
+       "query after rewrites": "select ....",
+       "range analysis": {
+         "table": "t1",
+         "indexes": [
+           "key", 
+           "key2"
+         ]
+       },
+       "something-else": "foo";
+     }
+ 
+  In order to do that, we need
+  - keep track of the identation
+  - put commas between items, but not after the last item
+  
+  this is achieved by every item:
+  - leaving the cursor at the end of the line after the just-written item.
+  - each item first writes \n then follows with appropriate amount of space
+
+*/
+
+/* Compound objects */
+Optimizer_trace& Optimizer_trace::start_list(const char *name)
+{
+  separator();
+
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": [");
+  push();
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::start_object(const char *name)
+{
+  separator();
+  
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": {");
+  push();
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::end_list(const char *name)
+{
+  // no separator
+  pop();
+  next_line();
+  trace.append("]");
+  return *this;
+}
+
+
+Optimizer_trace& Optimizer_trace::end_object(const char *name)
+{
+  // no separator
+  pop();
+  next_line();
+  trace.append("}");
+  return *this;
+}
+
+/* Methods to add named scalar objects */
+Optimizer_trace& Optimizer_trace::add_str(const char *name, const char *val)
+{
+  separator();
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": \"");
+  trace.append(val);
+  trace.append('"');
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::add_ll(const char *name, longlong val)
+{
+  char buf[32];
+  separator();
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": \"");
+  my_sprintf(buf, (buf, "%lld", val));
+  trace.append(buf);
+  trace.append("\"");
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::add_ull(const char *name, ulonglong val)
+{
+  char buf[32];
+  separator();
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": ");
+  my_sprintf(buf, (buf, "%llu", val));
+  trace.append(buf);
+  return *this;
+}
+
+
+Optimizer_trace& Optimizer_trace::add(const char *name, double val)
+{
+  char buf[32];
+  separator();
+  trace.append('"');
+  trace.append(name);
+  trace.append("\": ");
+  my_sprintf(buf, (buf, "%g", val));
+  trace.append(buf);
+  return *this;
+}
+
+/* Methods to add unnamed scalar objects */
+Optimizer_trace& Optimizer_trace::add(const char *val)
+{
+  separator();
+  trace.append('"');
+  trace.append(val);
+  trace.append('"');
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::add(longlong val)
+{
+  separator();
+  trace.append("\"TODO:NUMBER\"");
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::add(ulonglong val)
+{
+  separator();
+  trace.append("\"TODO:NUMBER\"");
+  return *this;
+}
+
+
+/** Formatting  **/
+void Optimizer_trace::next_line()
+{
+  trace.append('\n');
+  //TODO this is ridiculous:
+  for (int i= 0; i < depth*2; i++)
+    trace.append(' ');
+}
+
+void Optimizer_trace::push()
+{
+  depth++;
+  first.at(depth)= 1;
+}
+
+void Optimizer_trace::pop()
+{
+  depth--;
+}
+
+/*
+  This is called before writing each object
+*/
+void Optimizer_trace::separator()
+{
+  //DBUG_ASSERT(!dbug_in_blob_write);
+  // If we've already written an object at this level, first put a
+  // separator.
+  if (!first.at(depth))
+  {
+    trace.append(',');
+  }
+  next_line();
+  first.at(depth)=FALSE;
+}
+
+
+Optimizer_trace& Optimizer_trace::start_write(const char *name)
+{
+  separator();
+  
+  if (name)
+  {
+    trace.append('"');
+    trace.append(name);
+    trace.append("\": \"");
+  }
+  else
+    trace.append("\"");
+
+  return *this;
+}
+
+
+Optimizer_trace& Optimizer_trace::write(const char *str)
+{
+  trace.append(str);
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::write(const char *str, uint len)
+{
+  trace.append(str, len);
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::end_write()
+{
+  trace.append('"');
+  return *this;
+}
+
+Optimizer_trace& Optimizer_trace::add_item(const char *name, Item *item)
+{
+  char buff[256];
+  String str(buff,(uint32) sizeof(buff), system_charset_info);
+  str.length(0);
+  item->print(&str, QT_SICCINT);
+  str.append('\0');
+
+  add_str(name, str.ptr());
+  return *this;
+}
+
+/** Setup and start/end stuff */
+void Optimizer_trace::clear()
+{
+  trace.free();
+}
+
+void Optimizer_trace::start()
+{
+  trace.free();
+  started= TRUE;
+  depth= 0;
+  start_object("trace");
+}
+
+void Optimizer_trace::end()
+{
+  started= FALSE;
+  end_object("trace");
+}
+
+
+bool list_has_optimizer_trace_table(TABLE_LIST *tbl)
+{
+  for(;tbl; tbl= tbl->next_global)
+  {
+    if (tbl->schema_table && !strcmp(tbl->schema_table->table_name,
+                                     "OPTIMIZER_TRACE"))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+bool opt_trace_start(THD *thd, TABLE_LIST *tbl)
+{
+  // if we should be tracing but are not in the process of tracing
+  // already, start tracing
+  if (thd->variables.optimizer_trace && 
+      !list_has_optimizer_trace_table(tbl) &&
+      (!thd->opt_trace || !thd->opt_trace->is_started()))
+  {
+    if (!thd->opt_trace && !(thd->opt_trace= new Optimizer_trace()))
+      return FALSE;
+    thd->opt_trace->start();
+   return TRUE;
+  }
+  return FALSE;
+}
+
+void opt_trace_end(THD *thd, bool started)
+{
+  if (started)
+    thd->opt_trace->end();
+}
+
+#endif
+
+int opt_trace_fill_infschema_table(THD *thd, TABLE_LIST *tables, Item *cond);
+
+/**
+  Connects Information_Schema and optimizer trace
+*/
+int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *cond)
+{
+#ifdef OPTIMIZER_TRACE 
+  return  opt_trace_fill_infschema_table(thd, tables, cond);
+#else
+  my_error(ER_FEATURE_DISABLED, MYF(0), "optmizer trace", 
+           "enable-optimizer-trace");
+  return(1);
+#endif
+}
+
+ST_FIELD_INFO optimizer_trace_info[]=
+{
+  /* name, length, type, value, maybe_null, old_name, open_method */
+  {"QUERY_ID", 20, MYSQL_TYPE_LONG,   0, false, "Query_id", SKIP_OPEN_TABLE},
+  {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, 0, SKIP_OPEN_TABLE},
+  {NULL, 0,  MYSQL_TYPE_STRING, 0, true, NULL, 0}
+};
+
+
+int make_optimizer_trace_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
+{
+  ST_FIELD_INFO *field_info;
+  Name_resolution_context *context= &thd->lex->select_lex.context;
+  int i;
+
+  for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
+  {
+    field_info= &schema_table->fields_info[i];
+    Item_field *field= new Item_field(context,
+                                      NullS, NullS, field_info->field_name);
+    if (field)
+    {
+      field->set_name(field_info->old_name,
+                      strlen(field_info->old_name),
+                      system_charset_info);
+      if (add_item_to_list(thd, field))
+        return 1;
+    }
+  }
+  return 0;
+}
+
+int opt_trace_fill_infschema_table(THD *thd, TABLE_LIST *tables, Item *cond)
+{
+  DBUG_ENTER("opt_trace_fill_infschema_table");
+  TABLE *table= tables->table;
+  /* Go through each query in this thread's stored history... */
+  do
+  {
+    /* Set default values for this row. */
+    restore_record(table, s->default_values);
+
+    table->field[0]->store((longlong) 0, TRUE);
+    if (thd->opt_trace)
+      table->field[1]->store(thd->opt_trace->get_str().c_ptr(),
+                             thd->opt_trace->get_str().length(),
+                             system_charset_info);
+    else
+      table->field[1]->store("No saved trace",
+                             strlen("No saved trace"),
+                             system_charset_info);
+
+
+    if (schema_table_store_record(thd, table))
+      DBUG_RETURN(1);
+  } while (0);
+
+  DBUG_RETURN(0);
+}
+
+

=== added file 'sql/opt_trace.h'
--- a/sql/opt_trace.h	1970-01-01 00:00:00 +0000
+++ b/sql/opt_trace.h	2009-03-21 18:46:45 +0000
@@ -0,0 +1,142 @@
+/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#define OPTIMIZER_TRACE 1
+
+#ifdef OPTIMIZER_TRACE
+
+/*
+  A macro to be used
+    OPT_TRACE(thd, start_list("select"));
+    OPT_TRACE(thd, pop("select").);
+
+    OPT_TRACE(thd, print("AAA").);
+  
+
+  if (thd->opt_trace) 
+  {
+    thd->opt_trace->add("name", value);
+  }
+
+  if (thd->opt_trace) 
+  {
+    thd->opt_trace->add("name", value).add("othername", value2);
+  }
+*/
+
+// TODO : only have one check within unlikely(...)
+/*
+# define OPT_TRACE_HERE(thd, x) if (unlikely(thd_opt_trace && \
+                                        thd_opt_trace->is_started())) \
+                           { \
+                             thd->opt_trace->x; \
+                           }
+*/
+
+# define OPT_TRACE_IF(thd, cond, x) \
+         if (unlikely(thd->opt_trace && thd->opt_trace->is_started()) && \
+             (cond)) \
+         { \
+           thd->opt_trace->x; \
+         }
+
+# define OPT_TRACE(thd, x) if (unlikely(thd->opt_trace && \
+                                        thd->opt_trace->is_started())) \
+                           { \
+                             thd->opt_trace->x; \
+                           }
+
+# define OPT_IF_TRACE(thd, x) if (unlikely(thd->opt_trace && \
+                                           thd->opt_trace->is_started())) \
+                              { \
+                                Optimizer_trace *trace= thd->opt_trace; \
+                                x ;\
+                              }
+
+#define OPT_TRACE_DEBUG 1
+
+bool opt_trace_start(THD *thd);
+void opt_trace_end(THD *thd, bool started);
+
+/*
+  Optimizer trace. One can create the trace, write there, and then get what
+  was written.
+
+  Writing API is aimed to allow writing valid JSON.
+  
+*/
+class Optimizer_trace 
+{
+public:
+  Optimizer_trace(): depth(0), started(FALSE) {}
+
+  Optimizer_trace& start_list(const char *name);
+  Optimizer_trace& end_list(const char *name);
+
+  Optimizer_trace& start_object(const char *name);
+  Optimizer_trace& end_object(const char *name);
+  
+  /* Methods to add named scalar objects */
+  Optimizer_trace& add_str(const char *name, const char *val);
+  Optimizer_trace& add_ll(const char *name, longlong val);
+  Optimizer_trace& add_ull(const char *name, ulonglong val);
+  Optimizer_trace& add(const char *name, double val);
+  Optimizer_trace& add_item(const char *name, Item *item);
+
+  /* Methods to add unnamed scalar objects */
+  Optimizer_trace& add(const char *val);
+  Optimizer_trace& add(longlong val);
+  Optimizer_trace& add(ulonglong val);
+  
+  /* Methods to write one text blob in multiple steps */
+  Optimizer_trace& start_write(const char *name);
+  Optimizer_trace& write(const char* str);
+  Optimizer_trace& write(const char* str, uint len);
+  Optimizer_trace& end_write();
+
+  /* Global trace start/stop methods */
+  void clear();
+  void start();
+  void end();
+
+  bool is_started() { return started; }
+
+  String& get_str() { return trace; }
+
+//supposed-to-be-private:
+  void push();
+  void pop();
+  void separator();
+  void next_line();
+
+private:
+  /* Yes we're that simple for now */
+  String trace;
+  Dynamic_array<int> first;
+
+  int depth;
+  bool started;
+  
+  //TODO: if debug is used, a stack of tags
+};
+
+
+#else
+//psergey-todo: handle case of DBUG w/o TRACE... 
+# define OPT_TRACE(thd, x) 
+# define OPT_IF_TRACE(thd, x) 
+
+#endif
+

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2009-03-20 14:01:11 +0000
+++ b/sql/sql_parse.cc	2009-03-21 18:46:45 +0000
@@ -1589,6 +1589,8 @@ int prepare_schema_table(THD *thd, LEX *
     thd->profiling.discard_current_query();
 #endif
     break;
+  case SCH_OPTIMIZER_TRACE:
+    /*psergey-trace: TODO: something here? */
   case SCH_OPEN_TABLES:
   case SCH_VARIABLES:
   case SCH_STATUS:

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-03-20 14:01:11 +0000
+++ b/sql/sql_select.cc	2009-03-21 18:46:45 +0000
@@ -2005,7 +2005,7 @@ JOIN::optimize()
     for (uint i = const_tables; i < tables; i++)
       join_tab[i].table->prepare_for_position();
   }
-
+  //psergey-trace: todo: join QEP printout is here:
   DBUG_EXECUTE("info",TEST_join(this););
 
   if (const_tables != tables)
@@ -3781,6 +3781,9 @@ int pull_out_semijoin_tables(JOIN *join)
   DBUG_ENTER("pull_out_semijoin_tables");
   List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
    
+  OPT_TRACE_IF(join->thd, test(join->select_lex->sj_nests.elements),
+                          start_object("semi-join pullout"));
+
   /* Try pulling out of the each of the semi-joins */
   while ((sj_nest= sj_list_it++))
   {
@@ -3797,8 +3800,8 @@ int pull_out_semijoin_tables(JOIN *join)
         if (tbl->table->map & join->const_table_map)
         {
           pulled_tables |= tbl->table->map;
-          DBUG_PRINT("info", ("Table %s pulled out (reason: constant)",
-                              tbl->table->alias));
+          OPT_TRACE(join->thd, add_str("Pulled out a constant table", 
+                                       tbl->table->alias));
         }
       }
     }
@@ -3823,8 +3826,8 @@ int pull_out_semijoin_tables(JOIN *join)
           {
             pulled_a_table= TRUE;
             pulled_tables |= tbl->table->map;
-            DBUG_PRINT("info", ("Table %s pulled out (reason: func dep)",
-                                tbl->table->alias));
+            OPT_TRACE(join->thd, add_str("Pulled out a functionally dependent table", 
+                                         tbl->table->alias));
             /*
               Pulling a table out of uncorrelated subquery in general makes
               makes it correlated. See the NOTE to this funtion. 
@@ -3889,6 +3892,8 @@ int pull_out_semijoin_tables(JOIN *join)
         join->thd->restore_active_arena(arena, &backup);
     }
   }
+  OPT_TRACE_IF(join->thd, test(join->select_lex->sj_nests.elements),
+                          start_object("semi-join pullout"));
   DBUG_RETURN(0);
 }
 
@@ -3912,7 +3917,7 @@ static ha_rows get_quick_record_count(TH
     select->head=table;
     table->reginfo.impossible_range=0;
     OPT_TRACE(thd, start_object("range-analysis").
-                   add("table", select->head->alias));
+                   add_str("table", select->head->alias));
     error= select->test_quick_select(thd, *(key_map *)keys,(table_map) 0,
                                           limit, 0, FALSE);
     OPT_TRACE(thd, end_object("range-analysis"));
@@ -4167,6 +4172,7 @@ make_join_statistics(JOIN *join, TABLE_L
   /* Read tables with 0 or 1 rows (system tables) */
   join->const_table_map= 0;
 
+  OPT_TRACE(join->thd, start_object("const-table-detection"));
   for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count;
        p_pos < p_end ;
        p_pos++)
@@ -4348,11 +4354,9 @@ make_join_statistics(JOIN *join, TABLE_L
     }
   }
 
-  if (pull_out_semijoin_tables(join))
-    DBUG_RETURN(TRUE);
 
   /* Calc how many (possible) matched records in each table */
-
+  OPT_TRACE(join->thd, start_object("range_analysis"));
   for (s=stat ; s < stat_end ; s++)
   {
     if (s->type == JT_SYSTEM || s->type == JT_CONST)
@@ -4432,6 +4436,11 @@ make_join_statistics(JOIN *join, TABLE_L
       delete select;
     }
   }
+  OPT_TRACE(join->thd, end_object("range-analysis").
+                       end_object("const-table-detection"));
+
+  if (pull_out_semijoin_tables(join))
+    DBUG_RETURN(TRUE);
 
   join->join_tab=stat;
   join->map2table=stat_ref;
@@ -5615,7 +5624,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR
     (void) set_dynamic(keyuse,(uchar*) &key_end,i);
     keyuse->elements=i;
   }
-  DBUG_EXECUTE("opt", print_keyuse_array(keyuse););
+  OPT_IF_TRACE(thd, print_keyuse_array(trace, keyuse));
+  //DBUG_EXECUTE("opt", print_keyuse_array(keyuse););
   return FALSE;
 }
 
@@ -8415,7 +8425,7 @@ add_found_match_trig_cond(JOIN_TAB *tab,
   if (tab == root_tab)
     return cond;
   if ((tmp= add_found_match_trig_cond(tab->first_upper, cond, root_tab)))
-    tmp= new Item_func_trig_cond(tmp, &tab->found);
+    tmp= new Item_func_trig_cond(tmp, &tab->found, tab);
   if (tmp)
   {
     tmp->quick_fix_field();
@@ -8567,7 +8577,7 @@ static bool make_join_select(JOIN *join,
                                            (  table_map) 0, 0);
             if (!tmp)
               continue;
-            tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
+            tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl, TRUE);
             if (!tmp)
               DBUG_RETURN(1);
             tmp->quick_fix_field();
@@ -8862,7 +8872,7 @@ static bool make_join_select(JOIN *join,
                                          (table_map) 0, 0);
           if (!tmp)
             continue;
-          tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
+          tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl, TRUE);
           if (!tmp)
             DBUG_RETURN(1);
           tmp->quick_fix_field();
@@ -8884,6 +8894,8 @@ static bool make_join_select(JOIN *join,
         /* 
           Table tab is the last inner table of an outer join.
           An on expression is always attached to it.
+          (NOTE: print_outer_join_conds() makes some assumptions about what
+          the below code does. Be sure not to break them)
 	*/     
         COND *on_expr= *first_inner_tab->on_expr_ref;
 
@@ -8913,7 +8925,8 @@ static bool make_join_select(JOIN *join,
             DBUG_PRINT("info", ("Item_func_trig_cond"));
             tmp_cond= new Item_func_trig_cond(tmp_cond,
                                               &first_inner_tab->
-                                              not_null_compl);
+                                              not_null_compl,
+                                              TRUE);
             DBUG_PRINT("info", ("Item_func_trig_cond %p",
                                 tmp_cond));
             if (tmp_cond)
@@ -13307,6 +13320,9 @@ optimize_cond(JOIN *join, COND *conds, L
     *cond_value= Item::COND_TRUE;
   else
   {
+    //psergey-todo: remove the hack:
+    OPT_TRACE(join->thd, start_object("condition-processing").
+                         add_str("condition", build_equalities? "WHERE":"HAVING"));
     /* 
       Build all multiple equality predicates and eliminate equality
       predicates that can be inferred from these multiple equalities.
@@ -13314,13 +13330,13 @@ optimize_cond(JOIN *join, COND *conds, L
       that occurs in a function set a pointer to the multiple equality
       predicate. Substitute a constant instead of this field if the
       multiple equality contains a constant.
-    */ 
-    DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
+    */
+    OPT_TRACE(join->thd, add_item("original-cond", conds));
     if (build_equalities)
     {
       conds= build_equal_items(join->thd, conds, NULL, join_list,
                                &join->cond_equal);
-      DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
+     OPT_TRACE(join->thd, add_item("after equal_items", conds));
     }
     /* change field = field to field = const for each found field = const */
     propagate_cond_constants(thd, (I_List<COND_CMP> *) 0, conds, conds);
@@ -13328,9 +13344,11 @@ optimize_cond(JOIN *join, COND *conds, L
       Remove all instances of item == item
       Remove all and-levels where CONST item != CONST item
     */
+    OPT_TRACE(join->thd, add_item("after const change", conds));
     DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
     conds= remove_eq_conds(thd, conds, cond_value) ;
-    DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY););
+    OPT_TRACE(join->thd, add_item("after remove", conds));
+    OPT_TRACE(join->thd, end_object("condition-processing"));
   }
   DBUG_RETURN(conds);
 }
@@ -16694,6 +16712,7 @@ join_read_const_table(JOIN_TAB *tab, POS
   table->null_row=0;
   table->status=STATUS_NO_RECORD;
   
+  OPT_TRACE(tab->join->thd, add_str("table marked as constant", table->alias));
   if (tab->type == JT_SYSTEM)
   {
     if ((error=join_read_system(tab)))
@@ -21265,6 +21284,34 @@ void JOIN::clear()
   }
 }
 
+
+/* Utility function for EXPLAIN handling */
+
+uint create_union_table_name(SELECT_LEX_UNIT *unit, char *table_name_buffer)
+{
+  SELECT_LEX *sl= unit->first_select();
+  uint len= 6, lastop= 0;
+  memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
+  for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
+  {
+    len+= lastop;
+    lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
+                        "%u,", sl->select_number);
+  }
+  if (sl || len + lastop >= NAME_LEN)
+  {
+    memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
+    len+= 4;
+  }
+  else
+  {
+    len+= lastop;
+    table_name_buffer[len - 1]= '>';  // change ',' to '>'
+  }
+  return len;
+}
+
+
 /**
   EXPLAIN handling.
 
@@ -22222,311 +22269,3 @@ bool JOIN::change_result(select_result *
 /**
   @} (end of group Query_Optimizer)
 */
-
-
-/**************************************************************************
- * Optimizer trace starts 
- **************************************************************************/
-#ifdef OPTIMIZER_TRACE
-
-static const int ident_size=2;
-
-/*
-  General idea about how we make output: we want to produce formatting like
-  this:
-
-   "trace": {
-     "select" : {
-       "query after rewrites": "select ....",
-       "range analysis": {
-         "table": "t1",
-         "indexes": [
-           "key", 
-           "key2"
-         ]
-       },
-       "something-else": "foo";
-     }
- 
-  In order to do that, we need
-  - keep track of the identation
-  - put commas between items, but not after the last item
-  
-  this is achieved by every item:
-  - leaving the cursor at the end of the line after the just-written item.
-  - each item first writes \n then follows with appropriate amount of space
-
-*/
-
-/* Compound objects */
-Optimizer_trace& Optimizer_trace::start_list(const char *name)
-{
-  separator();
-
-  trace.append('"');
-  trace.append(name);
-  trace.append("\": [");
-  push();
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::start_object(const char *name)
-{
-  separator();
-  
-  trace.append('"');
-  trace.append(name);
-  trace.append("\": {");
-  push();
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::end_list(const char *name)
-{
-  // no separator
-  pop();
-  next_line();
-  trace.append("]");
-  return *this;
-}
-
-
-Optimizer_trace& Optimizer_trace::end_object(const char *name)
-{
-  // no separator
-  pop();
-  next_line();
-  trace.append("}");
-  return *this;
-}
-
-/* Methods to add named scalar objects */
-Optimizer_trace& Optimizer_trace::add(const char *name, const char *val)
-{
-  separator();
-  trace.append('"');
-  trace.append(name);
-  trace.append("\": \"");
-  trace.append(val);
-  trace.append('"');
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::add(const char *name, longlong val)
-{
-  char buf[32];
-  separator();
-  trace.append('"');
-  trace.append(name);
-  trace.append("\": ");
-  my_sprintf(buf, (buf, "%lld", val));
-  trace.append(buf);
-  trace.append('"');
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::add(const char *name, ulonglong val)
-{
-  char buf[32];
-  separator();
-  trace.append('"');
-  trace.append(name);
-  trace.append("\": ");
-  my_sprintf(buf, (buf, "%llu", val));
-  trace.append(buf);
-  return *this;
-}
-
-/* Methods to add unnamed scalar objects */
-Optimizer_trace& Optimizer_trace::add(const char *val)
-{
-  separator();
-  trace.append('"');
-  trace.append(val);
-  trace.append('"');
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::add(longlong val)
-{
-  separator();
-  trace.append("\"TODO:NUMBER\"");
-  return *this;
-}
-
-Optimizer_trace& Optimizer_trace::add(ulonglong val)
-{
-  separator();
-  trace.append("\"TODO:NUMBER\"");
-  return *this;
-}
-
-
-/** Formatting  **/
-void Optimizer_trace::next_line()
-{
-  trace.append('\n');
-  //TODO this is ridiculous:
-  for (int i= 0; i < depth*2; i++)
-    trace.append(' ');
-}
-
-void Optimizer_trace::push()
-{
-  depth++;
-  first.at(depth)= 1;
-}
-
-void Optimizer_trace::pop()
-{
-  depth--;
-}
-
-/*
-  This called before writing each object
-*/
-void Optimizer_trace::separator()
-{
-  // If we've already written an object at this level, first put a
-  // separator.
-  if (!first.at(depth))
-  {
-    trace.append(',');
-  }
-  next_line();
-  first.at(depth)=FALSE;
-}
-
-
-/** Setup and start/end stuff */
-void Optimizer_trace::clear()
-{
-  trace.free();
-}
-
-void Optimizer_trace::start()
-{
-  trace.free();
-  started= TRUE;
-  depth= 0;
-  start_object("trace");
-}
-
-void Optimizer_trace::end()
-{
-  started= FALSE;
-  end_object("trace");
-}
-
-bool list_has_optimizer_trace_table(TABLE_LIST *tbl)
-{
-  for(;tbl; tbl= tbl->next_global)
-  {
-    if (tbl->schema_table && !strcmp(tbl->schema_table->table_name,
-                                     "OPTIMIZER_TRACE"))
-      return TRUE;
-  }
-  return FALSE;
-}
-
-bool opt_trace_start(THD *thd, TABLE_LIST *tbl)
-{
-  // if we should be tracing but are not in the process of tracing
-  // already, start tracing
-  if (thd->variables.optimizer_trace && 
-      !list_has_optimizer_trace_table(tbl) &&
-      (!thd->opt_trace || !thd->opt_trace->is_started()))
-  {
-    if (!thd->opt_trace && !(thd->opt_trace= new Optimizer_trace()))
-      return FALSE;
-    thd->opt_trace->start();
-   return TRUE;
-  }
-  return FALSE;
-}
-
-void opt_trace_end(THD *thd, bool started)
-{
-  if (started)
-    thd->opt_trace->end();
-}
-
-#endif
-
-int opt_trace_fill_infschema_table(THD *thd, TABLE_LIST *tables, Item
-*cond);
-
-/**
-  Connects Information_Schema and optimizer trace
-*/
-int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *cond)
-{
-#ifdef OPTIMIZER_TRACE 
-  return  opt_trace_fill_infschema_table(thd, tables, cond);
-#else
-  my_error(ER_FEATURE_DISABLED, MYF(0), "optmizer trace", 
-           "enable-optimizer-trace");
-  return(1);
-#endif
-}
-
-ST_FIELD_INFO optimizer_trace_info[]=
-{
-  /* name, length, type, value, maybe_null, old_name, open_method */
-  {"QUERY_ID", 20, MYSQL_TYPE_LONG,   0, false, "Query_id", SKIP_OPEN_TABLE},
-  {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, 0, SKIP_OPEN_TABLE},
-  {NULL, 0,  MYSQL_TYPE_STRING, 0, true, NULL, 0}
-};
-
-
-int make_optimizer_trace_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
-{
-  ST_FIELD_INFO *field_info;
-  Name_resolution_context *context= &thd->lex->select_lex.context;
-  int i;
-
-  for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
-  {
-    field_info= &schema_table->fields_info[i];
-    Item_field *field= new Item_field(context,
-                                      NullS, NullS, field_info->field_name);
-    if (field)
-    {
-      field->set_name(field_info->old_name,
-                      strlen(field_info->old_name),
-                      system_charset_info);
-      if (add_item_to_list(thd, field))
-        return 1;
-    }
-  }
-  return 0;
-}
-
-int opt_trace_fill_infschema_table(THD *thd, TABLE_LIST *tables, Item *cond)
-{
-  DBUG_ENTER("opt_trace_fill_infschema_table");
-  TABLE *table= tables->table;
-  /* Go through each query in this thread's stored history... */
-  do
-  {
-    /* Set default values for this row. */
-    restore_record(table, s->default_values);
-
-    table->field[0]->store((longlong) 0, TRUE);
-    if (thd->opt_trace)
-      table->field[1]->store(thd->opt_trace->get_str().c_ptr(),
-                             thd->opt_trace->get_str().length(),
-                             system_charset_info);
-    else
-      table->field[1]->store("No saved trace",
-                             strlen("No saved trace"),
-                             system_charset_info);
-
-
-    if (schema_table_store_record(thd, table))
-      DBUG_RETURN(1);
-  } while (0);
-
-  DBUG_RETURN(0);
-}
-

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2009-03-20 14:01:11 +0000
+++ b/sql/sql_select.h	2009-03-21 18:46:45 +0000
@@ -1942,4 +1942,7 @@ void calc_used_field_length(THD *thd, JO
 int join_init_read_record(JOIN_TAB *tab);
 int do_sj_dups_weedout(THD *thd, SJ_TMP_TABLE *sjtbl); 
 
+/* EXPLAIN helper funcs */
+uint create_union_table_name(SELECT_LEX_UNIT *unit, char *table_name_buffer);
+
 

=== modified file 'sql/sql_test.cc'
--- a/sql/sql_test.cc	2009-02-13 16:30:54 +0000
+++ b/sql/sql_test.cc	2009-03-21 18:46:45 +0000
@@ -222,39 +222,41 @@ TEST_join(JOIN *join)
 
 #define FT_KEYPART   (MAX_REF_PARTS+10)
 
-void print_keyuse(KEYUSE *keyuse)
+void print_keyuse(Optimizer_trace *trace, KEYUSE *keyuse)
 {
   char buff[256];
-  char buf2[64]; 
+  char buff2[512]; 
   const char *fieldname;
   String str(buff,(uint32) sizeof(buff), system_charset_info);
   str.length(0);
-  keyuse->val->print(&str, QT_ORDINARY);
+  keyuse->val->print(&str, QT_SICCINT);
   str.append('\0');
   if (keyuse->keypart == FT_KEYPART)
-    fieldname= "FT_KEYPART";
+    fieldname= "<fulltext>";
   else
     fieldname= keyuse->table->key_info[keyuse->key].key_part[keyuse->keypart].field->field_name;
-  longlong2str(keyuse->used_tables, buf2, 16); 
-  DBUG_LOCK_FILE;
-  fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s  optimize= %d used_tables=%s "
-          "ref_table_rows= %lu keypart_map= %0lx\n",
-          keyuse->table->alias, fieldname, str.ptr(),
-          keyuse->optimize, buf2, (ulong)keyuse->ref_table_rows, 
-          keyuse->keypart_map);
-  DBUG_UNLOCK_FILE;
+  //!! longlong2str(keyuse->used_tables, buf2, 16); 
+  
+  //fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s  optimize= %d used_tables=%s "
+  //        "ref_table_rows= %lu keypart_map= %0lx\n",
+  //        keyuse->table->alias, fieldname, str.ptr(),
+  //        keyuse->optimize, buf2, (ulong)keyuse->ref_table_rows, 
+  //        keyuse->keypart_map);
   //key_part_map keypart_map; --?? there can be several? 
+  trace->start_object("keyuse");
+  my_sprintf(buff2, (buff2, "%s.%s= %s", keyuse->table->alias, fieldname, str.ptr()));
+  trace->add(buff2);
+  trace->end_object("keyuse");
 }
 
 
 /* purecov: begin inspected */
-void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array)
+void print_keyuse_array(Optimizer_trace *trace, DYNAMIC_ARRAY *keyuse_array)
 {
-  DBUG_LOCK_FILE;
-  fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements);
-  DBUG_UNLOCK_FILE;
+  trace->start_object("ref-optimizer");
   for(uint i=0; i < keyuse_array->elements; i++)
-    print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i));
+    print_keyuse(trace, (KEYUSE*)dynamic_array_ptr(keyuse_array, i));
+  trace->end_object("ref-optimizer");
 }
 
 

Thread
bzr commit into mysql-6.0-opt branch (sergefp:2731) WL#4800Sergey Petrunia21 Mar