diff options
Diffstat (limited to 'sql/sql_explain.h')
-rw-r--r-- | sql/sql_explain.h | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/sql/sql_explain.h b/sql/sql_explain.h new file mode 100644 index 00000000..c71ba3a6 --- /dev/null +++ b/sql/sql_explain.h @@ -0,0 +1,1064 @@ +/* + Copyright (c) 2013 Monty Program Ab + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + +== EXPLAIN/ANALYZE architecture == + +=== [SHOW] EXPLAIN data === +Query optimization produces two data structures: +1. execution data structures themselves (eg. JOINs, JOIN_TAB, etc, etc) +2. Explain data structures. + +#2 are self contained set of data structures that has sufficient info to +produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or +ANALYZE [FORMAT=JSON], without accessing the execution data structures. + +The exception is that Explain data structures have Item* pointers. See +ExplainDataStructureLifetime below for details. + +=== ANALYZE data === +EXPLAIN data structures have embedded ANALYZE data structures. These are +objects that are used to track how the parts of query plan were executed: +how many times each part of query plan was invoked, how many rows were +read/returned, etc. + +Each execution data structure keeps a direct pointer to its ANALYZE data +structure. It is needed so that execution code can quickly increment the +counters. + +(note that this increases the set of data that is frequently accessed +during the execution. What is the impact of this?) + +Since ANALYZE/EXPLAIN data structures are separated from execution data +structures, it is easy to have them survive until the end of the query, +where we can return ANALYZE [FORMAT=JSON] output to the user, or print +it into the slow query log. + +*/ + +#ifndef SQL_EXPLAIN_INCLUDED +#define SQL_EXPLAIN_INCLUDED + +class String_list: public List<char> +{ +public: + const char *append_str(MEM_ROOT *mem_root, const char *str); +}; + +class Json_writer; + +/************************************************************************************** + + Data structures for producing EXPLAIN outputs. + + These structures + - Can be produced inexpensively from query plan. + - Store sufficient information to produce tabular EXPLAIN output (the goal is + to be able to produce JSON also) + +*************************************************************************************/ + + + +class Explain_query; + +/* + A node can be either a SELECT, or a UNION. +*/ +class Explain_node : public Sql_alloc +{ +public: + Explain_node(MEM_ROOT *root) : + cache_tracker(NULL), + connection_type(EXPLAIN_NODE_OTHER), + children(root) + {} + /* A type specifying what kind of node this is */ + enum explain_node_type + { + EXPLAIN_UNION, + EXPLAIN_SELECT, + EXPLAIN_BASIC_JOIN, + EXPLAIN_UPDATE, + EXPLAIN_DELETE, + EXPLAIN_INSERT + }; + + /* How this node is connected */ + enum explain_connection_type { + EXPLAIN_NODE_OTHER, + EXPLAIN_NODE_DERIVED, /* Materialized derived table */ + EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */ + }; + + virtual enum explain_node_type get_type()= 0; + virtual uint get_select_id()= 0; + + /** + expression cache statistics + */ + Expression_cache_tracker* cache_tracker; + + /* + How this node is connected to its parent. + (NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently) + */ + enum explain_connection_type connection_type; + +protected: + /* + A node may have children nodes. When a node's explain structure is + created, children nodes may not yet have QPFs. This is why we store ids. + */ + Dynamic_array<int> children; +public: + void add_child(int select_no) + { + children.append(select_no); + } + + virtual int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze)=0; + virtual void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze)= 0; + + int print_explain_for_children(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + void print_explain_json_for_children(Explain_query *query, + Json_writer *writer, bool is_analyze); + bool print_explain_json_cache(Json_writer *writer, bool is_analyze); + virtual ~Explain_node() = default; +}; + + +class Explain_table_access; + + +/* + A basic join. This is only used for SJ-Materialization nests. + + Basic join doesn't have ORDER/GROUP/DISTINCT operations. It also cannot be + degenerate. + + It has its own select_id. +*/ +class Explain_basic_join : public Explain_node +{ +public: + enum explain_node_type get_type() { return EXPLAIN_BASIC_JOIN; } + + Explain_basic_join(MEM_ROOT *root) : Explain_node(root), join_tabs(NULL) {} + ~Explain_basic_join(); + + bool add_table(Explain_table_access *tab, Explain_query *query); + + uint get_select_id() { return select_id; } + + uint select_id; + + int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); + + void print_explain_json_interns(Explain_query *query, Json_writer *writer, + bool is_analyze); + + /* A flat array of Explain structs for tables. */ + Explain_table_access** join_tabs; + uint n_join_tabs; +}; + + +class Explain_aggr_node; +/* + EXPLAIN structure for a SELECT. + + A select can be: + 1. A degenerate case. In this case, message!=NULL, and it contains a + description of what kind of degenerate case it is (e.g. "Impossible + WHERE"). + 2. a non-degenrate join. In this case, join_tabs describes the join. + + In the non-degenerate case, a SELECT may have a GROUP BY/ORDER BY operation. + + In both cases, the select may have children nodes. class Explain_node + provides a way get node's children. +*/ + +class Explain_select : public Explain_basic_join +{ +public: + enum explain_node_type get_type() { return EXPLAIN_SELECT; } + + Explain_select(MEM_ROOT *root, bool is_analyze) : + Explain_basic_join(root), +#ifndef DBUG_OFF + select_lex(NULL), +#endif + linkage(UNSPECIFIED_TYPE), + is_lateral(false), + message(NULL), + having(NULL), having_value(Item::COND_UNDEF), + using_temporary(false), using_filesort(false), + time_tracker(is_analyze), + aggr_tree(NULL) + {} + + void add_linkage(Json_writer *writer); + +public: +#ifndef DBUG_OFF + SELECT_LEX *select_lex; +#endif + const char *select_type; + enum sub_select_type linkage; + bool is_lateral; + + /* + If message != NULL, this is a degenerate join plan, and all subsequent + members have no info + */ + const char *message; + + /* Expensive constant condition */ + Item *exec_const_cond; + Item *outer_ref_cond; + Item *pseudo_bits_cond; + + /* HAVING condition */ + Item *having; + Item::cond_result having_value; + + /* Global join attributes. In tabular form, they are printed on the first row */ + bool using_temporary; + bool using_filesort; + + /* ANALYZE members */ + Time_and_counter_tracker time_tracker; + + /* + Part of query plan describing sorting, temp.table usage, and duplicate + removal + */ + Explain_aggr_node* aggr_tree; + + int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); + + Table_access_tracker *get_using_temporary_read_tracker() + { + return &using_temporary_read_tracker; + } +private: + Table_access_tracker using_temporary_read_tracker; +}; + +///////////////////////////////////////////////////////////////////////////// +// EXPLAIN structures for ORDER/GROUP operations. +///////////////////////////////////////////////////////////////////////////// +typedef enum +{ + AGGR_OP_TEMP_TABLE, + AGGR_OP_FILESORT, + //AGGR_OP_READ_SORTED_FILE, // need this? + AGGR_OP_REMOVE_DUPLICATES, + AGGR_OP_WINDOW_FUNCS + //AGGR_OP_JOIN // Need this? +} enum_explain_aggr_node_type; + + +class Explain_aggr_node : public Sql_alloc +{ +public: + virtual enum_explain_aggr_node_type get_type()= 0; + virtual ~Explain_aggr_node() = default; + Explain_aggr_node *child; +}; + +class Explain_aggr_filesort : public Explain_aggr_node +{ + List<Item> sort_items; + List<ORDER::enum_order> sort_directions; +public: + enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; } + Filesort_tracker tracker; + + Explain_aggr_filesort(MEM_ROOT *mem_root, bool is_analyze, + Filesort *filesort); + + void print_json_members(Json_writer *writer, bool is_analyze); +}; + +class Explain_aggr_tmp_table : public Explain_aggr_node +{ +public: + enum_explain_aggr_node_type get_type() { return AGGR_OP_TEMP_TABLE; } +}; + +class Explain_aggr_remove_dups : public Explain_aggr_node +{ +public: + enum_explain_aggr_node_type get_type() { return AGGR_OP_REMOVE_DUPLICATES; } +}; + +class Explain_aggr_window_funcs : public Explain_aggr_node +{ + List<Explain_aggr_filesort> sorts; +public: + enum_explain_aggr_node_type get_type() { return AGGR_OP_WINDOW_FUNCS; } + + void print_json_members(Json_writer *writer, bool is_analyze); + friend class Window_funcs_computation; +}; + +///////////////////////////////////////////////////////////////////////////// + +extern const char *unit_operation_text[4]; +extern const char *pushed_derived_text; +extern const char *pushed_select_text; + +/* + Explain structure for a UNION [ALL]. + + A UNION may or may not have "Using filesort". +*/ + +class Explain_union : public Explain_node +{ +public: + Explain_union(MEM_ROOT *root, bool is_analyze) : + Explain_node(root), union_members(PSI_INSTRUMENT_MEM), + is_recursive_cte(false), + fake_select_lex_explain(root, is_analyze) + {} + + enum explain_node_type get_type() { return EXPLAIN_UNION; } + unit_common_op operation; + + uint get_select_id() + { + DBUG_ASSERT(union_members.elements() > 0); + return union_members.at(0); + } + /* + Members of the UNION. Note: these are different from UNION's "children". + Example: + + (select * from t1) union + (select * from t2) order by (select col1 from t3 ...) + + here + - select-from-t1 and select-from-t2 are "union members", + - select-from-t3 is the only "child". + */ + Dynamic_array<int> union_members; + + void add_select(int select_no) + { + union_members.append(select_no); + } + int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); + + const char *fake_select_type; + bool using_filesort; + bool using_tmp; + bool is_recursive_cte; + + /* + Explain data structure for "fake_select_lex" (i.e. for the degenerate + SELECT that reads UNION result). + It doesn't have a query plan, but we still need execution tracker, etc. + */ + Explain_select fake_select_lex_explain; + + Table_access_tracker *get_fake_select_lex_tracker() + { + return &fake_select_lex_tracker; + } + Table_access_tracker *get_tmptable_read_tracker() + { + return &tmptable_read_tracker; + } +private: + uint make_union_table_name(char *buf); + + Table_access_tracker fake_select_lex_tracker; + /* This one is for reading after ORDER BY */ + Table_access_tracker tmptable_read_tracker; +}; + + +class Explain_update; +class Explain_delete; +class Explain_insert; + + +/* + Explain structure for a query (i.e. a statement). + + This should be able to survive when the query plan was deleted. Currently, + we do not intend for it survive until after query's MEM_ROOT is freed. + + == ExplainDataStructureLifetime == + + >dispatch_command + | >mysql_parse + | | ... + | | + | | explain->query_plan_ready(); // (1) + | | + | | some_join->cleanup(); // (2) + | | + | | explain->notify_tables_are_closed(); // (3) + | | close_thread_tables(); // (4) + | | ... + | | free_items(); // (5) + | | ... + | | + | <mysql_parse + | + | log_slow_statement() // (6) + | + | free_root() + | + >dispatch_command + + (1) - Query plan construction is finished and it is available for reading. + + (2) - Temporary tables are freed (with exception of derived tables + which are freed at step (4)). + The tables are no longer accessible but one can still call + item->print(), even for items that refer to temp.tables (see + Item_field::print() for details) + + (3) - Notification about (4). + (4) - Tables used by the query are closed. One consequence of this is that + the values of the const tables' fields are not available anymore. + We could adjust the code in Item_field::print() to handle this but + instead we make step (3) disallow production of FORMAT=JSON output. + We also disable processing of SHOW EXPLAIN|ANALYZE output because + the query is about to finish anyway. + + (5) - Item objects are freed. After this, it's certainly not possible to + print them into FORMAT=JSON output. + + (6) - We may decide to log tabular EXPLAIN output to the slow query log. + +*/ + +class Explain_query : public Sql_alloc +{ +public: + Explain_query(THD *thd, MEM_ROOT *root); + ~Explain_query(); + + /* Add a new node */ + void add_node(Explain_node *node); + void add_insert_plan(Explain_insert *insert_plan_arg); + void add_upd_del_plan(Explain_update *upd_del_plan_arg); + + /* This will return a select, or a union */ + Explain_node *get_node(uint select_id); + + /* This will return a select (even if there is a union with this id) */ + Explain_select *get_select(uint select_id); + + Explain_union *get_union(uint select_id); + + /* Produce a tabular EXPLAIN output */ + int print_explain(select_result_sink *output, uint8 explain_flags, + bool is_analyze); + + /* Send tabular EXPLAIN to the client */ + int send_explain(THD *thd, bool extended); + + /* Return tabular EXPLAIN output as a text string */ + bool print_explain_str(THD *thd, String *out_str, bool is_analyze); + + int print_explain_json(select_result_sink *output, bool is_analyze, + ulonglong query_time_in_progress_ms= 0); + + /* If true, at least part of EXPLAIN can be printed */ + bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } + + void query_plan_ready(); + void notify_tables_are_closed(); + + MEM_ROOT *mem_root; + + Explain_update *get_upd_del_plan() { return upd_del_plan; } +private: + bool print_query_blocks_json(Json_writer *writer, const bool is_analyze); + void print_query_optimization_json(Json_writer *writer); + void send_explain_json_to_output(Json_writer *writer, select_result_sink *output); + + /* Explain_delete inherits from Explain_update */ + Explain_update *upd_del_plan; + + /* Query "plan" for INSERTs */ + Explain_insert *insert_plan; + + Dynamic_array<Explain_union*> unions; + Dynamic_array<Explain_select*> selects; + + THD *stmt_thd; // for APC start/stop + bool apc_enabled; + /* + Debugging aid: count how many times add_node() was called. Ideally, it + should be one, we currently allow O(1) query plan saves for each + select or union. The goal is not to have O(#rows_in_some_table), which + is unacceptable. + */ + longlong operations; +#ifndef DBUG_OFF + bool can_print_json= false; +#endif + + Exec_time_tracker optimization_time_tracker; +}; + + +/* + Some of the tags have matching text. See extra_tag_text for text names, and + Explain_table_access::append_tag_name() for code to convert from tag form to text + form. +*/ +enum explain_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_TABLE_FUNCTION, + + ET_total +}; + + +/* + Explain data structure describing join buffering use. +*/ + +class EXPLAIN_BKA_TYPE +{ +public: + EXPLAIN_BKA_TYPE() : join_alg(NULL) {} + + size_t join_buffer_size; + + bool incremental; + + /* + NULL if no join buferring used. + Other values: BNL, BNLH, BKA, BKAH. + */ + const char *join_alg; + + /* Information about MRR usage. */ + StringBuffer<64> mrr_type; + + bool is_using_jbuf() { return (join_alg != NULL); } +}; + + +/* + Data about how an index is used by some access method +*/ +class Explain_index_use : public Sql_alloc +{ + char *key_name; + uint key_len; + char *filter_name; + uint filter_len; +public: + String_list key_parts_list; + + Explain_index_use() + { + clear(); + } + + void clear() + { + key_name= NULL; + key_len= (uint)-1; + filter_name= NULL; + filter_len= (uint)-1; + } + bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); + bool set_pseudo_key(MEM_ROOT *root, const char *key_name); + + inline const char *get_key_name() const { return key_name; } + inline uint get_key_len() const { return key_len; } + //inline const char *get_filter_name() const { return filter_name; } +}; + + +/* + Query Plan data structure for Rowid filter. +*/ +class Explain_rowid_filter : public Sql_alloc +{ +public: + /* Quick select used to collect the rowids into filter */ + Explain_quick_select *quick; + + /* How many rows the above quick select is expected to return */ + ha_rows rows; + + /* Expected selectivity for the filter */ + double selectivity; + + /* Tracker with the information about how rowid filter is executed */ + Rowid_filter_tracker *tracker; + + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); + + /* + TODO: + Here should be ANALYZE members: + - r_rows for the quick select + - An object that tracked the table access time + - real selectivity of the filter. + */ +}; + + +/* + QPF for quick range selects, as well as index_merge select +*/ +class Explain_quick_select : public Sql_alloc +{ +public: + Explain_quick_select(int quick_type_arg) : quick_type(quick_type_arg) + {} + + const int quick_type; + + bool is_basic() + { + return (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE || + quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC || + quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX); + } + + /* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */ + Explain_index_use range; + + /* Used in all other cases */ + List<Explain_quick_select> children; + + void print_extra(String *str); + void print_key(String *str); + void print_key_len(String *str); + + void print_json(Json_writer *writer); + + void print_extra_recursive(String *str); +private: + const char *get_name_by_type(); +}; + + +/* + Data structure for "range checked for each record". + It's a set of keys, tabular explain prints hex bitmap, json prints key names. +*/ + +typedef const char* NAME; + +class Explain_range_checked_fer : public Sql_alloc +{ +public: + String_list key_set; + key_map keys_map; +private: + ha_rows full_scan, index_merge; + ha_rows *keys_stat; + NAME *keys_stat_names; + uint keys; + +public: + Explain_range_checked_fer() + :Sql_alloc(), full_scan(0), index_merge(0), + keys_stat(0), keys_stat_names(0), keys(0) + {} + + int append_possible_keys_stat(MEM_ROOT *alloc, + TABLE *table, key_map possible_keys); + void collect_data(QUICK_SELECT_I *quick); + void print_json(Json_writer *writer, bool is_analyze); +}; + + +/* + EXPLAIN data structure for a single JOIN_TAB. +*/ + +class Explain_table_access : public Sql_alloc +{ +public: + Explain_table_access(MEM_ROOT *root, bool timed) : + derived_select_number(0), + non_merged_sjm_number(0), + extra_tags(root), + range_checked_fer(NULL), + full_scan_on_null_key(false), + start_dups_weedout(false), + end_dups_weedout(false), + where_cond(NULL), + cache_cond(NULL), + pushed_index_cond(NULL), + sjm_nest(NULL), + pre_join_sort(NULL), + handler_for_stats(NULL), + jbuf_unpack_tracker(timed), + rowid_filter(NULL) + {} + ~Explain_table_access() { delete sjm_nest; } + + void push_extra(enum explain_extra_tag extra_tag); + + /* Internals */ + + /* id and 'select_type' are cared-of by the parent Explain_select */ + StringBuffer<32> table_name; + StringBuffer<32> used_partitions; + String_list used_partitions_list; + // valid with ET_USING_MRR + StringBuffer<32> mrr_type; + StringBuffer<32> firstmatch_table_name; + + /* + Non-zero number means this is a derived table. The number can be used to + find the query plan for the derived table + */ + int derived_select_number; + /* TODO: join with the previous member. */ + int non_merged_sjm_number; + + enum join_type type; + + bool used_partitions_set; + + /* Empty means "NULL" will be printed */ + String_list possible_keys; + + bool rows_set; /* not set means 'NULL' should be printed */ + bool filtered_set; /* not set means 'NULL' should be printed */ + // Valid if ET_USING_INDEX_FOR_GROUP_BY is present + bool loose_scan_is_scanning; + + /* + Index use: key name and length. + Note: that when one is accessing I_S tables, those may show use of + non-existant indexes. + + key.key_name == NULL means 'NULL' will be shown in tabular output. + key.key_len == (uint)-1 means 'NULL' will be shown in tabular output. + */ + Explain_index_use key; + + /* + when type==JT_HASH_NEXT, 'key' stores the hash join pseudo-key. + hash_next_key stores the table's key. + */ + Explain_index_use hash_next_key; + + String_list ref_list; + + ha_rows rows; + double filtered; + + /* + Contents of the 'Extra' column. Some are converted into strings, some have + parameters, values for which are stored below. + */ + Dynamic_array<enum explain_extra_tag> extra_tags; + + // Valid if ET_USING tag is present + Explain_quick_select *quick_info; + + /* Non-NULL value means this tab uses "range checked for each record" */ + Explain_range_checked_fer *range_checked_fer; + + bool full_scan_on_null_key; + + // valid with ET_USING_JOIN_BUFFER + EXPLAIN_BKA_TYPE bka_type; + + bool start_dups_weedout; + bool end_dups_weedout; + + /* + Note: lifespan of WHERE condition is less than lifespan of this object. + The below two are valid if tags include "ET_USING_WHERE". + (TODO: indexsubquery may put ET_USING_WHERE without setting where_cond?) + */ + Item *where_cond; + Item *cache_cond; + + /* + This is either pushed index condition, or BKA's index condition. + (the latter refers to columns of other tables and so can only be checked by + BKA code). Examine extra_tags to tell which one it is. + */ + Item *pushed_index_cond; + + Explain_basic_join *sjm_nest; + + /* + This describes a possible filesort() call that is done before doing the + join operation. + */ + Explain_aggr_filesort *pre_join_sort; + + /* ANALYZE members */ + + /* Tracker for reading the table */ + Table_access_tracker tracker; + Exec_time_tracker op_tracker; + Gap_time_tracker extra_time_tracker; + + /* + Handler object to get the handler_stats from. + + Notes: + This pointer is only valid until notify_tables_are_closed() is called. + After that, the tables may be freed or reused, together with their + handler_stats objects. + notify_tables_are_closed() disables printing of FORMAT=JSON output. + r_engine_stats is only printed in FORMAT=JSON output, so we're fine. + + We do not store pointers to temporary (aka "work") tables here. + Temporary tables may be freed (e.g. by JOIN::cleanup()) or re-created + during query execution (when HEAP table is converted into Aria). + */ + handler *handler_for_stats; + + /* When using join buffer: Track the reads from join buffer */ + Table_access_tracker jbuf_tracker; + + /* When using join buffer: time spent unpacking rows from the join buffer */ + Time_and_counter_tracker jbuf_unpack_tracker; + + /* + When using join buffer: time spent after unpacking rows from the join + buffer. This will capture the time spent checking the Join Condition: + the condition that depends on this table and preceding tables. + */ + Gap_time_tracker jbuf_extra_time_tracker; + + /* When using join buffer: Track the number of incoming record combinations */ + Counter_tracker jbuf_loops_tracker; + + Explain_rowid_filter *rowid_filter; + + int print_explain(select_result_sink *output, uint8 explain_flags, + bool is_analyze, + uint select_id, const char *select_type, + bool using_temporary, bool using_filesort); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); + +private: + void append_tag_name(String *str, enum explain_extra_tag tag); + void fill_key_str(String *key_str, bool is_json) const; + void fill_key_len_str(String *key_len_str, bool is_json) const; + double get_r_filtered(); + void tag_to_json(Json_writer *writer, enum explain_extra_tag tag); +}; + + +/* + EXPLAIN structure for single-table UPDATE. + + This is similar to Explain_table_access, except that it is more restrictive. + Also, it can have UPDATE operation options, but currently there aren't any. + + Explain_delete inherits from this. +*/ + +class Explain_update : public Explain_node +{ +public: + + Explain_update(MEM_ROOT *root, bool is_analyze) : + Explain_node(root), + filesort_tracker(NULL), + command_tracker(is_analyze), + handler_for_stats(NULL) + {} + + virtual enum explain_node_type get_type() { return EXPLAIN_UPDATE; } + virtual uint get_select_id() { return 1; /* always root */ } + + const char *select_type; + + StringBuffer<32> used_partitions; + String_list used_partitions_list; + bool used_partitions_set; + + bool impossible_where; + bool no_partitions; + StringBuffer<64> table_name; + + enum join_type jtype; + String_list possible_keys; + + /* Used key when doing a full index scan (possibly with limit) */ + Explain_index_use key; + + /* + MRR that's used with quick select. This should probably belong to the + quick select + */ + StringBuffer<64> mrr_type; + + Explain_quick_select *quick_info; + + bool using_where; + Item *where_cond; + + ha_rows rows; + + bool using_io_buffer; + + /* Tracker for doing reads when filling the buffer */ + Table_access_tracker buf_tracker; + + bool is_using_filesort() { return filesort_tracker? true: false; } + /* + Non-null value of filesort_tracker means "using filesort" + + if we are using filesort, then table_tracker is for the io done inside + filesort. + + 'tracker' is for tracking post-filesort reads. + */ + Filesort_tracker *filesort_tracker; + + /* ANALYZE members and methods */ + Table_access_tracker tracker; + + /* This tracks execution of the whole command */ + Time_and_counter_tracker command_tracker; + + /* TODO: This tracks time to read rows from the table */ + Exec_time_tracker table_tracker; + + /* The same as Explain_table_access::handler_for_stats */ + handler *handler_for_stats; + + virtual int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + virtual void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); +}; + + +/* + EXPLAIN data structure for an INSERT. + + At the moment this doesn't do much as we don't really have any query plans + for INSERT statements. +*/ + +class Explain_insert : public Explain_node +{ +public: + Explain_insert(MEM_ROOT *root) : + Explain_node(root) + {} + + StringBuffer<64> table_name; + + enum explain_node_type get_type() { return EXPLAIN_INSERT; } + uint get_select_id() { return 1; /* always root */ } + + int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); +}; + + +/* + EXPLAIN data of a single-table DELETE. +*/ + +class Explain_delete: public Explain_update +{ +public: + Explain_delete(MEM_ROOT *root, bool is_analyze) : + Explain_update(root, is_analyze) + {} + + /* + TRUE means we're going to call handler->delete_all_rows() and not read any + rows. + */ + bool deleting_all_rows; + + virtual enum explain_node_type get_type() { return EXPLAIN_DELETE; } + virtual uint get_select_id() { return 1; /* always root */ } + + virtual int print_explain(Explain_query *query, select_result_sink *output, + uint8 explain_flags, bool is_analyze); + virtual void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); +}; + + +#endif //SQL_EXPLAIN_INCLUDED |