259 lines
8.4 KiB
C++
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;
|
|
}
|