mysql5/mysql-5.7.27/sql/opt_explain_traditional.cc

259 lines
8.4 KiB
C++

/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
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,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
#include "opt_explain_traditional.h"
/**
Heads of "extra" column parts
This array must be in sync with Extra_tag enum.
*/
static const char *traditional_extra_tags[ET_total]=
{
NULL, // ET_none
"Using temporary", // ET_USING_TEMPORARY
"Using filesort", // ET_USING_FILESORT
"Using index condition", // ET_USING_INDEX_CONDITION
"Using", // ET_USING
"Range checked for each record", // ET_RANGE_CHECKED_FOR_EACH_RECORD
"Using where with pushed condition", // ET_USING_WHERE_WITH_PUSHED_CONDITION
"Using where", // ET_USING_WHERE
"Not exists", // ET_NOT_EXISTS
"Using MRR", // ET_USING_MRR
"Using index", // ET_USING_INDEX
"Full scan on NULL key", // ET_FULL_SCAN_ON_NULL_KEY
"Skip_open_table", // ET_SKIP_OPEN_TABLE
"Open_frm_only", // ET_OPEN_FRM_ONLY
"Open_full_table", // ET_OPEN_FULL_TABLE
"Scanned", // ET_SCANNED_DATABASES
"Using index for group-by", // ET_USING_INDEX_FOR_GROUP_BY
"Distinct", // ET_DISTINCT
"LooseScan", // ET_LOOSESCAN
"Start temporary", // ET_START_TEMPORARY
"End temporary", // ET_END_TEMPORARY
"FirstMatch", // ET_FIRST_MATCH
"Materialize", // ET_MATERIALIZE
"Start materialize", // ET_START_MATERIALIZE
"End materialize", // ET_END_MATERIALIZE
"Scan", // ET_SCAN
"Using join buffer", // ET_USING_JOIN_BUFFER
"const row not found", // ET_CONST_ROW_NOT_FOUND
"unique row not found", // ET_UNIQUE_ROW_NOT_FOUND
"Impossible ON condition", // ET_IMPOSSIBLE_ON_CONDITION
"", // ET_PUSHED_JOIN
"Ft_hints:" // ET_FT_HINTS
};
static const char *mod_type_name[]=
{
"NONE", "INSERT", "UPDATE", "DELETE", "REPLACE"
};
bool Explain_format_traditional::send_headers(Query_result *result)
{
return ((nil= new Item_null) == NULL ||
Explain_format::send_headers(result) ||
current_thd->send_explain_fields(output));
}
static bool push(List<Item> *items, qep_row::mem_root_str &s,
Item_null *nil)
{
if (s.is_empty())
return items->push_back(nil);
Item_string *item= new Item_string(s.str, s.length, system_charset_info);
return item == NULL || items->push_back(item);
}
static bool push(List<Item> *items, const char *s, size_t length)
{
Item_string *item= new Item_string(s, length, system_charset_info);
return item == NULL || items->push_back(item);
}
static bool push(List<Item> *items, List<const char> &c, Item_null *nil)
{
if (c.is_empty())
return items->push_back(nil);
StringBuffer<1024> buff;
List_iterator<const char> it(c);
const char *s;
while((s= it++))
{
buff.append(s);
buff.append(",");
}
if (!buff.is_empty())
buff.length(buff.length() - 1); // remove last ","
Item_string *item= new Item_string(buff.dup(current_thd->mem_root),
buff.length(), system_charset_info);
return item == NULL || items->push_back(item);
}
static bool push(List<Item> *items, const qep_row::column<uint> &c,
Item_null *nil)
{
if (c.is_empty())
return items->push_back(nil);
Item_uint *item= new Item_uint(c.get());
return item == NULL || items->push_back(item);
}
static bool push(List<Item> *items, const qep_row::column<ulonglong> &c,
Item_null *nil)
{
if (c.is_empty())
return items->push_back(nil);
Item_int *item= new Item_int(c.get(), MY_INT64_NUM_DECIMAL_DIGITS);
return item == NULL || items->push_back(item);
}
static bool push(List<Item> *items, const qep_row::column<float> &c,
Item_null *nil)
{
if (c.is_empty())
return items->push_back(nil);
Item_float *item= new Item_float(c.get(), 2);
return item == NULL || items->push_back(item);
}
bool Explain_format_traditional::push_select_type(List<Item> *items)
{
DBUG_ASSERT(!column_buffer.col_select_type.is_empty());
StringBuffer<32> buff;
if (column_buffer.is_dependent)
{
if (buff.append(STRING_WITH_LEN("DEPENDENT "), system_charset_info))
return true;
}
else if (!column_buffer.is_cacheable)
{
if (buff.append(STRING_WITH_LEN("UNCACHEABLE "), system_charset_info))
return true;
}
const SELECT_LEX::type_enum sel_type= column_buffer.col_select_type.get();
const char *type= (column_buffer.mod_type != MT_NONE &&
(sel_type == SELECT_LEX::SLT_PRIMARY ||
sel_type == SELECT_LEX::SLT_SIMPLE)) ?
mod_type_name[column_buffer.mod_type] :
SELECT_LEX::get_type_str(sel_type);
if (buff.append(type))
return true;
Item_string *item= new Item_string(buff.dup(current_thd->mem_root),
buff.length(), system_charset_info);
return item == NULL || items->push_back(item);
}
class Buffer_cleanup
{
public:
explicit Buffer_cleanup(qep_row *row)
: m_row(row)
{}
~Buffer_cleanup()
{
m_row->cleanup();
}
private:
qep_row *m_row;
};
bool Explain_format_traditional::flush_entry()
{
Buffer_cleanup bc(&column_buffer); // release column_buffer
List<Item> items;
if (push(&items, column_buffer.col_id, nil) ||
push_select_type(&items) ||
push(&items, column_buffer.col_table_name, nil) ||
push(&items, column_buffer.col_partitions, nil) ||
push(&items, column_buffer.col_join_type, nil) ||
push(&items, column_buffer.col_possible_keys, nil) ||
push(&items, column_buffer.col_key, nil) ||
push(&items, column_buffer.col_key_len, nil) ||
push(&items, column_buffer.col_ref, nil) ||
push(&items, column_buffer.col_rows, nil) ||
push(&items, column_buffer.col_filtered, nil))
return true;
if (column_buffer.col_message.is_empty() &&
column_buffer.col_extra.is_empty())
{
if (items.push_back(nil))
return true;
}
else if (!column_buffer.col_extra.is_empty())
{
StringBuffer<64> buff(system_charset_info);
List_iterator<qep_row::extra> it(column_buffer.col_extra);
qep_row::extra *e;
while ((e= it++))
{
DBUG_ASSERT(traditional_extra_tags[e->tag] != NULL);
if (buff.append(traditional_extra_tags[e->tag]))
return true;
if (e->data)
{
bool brackets= false;
switch (e->tag) {
case ET_RANGE_CHECKED_FOR_EACH_RECORD:
case ET_USING_INDEX_FOR_GROUP_BY:
case ET_USING_JOIN_BUFFER:
case ET_FIRST_MATCH:
brackets= true; // for backward compatibility
break;
default:
break;
}
if (e->tag != ET_FIRST_MATCH && // for backward compatibility
e->tag != ET_PUSHED_JOIN &&
buff.append(" "))
return true;
if (brackets && buff.append("("))
return true;
if (buff.append(e->data))
return true;
if (e->tag == ET_SCANNED_DATABASES &&
buff.append(e->data[0] == '1' ? " database" : " databases"))
return true;
if (brackets && buff.append(")"))
return true;
}
if (buff.append("; "))
return true;
}
if (!buff.is_empty())
buff.length(buff.length() - 2); // remove last "; "
if (push(&items, buff.dup(current_thd->mem_root), buff.length()))
return true;
}
else
{
if (push(&items, column_buffer.col_message, nil))
return true;
}
if (output->send_data(items))
return true;
return false;
}