#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#4800 | Sergey Petrunia | 21 Mar |