| 1 | /* |
| 2 | Copyright (c) 2013 Monty Program Ab |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
| 16 | |
| 17 | /* |
| 18 | |
| 19 | == EXPLAIN/ANALYZE architecture == |
| 20 | |
| 21 | === [SHOW] EXPLAIN data === |
| 22 | Query optimization produces two data structures: |
| 23 | 1. execution data structures themselves (eg. JOINs, JOIN_TAB, etc, etc) |
| 24 | 2. Explain data structures. |
| 25 | |
| 26 | #2 are self contained set of data structures that has sufficient info to |
| 27 | produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or |
| 28 | ANALYZE [FORMAT=JSON], without accessing the execution data structures. |
| 29 | |
| 30 | (the only exception is that Explain data structures keep Item* pointers, |
| 31 | and we require that one might call item->print(QT_EXPLAIN) when printing |
| 32 | FORMAT=JSON output) |
| 33 | |
| 34 | === ANALYZE data === |
| 35 | EXPLAIN data structures have embedded ANALYZE data structures. These are |
| 36 | objects that are used to track how the parts of query plan were executed: |
| 37 | how many times each part of query plan was invoked, how many rows were |
| 38 | read/returned, etc. |
| 39 | |
| 40 | Each execution data structure keeps a direct pointer to its ANALYZE data |
| 41 | structure. It is needed so that execution code can quickly increment the |
| 42 | counters. |
| 43 | |
| 44 | (note that this increases the set of data that is frequently accessed |
| 45 | during the execution. What is the impact of this?) |
| 46 | |
| 47 | Since ANALYZE/EXPLAIN data structures are separated from execution data |
| 48 | structures, it is easy to have them survive until the end of the query, |
| 49 | where we can return ANALYZE [FORMAT=JSON] output to the user, or print |
| 50 | it into the slow query log. |
| 51 | |
| 52 | */ |
| 53 | |
| 54 | #ifndef SQL_EXPLAIN_INCLUDED |
| 55 | #define SQL_EXPLAIN_INCLUDED |
| 56 | |
| 57 | class String_list: public List<char> |
| 58 | { |
| 59 | public: |
| 60 | const char *append_str(MEM_ROOT *mem_root, const char *str); |
| 61 | }; |
| 62 | |
| 63 | class Json_writer; |
| 64 | |
| 65 | /************************************************************************************** |
| 66 | |
| 67 | Data structures for producing EXPLAIN outputs. |
| 68 | |
| 69 | These structures |
| 70 | - Can be produced inexpensively from query plan. |
| 71 | - Store sufficient information to produce tabular EXPLAIN output (the goal is |
| 72 | to be able to produce JSON also) |
| 73 | |
| 74 | *************************************************************************************/ |
| 75 | |
| 76 | |
| 77 | const int FAKE_SELECT_LEX_ID= (int)UINT_MAX; |
| 78 | |
| 79 | class Explain_query; |
| 80 | |
| 81 | /* |
| 82 | A node can be either a SELECT, or a UNION. |
| 83 | */ |
| 84 | class Explain_node : public Sql_alloc |
| 85 | { |
| 86 | public: |
| 87 | Explain_node(MEM_ROOT *root) : |
| 88 | cache_tracker(NULL), |
| 89 | connection_type(EXPLAIN_NODE_OTHER), |
| 90 | children(root) |
| 91 | {} |
| 92 | /* A type specifying what kind of node this is */ |
| 93 | enum explain_node_type |
| 94 | { |
| 95 | EXPLAIN_UNION, |
| 96 | EXPLAIN_SELECT, |
| 97 | EXPLAIN_BASIC_JOIN, |
| 98 | EXPLAIN_UPDATE, |
| 99 | EXPLAIN_DELETE, |
| 100 | EXPLAIN_INSERT |
| 101 | }; |
| 102 | |
| 103 | /* How this node is connected */ |
| 104 | enum explain_connection_type { |
| 105 | EXPLAIN_NODE_OTHER, |
| 106 | EXPLAIN_NODE_DERIVED, /* Materialized derived table */ |
| 107 | EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */ |
| 108 | }; |
| 109 | |
| 110 | virtual enum explain_node_type get_type()= 0; |
| 111 | virtual int get_select_id()= 0; |
| 112 | |
| 113 | /** |
| 114 | expression cache statistics |
| 115 | */ |
| 116 | Expression_cache_tracker* cache_tracker; |
| 117 | |
| 118 | /* |
| 119 | How this node is connected to its parent. |
| 120 | (NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently) |
| 121 | */ |
| 122 | enum explain_connection_type connection_type; |
| 123 | |
| 124 | /* |
| 125 | A node may have children nodes. When a node's explain structure is |
| 126 | created, children nodes may not yet have QPFs. This is why we store ids. |
| 127 | */ |
| 128 | Dynamic_array<int> children; |
| 129 | void add_child(int select_no) |
| 130 | { |
| 131 | children.append(select_no); |
| 132 | } |
| 133 | |
| 134 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
| 135 | uint8 explain_flags, bool is_analyze)=0; |
| 136 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
| 137 | bool is_analyze)= 0; |
| 138 | |
| 139 | int print_explain_for_children(Explain_query *query, select_result_sink *output, |
| 140 | uint8 explain_flags, bool is_analyze); |
| 141 | void print_explain_json_for_children(Explain_query *query, |
| 142 | Json_writer *writer, bool is_analyze); |
| 143 | bool print_explain_json_cache(Json_writer *writer, bool is_analyze); |
| 144 | virtual ~Explain_node(){} |
| 145 | }; |
| 146 | |
| 147 | |
| 148 | class Explain_table_access; |
| 149 | |
| 150 | |
| 151 | /* |
| 152 | A basic join. This is only used for SJ-Materialization nests. |
| 153 | |
| 154 | Basic join doesn't have ORDER/GROUP/DISTINCT operations. It also cannot be |
| 155 | degenerate. |
| 156 | |
| 157 | It has its own select_id. |
| 158 | */ |
| 159 | class Explain_basic_join : public Explain_node |
| 160 | { |
| 161 | public: |
| 162 | enum explain_node_type get_type() { return EXPLAIN_BASIC_JOIN; } |
| 163 | |
| 164 | Explain_basic_join(MEM_ROOT *root) : Explain_node(root), join_tabs(NULL) {} |
| 165 | ~Explain_basic_join(); |
| 166 | |
| 167 | bool add_table(Explain_table_access *tab, Explain_query *query); |
| 168 | |
| 169 | int get_select_id() { return select_id; } |
| 170 | |
| 171 | int select_id; |
| 172 | |
| 173 | int print_explain(Explain_query *query, select_result_sink *output, |
| 174 | uint8 explain_flags, bool is_analyze); |
| 175 | void print_explain_json(Explain_query *query, Json_writer *writer, |
| 176 | bool is_analyze); |
| 177 | |
| 178 | void print_explain_json_interns(Explain_query *query, Json_writer *writer, |
| 179 | bool is_analyze); |
| 180 | |
| 181 | /* A flat array of Explain structs for tables. */ |
| 182 | Explain_table_access** join_tabs; |
| 183 | uint n_join_tabs; |
| 184 | }; |
| 185 | |
| 186 | |
| 187 | class Explain_aggr_node; |
| 188 | /* |
| 189 | EXPLAIN structure for a SELECT. |
| 190 | |
| 191 | A select can be: |
| 192 | 1. A degenerate case. In this case, message!=NULL, and it contains a |
| 193 | description of what kind of degenerate case it is (e.g. "Impossible |
| 194 | WHERE"). |
| 195 | 2. a non-degenrate join. In this case, join_tabs describes the join. |
| 196 | |
| 197 | In the non-degenerate case, a SELECT may have a GROUP BY/ORDER BY operation. |
| 198 | |
| 199 | In both cases, the select may have children nodes. class Explain_node |
| 200 | provides a way get node's children. |
| 201 | */ |
| 202 | |
| 203 | class Explain_select : public Explain_basic_join |
| 204 | { |
| 205 | public: |
| 206 | enum explain_node_type get_type() { return EXPLAIN_SELECT; } |
| 207 | |
| 208 | Explain_select(MEM_ROOT *root, bool is_analyze) : |
| 209 | Explain_basic_join(root), |
| 210 | #ifndef DBUG_OFF |
| 211 | select_lex(NULL), |
| 212 | #endif |
| 213 | linkage(UNSPECIFIED_TYPE), |
| 214 | message(NULL), |
| 215 | having(NULL), having_value(Item::COND_UNDEF), |
| 216 | using_temporary(false), using_filesort(false), |
| 217 | time_tracker(is_analyze), |
| 218 | aggr_tree(NULL) |
| 219 | {} |
| 220 | |
| 221 | void add_linkage(Json_writer *writer); |
| 222 | |
| 223 | public: |
| 224 | #ifndef DBUG_OFF |
| 225 | SELECT_LEX *select_lex; |
| 226 | #endif |
| 227 | const char *select_type; |
| 228 | enum sub_select_type linkage; |
| 229 | |
| 230 | /* |
| 231 | If message != NULL, this is a degenerate join plan, and all subsequent |
| 232 | members have no info |
| 233 | */ |
| 234 | const char *message; |
| 235 | |
| 236 | /* Expensive constant condition */ |
| 237 | Item *exec_const_cond; |
| 238 | Item *outer_ref_cond; |
| 239 | |
| 240 | /* HAVING condition */ |
| 241 | Item *having; |
| 242 | Item::cond_result having_value; |
| 243 | |
| 244 | /* Global join attributes. In tabular form, they are printed on the first row */ |
| 245 | bool using_temporary; |
| 246 | bool using_filesort; |
| 247 | |
| 248 | /* ANALYZE members */ |
| 249 | Time_and_counter_tracker time_tracker; |
| 250 | |
| 251 | /* |
| 252 | Part of query plan describing sorting, temp.table usage, and duplicate |
| 253 | removal |
| 254 | */ |
| 255 | Explain_aggr_node* aggr_tree; |
| 256 | |
| 257 | int print_explain(Explain_query *query, select_result_sink *output, |
| 258 | uint8 explain_flags, bool is_analyze); |
| 259 | void print_explain_json(Explain_query *query, Json_writer *writer, |
| 260 | bool is_analyze); |
| 261 | |
| 262 | Table_access_tracker *get_using_temporary_read_tracker() |
| 263 | { |
| 264 | return &using_temporary_read_tracker; |
| 265 | } |
| 266 | private: |
| 267 | Table_access_tracker using_temporary_read_tracker; |
| 268 | }; |
| 269 | |
| 270 | ///////////////////////////////////////////////////////////////////////////// |
| 271 | // EXPLAIN structures for ORDER/GROUP operations. |
| 272 | ///////////////////////////////////////////////////////////////////////////// |
| 273 | typedef enum |
| 274 | { |
| 275 | AGGR_OP_TEMP_TABLE, |
| 276 | AGGR_OP_FILESORT, |
| 277 | //AGGR_OP_READ_SORTED_FILE, // need this? |
| 278 | AGGR_OP_REMOVE_DUPLICATES, |
| 279 | AGGR_OP_WINDOW_FUNCS |
| 280 | //AGGR_OP_JOIN // Need this? |
| 281 | } enum_explain_aggr_node_type; |
| 282 | |
| 283 | |
| 284 | class Explain_aggr_node : public Sql_alloc |
| 285 | { |
| 286 | public: |
| 287 | virtual enum_explain_aggr_node_type get_type()= 0; |
| 288 | virtual ~Explain_aggr_node() {} |
| 289 | Explain_aggr_node *child; |
| 290 | }; |
| 291 | |
| 292 | class Explain_aggr_filesort : public Explain_aggr_node |
| 293 | { |
| 294 | List<Item> sort_items; |
| 295 | List<ORDER::enum_order> sort_directions; |
| 296 | public: |
| 297 | enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; } |
| 298 | Filesort_tracker tracker; |
| 299 | |
| 300 | Explain_aggr_filesort(MEM_ROOT *mem_root, bool is_analyze, |
| 301 | Filesort *filesort); |
| 302 | |
| 303 | void print_json_members(Json_writer *writer, bool is_analyze); |
| 304 | }; |
| 305 | |
| 306 | class Explain_aggr_tmp_table : public Explain_aggr_node |
| 307 | { |
| 308 | public: |
| 309 | enum_explain_aggr_node_type get_type() { return AGGR_OP_TEMP_TABLE; } |
| 310 | }; |
| 311 | |
| 312 | class Explain_aggr_remove_dups : public Explain_aggr_node |
| 313 | { |
| 314 | public: |
| 315 | enum_explain_aggr_node_type get_type() { return AGGR_OP_REMOVE_DUPLICATES; } |
| 316 | }; |
| 317 | |
| 318 | class Explain_aggr_window_funcs : public Explain_aggr_node |
| 319 | { |
| 320 | List<Explain_aggr_filesort> sorts; |
| 321 | public: |
| 322 | enum_explain_aggr_node_type get_type() { return AGGR_OP_WINDOW_FUNCS; } |
| 323 | |
| 324 | void print_json_members(Json_writer *writer, bool is_analyze); |
| 325 | friend class Window_funcs_computation; |
| 326 | }; |
| 327 | |
| 328 | ///////////////////////////////////////////////////////////////////////////// |
| 329 | |
| 330 | extern const char *unit_operation_text[4]; |
| 331 | |
| 332 | /* |
| 333 | Explain structure for a UNION. |
| 334 | |
| 335 | A UNION may or may not have "Using filesort". |
| 336 | */ |
| 337 | |
| 338 | class Explain_union : public Explain_node |
| 339 | { |
| 340 | public: |
| 341 | Explain_union(MEM_ROOT *root, bool is_analyze) : |
| 342 | Explain_node(root), |
| 343 | is_recursive_cte(false), |
| 344 | fake_select_lex_explain(root, is_analyze) |
| 345 | {} |
| 346 | |
| 347 | enum explain_node_type get_type() { return EXPLAIN_UNION; } |
| 348 | unit_common_op operation; |
| 349 | |
| 350 | int get_select_id() |
| 351 | { |
| 352 | DBUG_ASSERT(union_members.elements() > 0); |
| 353 | return union_members.at(0); |
| 354 | } |
| 355 | /* |
| 356 | Members of the UNION. Note: these are different from UNION's "children". |
| 357 | Example: |
| 358 | |
| 359 | (select * from t1) union |
| 360 | (select * from t2) order by (select col1 from t3 ...) |
| 361 | |
| 362 | here |
| 363 | - select-from-t1 and select-from-t2 are "union members", |
| 364 | - select-from-t3 is the only "child". |
| 365 | */ |
| 366 | Dynamic_array<int> union_members; |
| 367 | |
| 368 | void add_select(int select_no) |
| 369 | { |
| 370 | union_members.append(select_no); |
| 371 | } |
| 372 | int print_explain(Explain_query *query, select_result_sink *output, |
| 373 | uint8 explain_flags, bool is_analyze); |
| 374 | void print_explain_json(Explain_query *query, Json_writer *writer, |
| 375 | bool is_analyze); |
| 376 | |
| 377 | const char *fake_select_type; |
| 378 | bool using_filesort; |
| 379 | bool using_tmp; |
| 380 | bool is_recursive_cte; |
| 381 | |
| 382 | /* |
| 383 | Explain data structure for "fake_select_lex" (i.e. for the degenerate |
| 384 | SELECT that reads UNION result). |
| 385 | It doesn't have a query plan, but we still need execution tracker, etc. |
| 386 | */ |
| 387 | Explain_select fake_select_lex_explain; |
| 388 | |
| 389 | Table_access_tracker *get_fake_select_lex_tracker() |
| 390 | { |
| 391 | return &fake_select_lex_tracker; |
| 392 | } |
| 393 | Table_access_tracker *get_tmptable_read_tracker() |
| 394 | { |
| 395 | return &tmptable_read_tracker; |
| 396 | } |
| 397 | private: |
| 398 | uint make_union_table_name(char *buf); |
| 399 | |
| 400 | Table_access_tracker fake_select_lex_tracker; |
| 401 | /* This one is for reading after ORDER BY */ |
| 402 | Table_access_tracker tmptable_read_tracker; |
| 403 | }; |
| 404 | |
| 405 | |
| 406 | class Explain_update; |
| 407 | class Explain_delete; |
| 408 | class Explain_insert; |
| 409 | |
| 410 | |
| 411 | /* |
| 412 | Explain structure for a query (i.e. a statement). |
| 413 | |
| 414 | This should be able to survive when the query plan was deleted. Currently, |
| 415 | we do not intend for it survive until after query's MEM_ROOT is freed. It |
| 416 | does surivive freeing of query's items. |
| 417 | |
| 418 | For reference, the process of post-query cleanup is as follows: |
| 419 | |
| 420 | >dispatch_command |
| 421 | | >mysql_parse |
| 422 | | | ... |
| 423 | | | lex_end() |
| 424 | | | ... |
| 425 | | | >THD::cleanup_after_query |
| 426 | | | | ... |
| 427 | | | | free_items() |
| 428 | | | | ... |
| 429 | | | <THD::cleanup_after_query |
| 430 | | | |
| 431 | | <mysql_parse |
| 432 | | |
| 433 | | log_slow_statement() |
| 434 | | |
| 435 | | free_root() |
| 436 | | |
| 437 | >dispatch_command |
| 438 | |
| 439 | That is, the order of actions is: |
| 440 | - free query's Items |
| 441 | - write to slow query log |
| 442 | - free query's MEM_ROOT |
| 443 | |
| 444 | */ |
| 445 | |
| 446 | class Explain_query : public Sql_alloc |
| 447 | { |
| 448 | public: |
| 449 | Explain_query(THD *thd, MEM_ROOT *root); |
| 450 | ~Explain_query(); |
| 451 | |
| 452 | /* Add a new node */ |
| 453 | void add_node(Explain_node *node); |
| 454 | void add_insert_plan(Explain_insert *insert_plan_arg); |
| 455 | void add_upd_del_plan(Explain_update *upd_del_plan_arg); |
| 456 | |
| 457 | /* This will return a select, or a union */ |
| 458 | Explain_node *get_node(uint select_id); |
| 459 | |
| 460 | /* This will return a select (even if there is a union with this id) */ |
| 461 | Explain_select *get_select(uint select_id); |
| 462 | |
| 463 | Explain_union *get_union(uint select_id); |
| 464 | |
| 465 | /* Produce a tabular EXPLAIN output */ |
| 466 | int print_explain(select_result_sink *output, uint8 explain_flags, |
| 467 | bool is_analyze); |
| 468 | |
| 469 | /* Send tabular EXPLAIN to the client */ |
| 470 | int send_explain(THD *thd); |
| 471 | |
| 472 | /* Return tabular EXPLAIN output as a text string */ |
| 473 | bool print_explain_str(THD *thd, String *out_str, bool is_analyze); |
| 474 | |
| 475 | void print_explain_json(select_result_sink *output, bool is_analyze); |
| 476 | |
| 477 | /* If true, at least part of EXPLAIN can be printed */ |
| 478 | bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } |
| 479 | |
| 480 | void query_plan_ready(); |
| 481 | |
| 482 | MEM_ROOT *mem_root; |
| 483 | |
| 484 | Explain_update *get_upd_del_plan() { return upd_del_plan; } |
| 485 | private: |
| 486 | /* Explain_delete inherits from Explain_update */ |
| 487 | Explain_update *upd_del_plan; |
| 488 | |
| 489 | /* Query "plan" for INSERTs */ |
| 490 | Explain_insert *insert_plan; |
| 491 | |
| 492 | Dynamic_array<Explain_union*> unions; |
| 493 | Dynamic_array<Explain_select*> selects; |
| 494 | |
| 495 | THD *thd; // for APC start/stop |
| 496 | bool apc_enabled; |
| 497 | /* |
| 498 | Debugging aid: count how many times add_node() was called. Ideally, it |
| 499 | should be one, we currently allow O(1) query plan saves for each |
| 500 | select or union. The goal is not to have O(#rows_in_some_table), which |
| 501 | is unacceptable. |
| 502 | */ |
| 503 | longlong operations; |
| 504 | }; |
| 505 | |
| 506 | |
| 507 | /* |
| 508 | Some of the tags have matching text. See extra_tag_text for text names, and |
| 509 | Explain_table_access::append_tag_name() for code to convert from tag form to text |
| 510 | form. |
| 511 | */ |
| 512 | enum |
| 513 | { |
| 514 | ET_none= 0, /* not-a-tag */ |
| 515 | ET_USING_INDEX_CONDITION, |
| 516 | ET_USING_INDEX_CONDITION_BKA, |
| 517 | ET_USING, /* For quick selects of various kinds */ |
| 518 | ET_RANGE_CHECKED_FOR_EACH_RECORD, |
| 519 | ET_USING_WHERE_WITH_PUSHED_CONDITION, |
| 520 | ET_USING_WHERE, |
| 521 | ET_NOT_EXISTS, |
| 522 | |
| 523 | ET_USING_INDEX, |
| 524 | ET_FULL_SCAN_ON_NULL_KEY, |
| 525 | ET_SKIP_OPEN_TABLE, |
| 526 | ET_OPEN_FRM_ONLY, |
| 527 | ET_OPEN_FULL_TABLE, |
| 528 | |
| 529 | ET_SCANNED_0_DATABASES, |
| 530 | ET_SCANNED_1_DATABASE, |
| 531 | ET_SCANNED_ALL_DATABASES, |
| 532 | |
| 533 | ET_USING_INDEX_FOR_GROUP_BY, |
| 534 | |
| 535 | ET_USING_MRR, // does not print "Using mrr". |
| 536 | |
| 537 | ET_DISTINCT, |
| 538 | ET_LOOSESCAN, |
| 539 | ET_START_TEMPORARY, |
| 540 | ET_END_TEMPORARY, |
| 541 | ET_FIRST_MATCH, |
| 542 | |
| 543 | ET_USING_JOIN_BUFFER, |
| 544 | |
| 545 | ET_CONST_ROW_NOT_FOUND, |
| 546 | ET_UNIQUE_ROW_NOT_FOUND, |
| 547 | ET_IMPOSSIBLE_ON_CONDITION, |
| 548 | |
| 549 | ET_total |
| 550 | }; |
| 551 | |
| 552 | |
| 553 | /* |
| 554 | Explain data structure describing join buffering use. |
| 555 | */ |
| 556 | |
| 557 | class EXPLAIN_BKA_TYPE |
| 558 | { |
| 559 | public: |
| 560 | EXPLAIN_BKA_TYPE() : join_alg(NULL) {} |
| 561 | |
| 562 | size_t join_buffer_size; |
| 563 | |
| 564 | bool incremental; |
| 565 | |
| 566 | /* |
| 567 | NULL if no join buferring used. |
| 568 | Other values: BNL, BNLH, BKA, BKAH. |
| 569 | */ |
| 570 | const char *join_alg; |
| 571 | |
| 572 | /* Information about MRR usage. */ |
| 573 | StringBuffer<64> mrr_type; |
| 574 | |
| 575 | bool is_using_jbuf() { return (join_alg != NULL); } |
| 576 | }; |
| 577 | |
| 578 | |
| 579 | /* |
| 580 | Data about how an index is used by some access method |
| 581 | */ |
| 582 | class Explain_index_use : public Sql_alloc |
| 583 | { |
| 584 | char *key_name; |
| 585 | uint key_len; |
| 586 | public: |
| 587 | String_list key_parts_list; |
| 588 | |
| 589 | Explain_index_use() |
| 590 | { |
| 591 | clear(); |
| 592 | } |
| 593 | |
| 594 | void clear() |
| 595 | { |
| 596 | key_name= NULL; |
| 597 | key_len= (uint)-1; |
| 598 | } |
| 599 | bool set(MEM_ROOT *root, KEY *key_name, uint key_len_arg); |
| 600 | bool set_pseudo_key(MEM_ROOT *root, const char *key_name); |
| 601 | |
| 602 | inline const char *get_key_name() const { return key_name; } |
| 603 | inline uint get_key_len() const { return key_len; } |
| 604 | }; |
| 605 | |
| 606 | |
| 607 | /* |
| 608 | QPF for quick range selects, as well as index_merge select |
| 609 | */ |
| 610 | class Explain_quick_select : public Sql_alloc |
| 611 | { |
| 612 | public: |
| 613 | Explain_quick_select(int quick_type_arg) : quick_type(quick_type_arg) |
| 614 | {} |
| 615 | |
| 616 | const int quick_type; |
| 617 | |
| 618 | bool is_basic() |
| 619 | { |
| 620 | return (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE || |
| 621 | quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC || |
| 622 | quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX); |
| 623 | } |
| 624 | |
| 625 | /* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */ |
| 626 | Explain_index_use range; |
| 627 | |
| 628 | /* Used in all other cases */ |
| 629 | List<Explain_quick_select> children; |
| 630 | |
| 631 | void (String *str); |
| 632 | void print_key(String *str); |
| 633 | void print_key_len(String *str); |
| 634 | |
| 635 | void print_json(Json_writer *writer); |
| 636 | |
| 637 | void (String *str); |
| 638 | private: |
| 639 | const char *get_name_by_type(); |
| 640 | }; |
| 641 | |
| 642 | |
| 643 | /* |
| 644 | Data structure for "range checked for each record". |
| 645 | It's a set of keys, tabular explain prints hex bitmap, json prints key names. |
| 646 | */ |
| 647 | |
| 648 | typedef const char* NAME; |
| 649 | |
| 650 | class Explain_range_checked_fer : public Sql_alloc |
| 651 | { |
| 652 | public: |
| 653 | String_list key_set; |
| 654 | key_map keys_map; |
| 655 | private: |
| 656 | ha_rows full_scan, index_merge; |
| 657 | ha_rows *keys_stat; |
| 658 | NAME *keys_stat_names; |
| 659 | uint keys; |
| 660 | |
| 661 | public: |
| 662 | Explain_range_checked_fer() |
| 663 | :Sql_alloc(), full_scan(0), index_merge(0), |
| 664 | keys_stat(0), keys_stat_names(0), keys(0) |
| 665 | {} |
| 666 | |
| 667 | int append_possible_keys_stat(MEM_ROOT *alloc, |
| 668 | TABLE *table, key_map possible_keys); |
| 669 | void collect_data(QUICK_SELECT_I *quick); |
| 670 | void print_json(Json_writer *writer, bool is_analyze); |
| 671 | }; |
| 672 | |
| 673 | /* |
| 674 | EXPLAIN data structure for a single JOIN_TAB. |
| 675 | */ |
| 676 | |
| 677 | class Explain_table_access : public Sql_alloc |
| 678 | { |
| 679 | public: |
| 680 | Explain_table_access(MEM_ROOT *root) : |
| 681 | derived_select_number(0), |
| 682 | non_merged_sjm_number(0), |
| 683 | extra_tags(root), |
| 684 | range_checked_fer(NULL), |
| 685 | full_scan_on_null_key(false), |
| 686 | start_dups_weedout(false), |
| 687 | end_dups_weedout(false), |
| 688 | where_cond(NULL), |
| 689 | cache_cond(NULL), |
| 690 | pushed_index_cond(NULL), |
| 691 | sjm_nest(NULL), |
| 692 | pre_join_sort(NULL) |
| 693 | {} |
| 694 | ~Explain_table_access() { delete sjm_nest; } |
| 695 | |
| 696 | void (enum explain_extra_tag ); |
| 697 | |
| 698 | /* Internals */ |
| 699 | |
| 700 | /* id and 'select_type' are cared-of by the parent Explain_select */ |
| 701 | StringBuffer<32> table_name; |
| 702 | StringBuffer<32> used_partitions; |
| 703 | String_list used_partitions_list; |
| 704 | // valid with ET_USING_MRR |
| 705 | StringBuffer<32> mrr_type; |
| 706 | StringBuffer<32> firstmatch_table_name; |
| 707 | |
| 708 | /* |
| 709 | Non-zero number means this is a derived table. The number can be used to |
| 710 | find the query plan for the derived table |
| 711 | */ |
| 712 | int derived_select_number; |
| 713 | /* TODO: join with the previous member. */ |
| 714 | int non_merged_sjm_number; |
| 715 | |
| 716 | enum join_type type; |
| 717 | |
| 718 | bool used_partitions_set; |
| 719 | |
| 720 | /* Empty means "NULL" will be printed */ |
| 721 | String_list possible_keys; |
| 722 | |
| 723 | bool rows_set; /* not set means 'NULL' should be printed */ |
| 724 | bool filtered_set; /* not set means 'NULL' should be printed */ |
| 725 | // Valid if ET_USING_INDEX_FOR_GROUP_BY is present |
| 726 | bool loose_scan_is_scanning; |
| 727 | |
| 728 | /* |
| 729 | Index use: key name and length. |
| 730 | Note: that when one is accessing I_S tables, those may show use of |
| 731 | non-existant indexes. |
| 732 | |
| 733 | key.key_name == NULL means 'NULL' will be shown in tabular output. |
| 734 | key.key_len == (uint)-1 means 'NULL' will be shown in tabular output. |
| 735 | */ |
| 736 | Explain_index_use key; |
| 737 | |
| 738 | /* |
| 739 | when type==JT_HASH_NEXT, 'key' stores the hash join pseudo-key. |
| 740 | hash_next_key stores the table's key. |
| 741 | */ |
| 742 | Explain_index_use hash_next_key; |
| 743 | |
| 744 | String_list ref_list; |
| 745 | |
| 746 | ha_rows rows; |
| 747 | double filtered; |
| 748 | |
| 749 | /* |
| 750 | Contents of the 'Extra' column. Some are converted into strings, some have |
| 751 | parameters, values for which are stored below. |
| 752 | */ |
| 753 | Dynamic_array<enum explain_extra_tag> ; |
| 754 | |
| 755 | // Valid if ET_USING tag is present |
| 756 | Explain_quick_select *quick_info; |
| 757 | |
| 758 | /* Non-NULL value means this tab uses "range checked for each record" */ |
| 759 | Explain_range_checked_fer *range_checked_fer; |
| 760 | |
| 761 | bool full_scan_on_null_key; |
| 762 | |
| 763 | // valid with ET_USING_JOIN_BUFFER |
| 764 | EXPLAIN_BKA_TYPE bka_type; |
| 765 | |
| 766 | bool start_dups_weedout; |
| 767 | bool end_dups_weedout; |
| 768 | |
| 769 | /* |
| 770 | Note: lifespan of WHERE condition is less than lifespan of this object. |
| 771 | The below two are valid if tags include "ET_USING_WHERE". |
| 772 | (TODO: indexsubquery may put ET_USING_WHERE without setting where_cond?) |
| 773 | */ |
| 774 | Item *where_cond; |
| 775 | Item *cache_cond; |
| 776 | |
| 777 | /* |
| 778 | This is either pushed index condition, or BKA's index condition. |
| 779 | (the latter refers to columns of other tables and so can only be checked by |
| 780 | BKA code). Examine extra_tags to tell which one it is. |
| 781 | */ |
| 782 | Item *pushed_index_cond; |
| 783 | |
| 784 | Explain_basic_join *sjm_nest; |
| 785 | |
| 786 | /* |
| 787 | This describes a possible filesort() call that is done before doing the |
| 788 | join operation. |
| 789 | */ |
| 790 | Explain_aggr_filesort *pre_join_sort; |
| 791 | |
| 792 | /* ANALYZE members */ |
| 793 | |
| 794 | /* Tracker for reading the table */ |
| 795 | Table_access_tracker tracker; |
| 796 | Exec_time_tracker op_tracker; |
| 797 | Table_access_tracker jbuf_tracker; |
| 798 | |
| 799 | int print_explain(select_result_sink *output, uint8 explain_flags, |
| 800 | bool is_analyze, |
| 801 | uint select_id, const char *select_type, |
| 802 | bool using_temporary, bool using_filesort); |
| 803 | void print_explain_json(Explain_query *query, Json_writer *writer, |
| 804 | bool is_analyze); |
| 805 | |
| 806 | private: |
| 807 | void (String *str, enum explain_extra_tag tag); |
| 808 | void fill_key_str(String *key_str, bool is_json) const; |
| 809 | void fill_key_len_str(String *key_len_str) const; |
| 810 | double get_r_filtered(); |
| 811 | void (Json_writer *writer, enum explain_extra_tag tag); |
| 812 | }; |
| 813 | |
| 814 | |
| 815 | /* |
| 816 | EXPLAIN structure for single-table UPDATE. |
| 817 | |
| 818 | This is similar to Explain_table_access, except that it is more restrictive. |
| 819 | Also, it can have UPDATE operation options, but currently there aren't any. |
| 820 | |
| 821 | Explain_delete inherits from this. |
| 822 | */ |
| 823 | |
| 824 | class Explain_update : public Explain_node |
| 825 | { |
| 826 | public: |
| 827 | |
| 828 | Explain_update(MEM_ROOT *root, bool is_analyze) : |
| 829 | Explain_node(root), |
| 830 | filesort_tracker(NULL), |
| 831 | command_tracker(is_analyze) |
| 832 | {} |
| 833 | |
| 834 | virtual enum explain_node_type get_type() { return EXPLAIN_UPDATE; } |
| 835 | virtual int get_select_id() { return 1; /* always root */ } |
| 836 | |
| 837 | const char *select_type; |
| 838 | |
| 839 | StringBuffer<32> used_partitions; |
| 840 | String_list used_partitions_list; |
| 841 | bool used_partitions_set; |
| 842 | |
| 843 | bool impossible_where; |
| 844 | bool no_partitions; |
| 845 | StringBuffer<64> table_name; |
| 846 | |
| 847 | enum join_type jtype; |
| 848 | String_list possible_keys; |
| 849 | |
| 850 | /* Used key when doing a full index scan (possibly with limit) */ |
| 851 | Explain_index_use key; |
| 852 | |
| 853 | /* |
| 854 | MRR that's used with quick select. This should probably belong to the |
| 855 | quick select |
| 856 | */ |
| 857 | StringBuffer<64> mrr_type; |
| 858 | |
| 859 | Explain_quick_select *quick_info; |
| 860 | |
| 861 | bool using_where; |
| 862 | Item *where_cond; |
| 863 | |
| 864 | ha_rows rows; |
| 865 | |
| 866 | bool using_io_buffer; |
| 867 | |
| 868 | /* Tracker for doing reads when filling the buffer */ |
| 869 | Table_access_tracker buf_tracker; |
| 870 | |
| 871 | bool is_using_filesort() { return filesort_tracker? true: false; } |
| 872 | /* |
| 873 | Non-null value of filesort_tracker means "using filesort" |
| 874 | |
| 875 | if we are using filesort, then table_tracker is for the io done inside |
| 876 | filesort. |
| 877 | |
| 878 | 'tracker' is for tracking post-filesort reads. |
| 879 | */ |
| 880 | Filesort_tracker *filesort_tracker; |
| 881 | |
| 882 | /* ANALYZE members and methods */ |
| 883 | Table_access_tracker tracker; |
| 884 | |
| 885 | /* This tracks execution of the whole command */ |
| 886 | Time_and_counter_tracker command_tracker; |
| 887 | |
| 888 | /* TODO: This tracks time to read rows from the table */ |
| 889 | Exec_time_tracker table_tracker; |
| 890 | |
| 891 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
| 892 | uint8 explain_flags, bool is_analyze); |
| 893 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
| 894 | bool is_analyze); |
| 895 | }; |
| 896 | |
| 897 | |
| 898 | /* |
| 899 | EXPLAIN data structure for an INSERT. |
| 900 | |
| 901 | At the moment this doesn't do much as we don't really have any query plans |
| 902 | for INSERT statements. |
| 903 | */ |
| 904 | |
| 905 | class Explain_insert : public Explain_node |
| 906 | { |
| 907 | public: |
| 908 | Explain_insert(MEM_ROOT *root) : |
| 909 | Explain_node(root) |
| 910 | {} |
| 911 | |
| 912 | StringBuffer<64> table_name; |
| 913 | |
| 914 | enum explain_node_type get_type() { return EXPLAIN_INSERT; } |
| 915 | int get_select_id() { return 1; /* always root */ } |
| 916 | |
| 917 | int print_explain(Explain_query *query, select_result_sink *output, |
| 918 | uint8 explain_flags, bool is_analyze); |
| 919 | void print_explain_json(Explain_query *query, Json_writer *writer, |
| 920 | bool is_analyze); |
| 921 | }; |
| 922 | |
| 923 | |
| 924 | /* |
| 925 | EXPLAIN data of a single-table DELETE. |
| 926 | */ |
| 927 | |
| 928 | class Explain_delete: public Explain_update |
| 929 | { |
| 930 | public: |
| 931 | Explain_delete(MEM_ROOT *root, bool is_analyze) : |
| 932 | Explain_update(root, is_analyze) |
| 933 | {} |
| 934 | |
| 935 | /* |
| 936 | TRUE means we're going to call handler->delete_all_rows() and not read any |
| 937 | rows. |
| 938 | */ |
| 939 | bool deleting_all_rows; |
| 940 | |
| 941 | virtual enum explain_node_type get_type() { return EXPLAIN_DELETE; } |
| 942 | virtual int get_select_id() { return 1; /* always root */ } |
| 943 | |
| 944 | virtual int print_explain(Explain_query *query, select_result_sink *output, |
| 945 | uint8 explain_flags, bool is_analyze); |
| 946 | virtual void print_explain_json(Explain_query *query, Json_writer *writer, |
| 947 | bool is_analyze); |
| 948 | }; |
| 949 | |
| 950 | |
| 951 | #endif //SQL_EXPLAIN_INCLUDED |
| 952 | |