Commit 03691a77 authored by Sergey Petrunya's avatar Sergey Petrunya

SHOW EXPLAIN UPDATE/DELETE

- Introduce "Query Plan Footprints" (abbrev. QPFs)
  QPF is a part of query plan that is 
  1. sufficient to produce EXPLAIN output,
  2. can be used to produce EXPLAIN output even after its subquery/union
     was executed and deleted
  3. is cheap to save so that we can always save query plans

- This patch doesn't fully address #2, we make/save strings for 
  a number of EXPLAIN's columns.  This will be fixed.
parent 9718b976
...@@ -99,6 +99,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ...@@ -99,6 +99,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/sql_expression_cache.cc ../sql/sql_expression_cache.cc
../sql/my_apc.cc ../sql/my_apc.h ../sql/my_apc.cc ../sql/my_apc.h
../sql/rpl_gtid.cc ../sql/rpl_gtid.cc
../sql/opt_qpf.cc ../sql/opt_qpf.h
${GEN_SOURCES} ${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE} ${MYSYS_LIBWRAP_SOURCE}
) )
......
...@@ -80,6 +80,7 @@ SET (SQL_SOURCE ...@@ -80,6 +80,7 @@ SET (SQL_SOURCE
sql_reload.cc sql_reload.cc
# added in MariaDB: # added in MariaDB:
opt_qpf.h opt_qpf.cc
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
create_options.cc multi_range_read.cc create_options.cc multi_range_read.cc
opt_index_cond_pushdown.cc opt_subselect.cc opt_index_cond_pushdown.cc opt_subselect.cc
......
...@@ -286,7 +286,8 @@ ...@@ -286,7 +286,8 @@
(yes, the sum is deliberately inaccurate) (yes, the sum is deliberately inaccurate)
TODO remove the limit, use dynarrays TODO remove the limit, use dynarrays
*/ */
#define MAX_HA 15 /*#define MAX_HA 15*/
#define MAX_HA 32
/* /*
Use this instead of 0 as the initial value for the slot number of Use this instead of 0 as the initial value for the slot number of
......
/*
TODO MP AB copyright
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
#include "sql_priv.h"
#include "sql_select.h"
QPF_query::QPF_query()
{
memset(&unions, 0, sizeof(unions));
memset(&selects, 0, sizeof(selects));
}
QPF_node *QPF_query::get_node(uint select_id)
{
if (unions[select_id])
return unions[select_id];
else
return selects[select_id];
}
QPF_select *QPF_query::get_select(uint select_id)
{
return selects[select_id];
}
void QPF_query::add_node(QPF_node *node)
{
if (node->get_type() == QPF_node::QPF_UNION)
{
QPF_union *u= (QPF_union*)node;
unions[u->get_select_id()]= u;
}
else
{
QPF_select *sel= (QPF_select*)node;
if (sel->select_id == (int)UINT_MAX)
{
//TODO this is a "fake select" from a UNION.
DBUG_ASSERT(0);
}
else
selects[sel->select_id] = sel;
}
}
/*
The main entry point to print EXPLAIN of the entire query
*/
int QPF_query::print_explain(select_result_sink *output,
uint8 explain_flags)
{
// Start with id=1
QPF_node *node= get_node(1);
return node->print_explain(this, output, explain_flags);
}
void QPF_union::push_table_name(List<Item> *item_list)
{
}
static void push_str(List<Item> *item_list, const char *str)
{
item_list->push_back(new Item_string(str,
strlen(str), system_charset_info));
}
static void push_string(List<Item> *item_list, String *str)
{
item_list->push_back(new Item_string(str->ptr(), str->length(),
system_charset_info));
}
int QPF_union::print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags)
{
// print all children, in order
for (int i= 0; i < (int) children.elements(); i++)
{
QPF_select *sel= query->get_select(children.at(i));
sel->print_explain(query, output, explain_flags);
}
/* Print a line with "UNION RESULT" */
List<Item> item_list;
Item *item_null= new Item_null();
/* `id` column */
item_list.push_back(item_null);
/* `select_type` column */
push_str(&item_list, fake_select_type);
/* `table` column: something like "<union1,2>" */
//
{
char table_name_buffer[SAFE_NAME_LEN];
uint childno= 0;
uint len= 6, lastop= 0;
memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
for (; childno < children.elements() && len + lastop + 5 < NAME_LEN;
childno++)
{
len+= lastop;
lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
"%u,", children.at(childno));
}
if (childno < children.elements() || 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 '>'
}
const CHARSET_INFO *cs= system_charset_info;
item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
//
push_table_name(&item_list);
/* `partitions` column */
if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* `type` column */
push_str(&item_list, join_type_str[JT_ALL]);
/* `possible_keys` column */
item_list.push_back(item_null);
/* `key` */
item_list.push_back(item_null);
/* `key_len` */
item_list.push_back(item_null);
/* `ref` */
item_list.push_back(item_null);
/* `rows` */
item_list.push_back(item_null);
/* `filtered` */
if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* `Extra` */
StringBuffer<256> extra_buf;
if (using_filesort)
{
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
const CHARSET_INFO *cs= system_charset_info;
item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs));
if (output->send_data(item_list))
return 1;
return 0;
}
int QPF_select::print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags)
{
if (message)
{
List<Item> item_list;
const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
item_list.push_back(new Item_int((int32) select_id));
item_list.push_back(new Item_string(select_type,
strlen(select_type), cs));
for (uint i=0 ; i < 7; i++)
item_list.push_back(item_null);
if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
item_list.push_back(new Item_string(message,strlen(message),cs));
if (output->send_data(item_list))
return 1;
return 0;
}
else
{
bool using_tmp= using_temporary;
bool using_fs= using_filesort;
for (uint i=0; i< n_join_tabs; i++)
{
join_tabs[i]->print_explain(output, explain_flags, select_id,
select_type, using_tmp, using_fs);
if (i == 0)
{
/*
"Using temporary; Using filesort" should only be shown near the 1st
table
*/
using_tmp= false;
using_fs= false;
}
}
}
return 0;
}
int QPF_table_access::print_explain(select_result_sink *output, uint8 explain_flags,
uint select_id, const char *select_type,
bool using_temporary, bool using_filesort)
{
List<Item> item_list;
Item *item_null= new Item_null();
//const CHARSET_INFO *cs= system_charset_info;
/* `id` column */
item_list.push_back(new Item_int((int32) select_id));
/* `select_type` column */
push_str(&item_list, select_type);
/* `table` column */
push_string(&item_list, &table_name);
/* `partitions` column */
if (explain_flags & DESCRIBE_PARTITIONS)
{
if (used_partitions_set)
{
push_string(&item_list, &used_partitions);
}
else
item_list.push_back(item_null);
}
/* `type` column */
push_str(&item_list, join_type_str[type]);
/* `possible_keys` column */
//push_str(item_list, "TODO");
item_list.push_back(item_null);
/* `key` */
if (key_set)
push_string(&item_list, &key);
else
item_list.push_back(item_null);
/* `key_len` */
if (key_len_set)
push_string(&item_list, &key_len);
else
item_list.push_back(item_null);
/* `ref` */
if (ref_set)
push_string(&item_list, &ref);
else
item_list.push_back(item_null);
/* `rows` */
if (rows_set)
{
item_list.push_back(new Item_int((longlong) (ulonglong) rows,
MY_INT64_NUM_DECIMAL_DIGITS));
}
else
item_list.push_back(item_null);
/* `filtered` */
if (explain_flags & DESCRIBE_EXTENDED)
{
if (filtered_set)
{
item_list.push_back(new Item_float(filtered, 2));
}
else
item_list.push_back(item_null);
}
/* `Extra` */
StringBuffer<256> extra_buf;
bool first= true;
for (int i=0; i < (int)extra_tags.elements(); i++)
{
if (first)
first= false;
else
extra_buf.append(STRING_WITH_LEN("; "));
append_tag_name(&extra_buf, extra_tags.at(i));
}
if (using_temporary)
{
if (first)
first= false;
else
extra_buf.append(STRING_WITH_LEN("; "));
extra_buf.append(STRING_WITH_LEN("Using temporary"));
}
if (using_filesort)
{
if (first)
first= false;
else
extra_buf.append(STRING_WITH_LEN("; "));
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
const CHARSET_INFO *cs= system_charset_info;
item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs));
if (output->send_data(item_list))
return 1;
return 0;
}
const char * extra_tag_text[]=
{
"ET_none",
"Using index condition",
"Using index condition(BKA)",
"Using ", //special
"Range checked for each record (index map: 0x", //special
"Using where with pushed condition",
"Using where",
"Not exists",
"Using index",
"Full scan on NULL key",
"Skip_open_table",
"Open_frm_only",
"Open_full_table",
"Scanned 0 databases",
"Scanned 1 database",
"Scanned all databases",
"Using index for group-by", // Special?
"USING MRR: DONT PRINT ME", // Special!
"Distinct",
"LooseScan",
"Start temporary",
"End temporary",
"FirstMatch", //TODO: also handle special variant!
"Using join buffer", // Special!,
"const row not found",
"unique row not found",
"Impossible ON condition"
};
void QPF_table_access::append_tag_name(String *str, enum Extra_tag tag)
{
switch (tag) {
case ET_USING:
{
// quick select
str->append(STRING_WITH_LEN("Using "));
str->append(quick_info);
break;
}
case ET_RANGE_CHECKED_FOR_EACH_RECORD:
{
/* 4 bits per 1 hex digit + terminating '\0' */
char buf[MAX_KEY / 4 + 1];
str->append(STRING_WITH_LEN("Range checked for each "
"record (index map: 0x"));
str->append(range_checked_map.print(buf));
str->append(')');
break;
}
case ET_USING_MRR:
{
str->append(mrr_type);
break;
}
case ET_USING_JOIN_BUFFER:
{
str->append(extra_tag_text[tag]);
str->append(join_buffer_type);
break;
}
default:
str->append(extra_tag_text[tag]);
}
}
/**************************************************************************************
Query Plan Footprint (QPF) structures
These structures
- Can be produced in-expensively from query plan.
- Store sufficient information to produce either a tabular or a json EXPLAIN
output
- Have methods that produce a tabular output.
*************************************************************************************/
class QPF_query;
/*
A node can be either a SELECT, or a UNION.
*/
class QPF_node : public Sql_alloc
{
public:
enum qpf_node_type {QPF_UNION, QPF_SELECT};
virtual enum qpf_node_type get_type()= 0;
virtual int print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags)=0;
virtual ~QPF_node(){}
};
/*
Nesting.
QPF_select may have children QPF_select-s.
(these can be FROM-subqueries, or subqueries from other clauses)
As for unions, the standard approach is:
- UNION node can be where the select node can be;
- the union has a select that retrieves results from temptable (a special
kind of child)
- and it has regular children selects that are merged into the union.
*/
class QPF_table_access;
class QPF_select : public QPF_node
{
/*Construction interface */
public:
enum qpf_node_type get_type() { return QPF_SELECT; }
#if 0
/* Constructs a finished degenerate join plan */
QPF_select(int select_id_arg, const char *select_type_arg, const char* msg) :
select_id(select_id_arg),
select_type(select_type_arg),
message(msg),
join_tabs(NULL), n_join_tabs(0)
{}
/* Constructs an un-finished, non degenerate join plan. */
QPF_select(int select_id_arg, const char *select_type_arg) :
select_id(select_id_arg),
select_type(select_type_arg),
message(NULL),
join_tabs(NULL), n_join_tabs(0)
{}
#endif
QPF_select() :
message(NULL), join_tabs(NULL),
using_temporary(false), using_filesort(false)
{}
bool add_table(QPF_table_access *tab)
{
if (!join_tabs)
{
join_tabs= (QPF_table_access**) malloc(sizeof(QPF_table_access*) * MAX_TABLES);
n_join_tabs= 0;
}
join_tabs[n_join_tabs++]= tab;
return false;
}
public:
int select_id; /* -1 means NULL. */
const char *select_type;
/*
If message != NULL, this is a degenerate join plan, and all subsequent
members have no info
*/
const char *message;
/*
According to the discussion: this should be an array of "table
descriptors".
As for SJ-Materialization. Start_materialize/end_materialize markers?
*/
QPF_table_access** join_tabs;
uint n_join_tabs;
/* Global join attributes. In tabular form, they are printed on the first row */
bool using_temporary;
bool using_filesort;
void print_tabular(select_result_sink *output, uint8 explain_flags//,
//bool *printed_anything
);
int print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags);
};
class QPF_union : public QPF_node
{
public:
enum qpf_node_type get_type() { return QPF_UNION; }
int get_select_id()
{
DBUG_ASSERT(children.elements() > 0);
return children.at(0);
}
// This has QPF_select children
Dynamic_array<int> children;
void add_select(int select_no)
{
children.append(select_no);
}
void push_table_name(List<Item> *item_list);
int print_explain(QPF_query *query, select_result_sink *output,
uint8 explain_flags);
const char *fake_select_type;
bool using_filesort;
};
/*
This is the whole query.
*/
class QPF_query
{
public:
QPF_query();
void add_node(QPF_node *node);
int print_explain(select_result_sink *output, uint8 explain_flags);
/* This will return a select, or a union */
QPF_node *get_node(uint select_id);
/* This will return a select (even if there is a union with this id) */
QPF_select *get_select(uint select_id);
private:
QPF_union *unions[MAX_TABLES];
QPF_select *selects[MAX_TABLES];
};
enum Extra_tag
{
ET_none= 0, /* not-a-tag */
ET_USING_INDEX_CONDITION,
ET_USING_INDEX_CONDITION_BKA,
ET_USING, /* For quick selects of various kinds */
ET_RANGE_CHECKED_FOR_EACH_RECORD,
ET_USING_WHERE_WITH_PUSHED_CONDITION,
ET_USING_WHERE,
ET_NOT_EXISTS,
ET_USING_INDEX,
ET_FULL_SCAN_ON_NULL_KEY,
ET_SKIP_OPEN_TABLE,
ET_OPEN_FRM_ONLY,
ET_OPEN_FULL_TABLE,
ET_SCANNED_0_DATABASES,
ET_SCANNED_1_DATABASE,
ET_SCANNED_ALL_DATABASES,
ET_USING_INDEX_FOR_GROUP_BY,
ET_USING_MRR, // does not print "Using mrr".
ET_DISTINCT,
ET_LOOSESCAN,
ET_START_TEMPORARY,
ET_END_TEMPORARY,
ET_FIRST_MATCH,
ET_USING_JOIN_BUFFER,
ET_CONST_ROW_NOT_FOUND,
ET_UNIQUE_ROW_NOT_FOUND,
ET_IMPOSSIBLE_ON_CONDITION,
ET_total
};
class QPF_table_access
{
public:
void push_extra(enum Extra_tag extra_tag);
/* Internals */
public:
/* id and 'select_type' are cared-of by the parent QPF_select */
TABLE *table;
StringBuffer<256> table_name;
enum join_type type;
StringBuffer<256> used_partitions;
bool used_partitions_set;
key_map possible_keys;
uint key_no;
uint key_length;
Dynamic_array<enum Extra_tag> extra_tags;
//temporary:
StringBuffer<256> key;
StringBuffer<256> key_len;
StringBuffer<256> ref;
bool key_set;
bool key_len_set;
bool ref_set;
bool rows_set;
ha_rows rows;
double filtered;
bool filtered_set;
/* Various stuff for 'Extra' column*/
uint join_cache_level;
// Valid if ET_USING tag is present
StringBuffer<256> quick_info;
// Valid if ET_USING_INDEX_FOR_GROUP_BY is present
StringBuffer<256> loose_scan_type;
// valid with ET_RANGE_CHECKED_FOR_EACH_RECORD
key_map range_checked_map;
// valid with ET_USING_MRR
StringBuffer <256> mrr_type;
// valid with ET_USING_JOIN_BUFFER
StringBuffer <256> join_buffer_type;
TABLE *firstmatch_table;
int print_explain(select_result_sink *output, uint8 explain_flags,
uint select_id, const char *select_type,
bool using_temporary, bool using_filesort);
private:
void append_tag_name(String *str, enum Extra_tag tag);
};
// Update_plan and Delete_plan belong to this kind of structures, too.
// TODO: should Update_plan inherit from QPF_table_access?
...@@ -169,6 +169,8 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, ...@@ -169,6 +169,8 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags,
extra_str.c_ptr()); extra_str.c_ptr());
*printed_anything= true; *printed_anything= true;
/*
psergey-todo: handle all this through saving QPF.
for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit(); for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit();
unit; unit;
...@@ -177,6 +179,7 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags, ...@@ -177,6 +179,7 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags,
if (unit->print_explain(output, explain_flags, printed_anything)) if (unit->print_explain(output, explain_flags, printed_anything))
return 1; return 1;
} }
*/
return 0; return 0;
} }
......
...@@ -4172,6 +4172,7 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) ...@@ -4172,6 +4172,7 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
return all_merged; return all_merged;
} }
int LEX::print_explain(select_result_sink *output, uint8 explain_flags, int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything) bool *printed_anything)
{ {
...@@ -4180,11 +4181,87 @@ int LEX::print_explain(select_result_sink *output, uint8 explain_flags, ...@@ -4180,11 +4181,87 @@ int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
upd_del_plan->print_explain(output, explain_flags, printed_anything); upd_del_plan->print_explain(output, explain_flags, printed_anything);
return 0; return 0;
} }
int res= unit.print_explain(output, explain_flags, printed_anything); //int res= unit.print_explain(output, explain_flags, printed_anything);
return res;
//psergey-todo: here, we should make Query Plan Footprint, and then produce
// an EXPLAIN output from it.
/*
The new, QueryPlanFootprint way:
*/
QPF_query qpf;
unit.save_qpf(&qpf);
//return res;
return 0;
} }
void st_select_lex::save_qpf(QPF_query *output)
{
int res;
if (join && join->have_query_plan == JOIN::QEP_AVAILABLE)
{
/*
There is a number of reasons join can be marked as degenerate, so all
three conditions below can happen simultaneously, or individually:
*/
if (!join->table_count || !join->tables_list || join->zero_result_cause)
{
/* It's a degenerate join */
const char *cause= join->zero_result_cause ? join-> zero_result_cause :
"No tables used";
res= join->save_qpf(output, FALSE, FALSE, FALSE, cause);
}
else
{
join->save_qpf(output, join->need_tmp, // need_tmp_table
!join->skip_sort_order && !join->no_order &&
(join->order || join->group_list), // bool need_order
join->select_distinct, // bool distinct
NULL); //const char *message
}
if (res)
goto err;
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
{
/*
Display subqueries only if they are not parts of eliminated WHERE/ON
clauses.
*/
if (!(unit->item && unit->item->eliminated))
{
unit->save_qpf(output);
}
}
}
else
{
const char *msg;
if (!join)
DBUG_ASSERT(0); /* Seems not to be possible */
/* Not printing anything useful, don't touch *printed_anything here */
if (join->have_query_plan == JOIN::QEP_NOT_PRESENT_YET)
msg= "Not yet optimized";
else
{
DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
msg= "Query plan already deleted";
}
set_explain_type(TRUE/* on_the_fly */);
QPF_select *qp_sel= new QPF_select;
qp_sel->select_id= select_number;
qp_sel->select_type= type;
qp_sel->message= msg;
output->add_node(qp_sel);
}
err:
return ;//res;
}
#if 0
int st_select_lex::print_explain(select_result_sink *output, int st_select_lex::print_explain(select_result_sink *output,
uint8 explain_flags, uint8 explain_flags,
bool *printed_anything) bool *printed_anything)
...@@ -4253,8 +4330,60 @@ int st_select_lex::print_explain(select_result_sink *output, ...@@ -4253,8 +4330,60 @@ int st_select_lex::print_explain(select_result_sink *output,
err: err:
return res; return res;
} }
#endif
int st_select_lex_unit::save_qpf(QPF_query *output)
{
//int res= 0;
SELECT_LEX *first= first_select();
QPF_union *qpfu= new QPF_union;
/*
TODO: The following code should be eliminated. If we have a capability to
save Query Plan Footprints, we should just save them, and never need to
print "query plan already deleted".
*/
if (first && !first->next_select() && !first->join)
{
/*
If there is only one child, 'first', and it has join==NULL, emit "not in
EXPLAIN state" error.
*/
const char *msg="Query plan already deleted";
first->set_explain_type(TRUE/* on_the_fly */);
QPF_select *qp_sel= new QPF_select;
qp_sel->select_id= first->select_number;
qp_sel->select_type= first->type;
qp_sel->message= msg;
output->add_node(qp_sel);
qpfu->add_select(qp_sel->select_id);
return 0;
}
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
sl->save_qpf(output);
qpfu->add_select(sl->select_number);
}
// Save the UNION node
output->add_node(qpfu);
#if 0
/* Note: fake_select_lex->join may be NULL or non-NULL at this point */
if (fake_select_lex)
{
res= print_fake_select_lex_join(output, TRUE /* on the fly */,
fake_select_lex, explain_flags);
}
return res;
#endif
return 0;
}
#if 0
int st_select_lex_unit::print_explain(select_result_sink *output, int st_select_lex_unit::print_explain(select_result_sink *output,
uint8 explain_flags, bool *printed_anything) uint8 explain_flags, bool *printed_anything)
{ {
...@@ -4288,7 +4417,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output, ...@@ -4288,7 +4417,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output,
} }
return res; return res;
} }
#endif
/** /**
A routine used by the parser to decide whether we are specifying a full A routine used by the parser to decide whether we are specifying a full
......
...@@ -617,7 +617,7 @@ class select_result; ...@@ -617,7 +617,7 @@ class select_result;
class JOIN; class JOIN;
class select_union; class select_union;
class Procedure; class Procedure;
class QPF_query;
class st_select_lex_unit: public st_select_lex_node { class st_select_lex_unit: public st_select_lex_node {
protected: protected:
...@@ -728,8 +728,11 @@ public: ...@@ -728,8 +728,11 @@ public:
friend int subselect_union_engine::exec(); friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types(); List<Item> *get_unit_column_types();
#if 0
int print_explain(select_result_sink *output, uint8 explain_flags, int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything); bool *printed_anything);
#endif
int save_qpf(QPF_query *output);
}; };
typedef class st_select_lex_unit SELECT_LEX_UNIT; typedef class st_select_lex_unit SELECT_LEX_UNIT;
...@@ -1048,8 +1051,11 @@ public: ...@@ -1048,8 +1051,11 @@ public:
bool save_prep_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd);
bool is_merged_child_of(st_select_lex *ancestor); bool is_merged_child_of(st_select_lex *ancestor);
#if 0
int print_explain(select_result_sink *output, uint8 explain_flags, int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything); bool *printed_anything);
#endif
void save_qpf(QPF_query *output);
/* /*
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags: For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
- Non-aggregated fields are used in this select. - Non-aggregated fields are used in this select.
...@@ -2360,6 +2366,8 @@ class SQL_SELECT; ...@@ -2360,6 +2366,8 @@ class SQL_SELECT;
/* /*
Query plan of a single-table UPDATE. Query plan of a single-table UPDATE.
(This is actually a plan for single-table DELETE also) (This is actually a plan for single-table DELETE also)
TODO: this should be a query plan footprint, not a query plan.
*/ */
class Update_plan class Update_plan
{ {
...@@ -2411,6 +2419,7 @@ public: ...@@ -2411,6 +2419,7 @@ public:
}; };
class QPF_query;
/* The state of the lex parsing. This is saved in the THD struct */ /* The state of the lex parsing. This is saved in the THD struct */
struct LEX: public Query_tables_list struct LEX: public Query_tables_list
...@@ -2424,6 +2433,7 @@ struct LEX: public Query_tables_list ...@@ -2424,6 +2433,7 @@ struct LEX: public Query_tables_list
/* For single-table DELETE: its query plan */ /* For single-table DELETE: its query plan */
Update_plan *upd_del_plan; Update_plan *upd_del_plan;
QPF_query *query_plan_footprint;
char *length,*dec,*change; char *length,*dec,*change;
LEX_STRING name; LEX_STRING name;
......
...@@ -4811,7 +4811,15 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -4811,7 +4811,15 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
if (!(result= new select_send())) if (!(result= new select_send()))
return 1; /* purecov: inspected */ return 1; /* purecov: inspected */
thd->send_explain_fields(result); thd->send_explain_fields(result);
thd->lex->query_plan_footprint= new QPF_query;
res= mysql_explain_union(thd, &thd->lex->unit, result); res= mysql_explain_union(thd, &thd->lex->unit, result);
thd->lex->query_plan_footprint->print_explain(result, thd->lex->describe);
//psergey-todo: here, produce the EXPLAIN output.
// mysql_explain_union() itself is only responsible for calling
// optimize() for all parts of the query.
/* /*
The code which prints the extended description is not robust The code which prints the extended description is not robust
against malformed queries, so skip it if we have an error. against malformed queries, so skip it if we have an error.
......
This diff is collapsed.
...@@ -198,6 +198,12 @@ int rr_sequential(READ_RECORD *info); ...@@ -198,6 +198,12 @@ int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info); int rr_sequential_and_unpack(READ_RECORD *info);
#include "opt_qpf.h"
/**************************************************************************************
* New EXPLAIN structures END
*************************************************************************************/
class JOIN_CACHE; class JOIN_CACHE;
class SJ_TMP_TABLE; class SJ_TMP_TABLE;
class JOIN_TAB_RANGE; class JOIN_TAB_RANGE;
...@@ -252,7 +258,9 @@ typedef struct st_join_table { ...@@ -252,7 +258,9 @@ typedef struct st_join_table {
JOIN_TAB_RANGE *bush_children; JOIN_TAB_RANGE *bush_children;
/* Special content for EXPLAIN 'Extra' column or NULL if none */ /* Special content for EXPLAIN 'Extra' column or NULL if none */
const char *info; enum Extra_tag info;
//const char *info;
/* /*
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
column, or 0 if there is no info. column, or 0 if there is no info.
...@@ -1454,11 +1462,14 @@ public: ...@@ -1454,11 +1462,14 @@ public:
{ {
return (unit->item && unit->item->is_in_predicate()); return (unit->item && unit->item->is_in_predicate());
} }
/*
int print_explain(select_result_sink *result, uint8 explain_flags, int print_explain(select_result_sink *result, uint8 explain_flags,
bool on_the_fly, bool on_the_fly,
bool need_tmp_table, bool need_order, bool need_tmp_table, bool need_order,
bool distinct,const char *message); bool distinct,const char *message);
*/
int save_qpf(QPF_query *output, bool need_tmp_table, bool need_order,
bool distinct, const char *message);
private: private:
/** /**
TRUE if the query contains an aggregate function but has no GROUP TRUE if the query contains an aggregate function but has no GROUP
......
...@@ -6,7 +6,7 @@ filter='\.cc$\|\.c$\|\.h$\|\.yy$' ...@@ -6,7 +6,7 @@ filter='\.cc$\|\.c$\|\.h$\|\.yy$'
list="find . -type f" list="find . -type f"
bzr root >/dev/null 2>/dev/null && list="bzr ls --from-root -R --kind=file --versioned" bzr root >/dev/null 2>/dev/null && list="bzr ls --from-root -R --kind=file --versioned"
$list |grep $filter |while read f; $list |grep $filter | grep -v gen-cpp |while read f;
do do
etags -o TAGS --append $f etags -o TAGS --append $f
done done
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment