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
../sql/sql_expression_cache.cc
../sql/my_apc.cc ../sql/my_apc.h
../sql/rpl_gtid.cc
../sql/opt_qpf.cc ../sql/opt_qpf.h
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
......
......@@ -80,6 +80,7 @@ SET (SQL_SOURCE
sql_reload.cc
# added in MariaDB:
opt_qpf.h opt_qpf.cc
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
create_options.cc multi_range_read.cc
opt_index_cond_pushdown.cc opt_subselect.cc
......
......@@ -286,7 +286,8 @@
(yes, the sum is deliberately inaccurate)
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
......
/*
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,7 +169,9 @@ int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags,
extra_str.c_ptr());
*printed_anything= true;
/*
psergey-todo: handle all this through saving QPF.
for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
......@@ -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))
return 1;
}
*/
return 0;
}
......
......@@ -4172,6 +4172,7 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
return all_merged;
}
int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything)
{
......@@ -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);
return 0;
}
int res= unit.print_explain(output, explain_flags, printed_anything);
return res;
//int res= unit.print_explain(output, explain_flags, printed_anything);
//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,
uint8 explain_flags,
bool *printed_anything)
......@@ -4253,8 +4330,60 @@ int st_select_lex::print_explain(select_result_sink *output,
err:
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,
uint8 explain_flags, bool *printed_anything)
{
......@@ -4288,7 +4417,7 @@ int st_select_lex_unit::print_explain(select_result_sink *output,
}
return res;
}
#endif
/**
A routine used by the parser to decide whether we are specifying a full
......
......@@ -617,7 +617,7 @@ class select_result;
class JOIN;
class select_union;
class Procedure;
class QPF_query;
class st_select_lex_unit: public st_select_lex_node {
protected:
......@@ -728,8 +728,11 @@ public:
friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types();
#if 0
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
#endif
int save_qpf(QPF_query *output);
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
......@@ -1048,8 +1051,11 @@ public:
bool save_prep_leaf_tables(THD *thd);
bool is_merged_child_of(st_select_lex *ancestor);
#if 0
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
#endif
void save_qpf(QPF_query *output);
/*
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
- Non-aggregated fields are used in this select.
......@@ -2360,6 +2366,8 @@ class SQL_SELECT;
/*
Query plan of a single-table UPDATE.
(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
{
......@@ -2411,6 +2419,7 @@ public:
};
class QPF_query;
/* The state of the lex parsing. This is saved in the THD struct */
struct LEX: public Query_tables_list
......@@ -2424,6 +2433,7 @@ struct LEX: public Query_tables_list
/* For single-table DELETE: its query plan */
Update_plan *upd_del_plan;
QPF_query *query_plan_footprint;
char *length,*dec,*change;
LEX_STRING name;
......
......@@ -4811,7 +4811,15 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
if (!(result= new select_send()))
return 1; /* purecov: inspected */
thd->send_explain_fields(result);
thd->lex->query_plan_footprint= new QPF_query;
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
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);
int rr_sequential_and_unpack(READ_RECORD *info);
#include "opt_qpf.h"
/**************************************************************************************
* New EXPLAIN structures END
*************************************************************************************/
class JOIN_CACHE;
class SJ_TMP_TABLE;
class JOIN_TAB_RANGE;
......@@ -252,7 +258,9 @@ typedef struct st_join_table {
JOIN_TAB_RANGE *bush_children;
/* 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'
column, or 0 if there is no info.
......@@ -1454,11 +1462,14 @@ public:
{
return (unit->item && unit->item->is_in_predicate());
}
/*
int print_explain(select_result_sink *result, uint8 explain_flags,
bool on_the_fly,
bool need_tmp_table, bool need_order,
bool distinct,const char *message);
*/
int save_qpf(QPF_query *output, bool need_tmp_table, bool need_order,
bool distinct, const char *message);
private:
/**
TRUE if the query contains an aggregate function but has no GROUP
......
......@@ -6,7 +6,7 @@ filter='\.cc$\|\.c$\|\.h$\|\.yy$'
list="find . -type f"
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
etags -o TAGS --append $f
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