diff options
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r-- | sql/sql_select.cc | 723 |
1 files changed, 490 insertions, 233 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3bb62598027..9ec348f07c0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1002,20 +1002,33 @@ err: DBUG_RETURN(res); /* purecov: inspected */ } +void join_save_qpf(JOIN *join); int JOIN::optimize() { + bool was_optimized= optimized; int res= optimize_inner(); /* If we're inside a non-correlated subquery, this function may be called for the second time after the subquery has been executed and deleted. The second call will not produce a valid query plan, it will short-circuit because optimized==TRUE. + + "was_optimized != optimized" is here to handle this case: + - first optimization starts, gets an error (from a const. cheap + subquery), returns 1 + - another JOIN::optimize() call made, and now join->optimize() will + return 0, even though we never had a query plan. */ - if (!res && have_query_plan != QEP_DELETED) + if (was_optimized != optimized && !res && have_query_plan != QEP_DELETED) + { have_query_plan= QEP_AVAILABLE; + join_save_qpf(this); + } return res; } + + /** global select optimisation. @@ -1602,8 +1615,10 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S JOIN_TAB *tab= &join_tab[const_tables]; bool all_order_fields_used; if (order) + { skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1, &tab->table->keys_in_use_for_order_by); + } if ((group_list=create_distinct_group(thd, select_lex->ref_pointer_array, order, fields_list, all_fields, &all_order_fields_used))) @@ -2280,11 +2295,48 @@ JOIN::save_join_tab() } +void join_save_qpf(JOIN *join) +{ + THD *thd= join->thd; +//TODO: why not call st_select_lex::save_qpf here? + + if (join->select_lex->select_number != UINT_MAX && + join->select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && + join->have_query_plan != JOIN::QEP_NOT_PRESENT_YET && + join->have_query_plan != JOIN::QEP_DELETED && // this happens when there was no QEP ever, but then + //cleanup() is called multiple times + + thd->lex->query_plan_footprint && // for "SET" command in SPs. + !thd->lex->query_plan_footprint->get_select(join->select_lex->select_number)) + { + const char *message= NULL; + + if (!join->table_count || !join->tables_list || join->zero_result_cause) + { + /* It's a degenerate join */ + message= join->zero_result_cause ? join->zero_result_cause : "No tables used"; + } + + join->save_qpf(thd->lex->query_plan_footprint, + 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 + message); // message + } +} + + void JOIN::exec() { /* Enable SHOW EXPLAIN only if we're in the top-level query. */ + + /* + psergey: we can produce SHOW explain at this point. This means, we're ready + to save the query plan. + */ thd->apc_target.enable(); DBUG_EXECUTE_IF("show_explain_probe_join_exec_start", if (dbug_user_var_equals_int(thd, @@ -2295,13 +2347,42 @@ void JOIN::exec() exec_inner(); + if (!exec_qpf_saved) + { + if (select_lex->select_number != UINT_MAX && + select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && + have_query_plan != QEP_NOT_PRESENT_YET && + have_query_plan != QEP_DELETED && // this happens when there was no QEP ever, but then + //cleanup() is called multiple times + + thd->lex->query_plan_footprint //&& // for "SET" command in SPs. + /*!thd->lex->query_plan_footprint->get_select(select_lex->select_number)*/) + { + const char *message= NULL; + + if (!table_count || !tables_list || zero_result_cause) + { + /* It's a degenerate join */ + message= zero_result_cause ? zero_result_cause : "No tables used"; + } + + save_qpf(thd->lex->query_plan_footprint, + need_tmp, // need_tmp_table + // !skip_sort_order && !no_order && + // (order || group_list), // bool need_order + order != 0 && !skip_sort_order, + select_distinct, // bool distinct + message); // message + } + exec_qpf_saved= true; + } + DBUG_EXECUTE_IF("show_explain_probe_join_exec_end", if (dbug_user_var_equals_int(thd, "show_explain_probe_select_id", select_lex->select_number)) dbug_serve_apcs(thd, 1); ); - thd->apc_target.disable(); } @@ -3855,7 +3936,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (*s->on_expr_ref) { /* Generate empty row */ - s->info= "Impossible ON condition"; + s->info= ET_IMPOSSIBLE_ON_CONDITION; found_const_table_map|= s->table->map; s->type= JT_CONST; mark_as_null_row(s->table); // All fields are NULL @@ -11088,7 +11169,37 @@ void JOIN::cleanup(bool full) DBUG_ENTER("JOIN::cleanup"); DBUG_PRINT("enter", ("full %u", (uint) full)); - have_query_plan= QEP_DELETED; + if (full) + { + /* Save it again */ +#if 0 + if (select_lex->select_number != UINT_MAX && + select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ && + have_query_plan != QEP_NOT_PRESENT_YET && + have_query_plan != QEP_DELETED && // this happens when there was no QEP ever, but then + //cleanup() is called multiple times + + thd->lex->query_plan_footprint //&& // for "SET" command in SPs. + /*!thd->lex->query_plan_footprint->get_select(select_lex->select_number)*/) + { + const char *message= NULL; + + if (!table_count || !tables_list || zero_result_cause) + { + /* It's a degenerate join */ + message= zero_result_cause ? zero_result_cause : "No tables used"; + } + + save_qpf(thd->lex->query_plan_footprint, + need_tmp, // need_tmp_table + !skip_sort_order && !no_order && + (order || group_list), // bool need_order + select_distinct, // bool distinct + message); // message + } +#endif + have_query_plan= QEP_DELETED; //psergey: this is a problem! + } if (table) { @@ -17265,7 +17376,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) { if ((error=join_read_system(tab))) { // Info for DESCRIBE - tab->info="const row not found"; + tab->info= ET_CONST_ROW_NOT_FOUND; /* Mark for EXPLAIN that the row was not found */ pos->records_read=0.0; pos->ref_depend_map= 0; @@ -17291,7 +17402,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) table->disable_keyread(); if (error) { - tab->info="unique row not found"; + tab->info= ET_UNIQUE_ROW_NOT_FOUND; /* Mark for EXPLAIN that the row was not found */ pos->records_read=0.0; pos->ref_depend_map= 0; @@ -22150,23 +22261,20 @@ void JOIN::clear() /* Print an EXPLAIN line with all NULLs and given message in the 'Extra' column */ + int print_explain_message_line(select_result_sink *result, - SELECT_LEX *select_lex, - bool on_the_fly, uint8 options, + uint select_number, + const char *select_type, const char *message) { const CHARSET_INFO *cs= system_charset_info; Item *item_null= new Item_null(); List<Item> item_list; - if (on_the_fly) - select_lex->set_explain_type(on_the_fly); - - item_list.push_back(new Item_int((int32) - select_lex->select_number)); - item_list.push_back(new Item_string(select_lex->type, - strlen(select_lex->type), cs)); + item_list.push_back(new Item_int((int32) select_number)); + 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 (options & DESCRIBE_PARTITIONS) @@ -22182,6 +22290,104 @@ int print_explain_message_line(select_result_sink *result, } +/* + Make a comma-separated list of possible_keys names and add it into the string +*/ + +void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line) +{ + if (!possible_keys.is_clear_all()) + { + uint j; + for (j=0 ; j < table->s->keys ; j++) + { + if (possible_keys.is_set(j)) + { + if (line->length()) + line->append(','); + line->append(table->key_info[j].name, + strlen(table->key_info[j].name), + system_charset_info); + } + } + } +} + +/* + Print an EXPLAIN output row, based on information provided in the parameters + + @note + Parameters that may have NULL value in EXPLAIN output, should be passed + (char*)NULL. + + @return + 0 - OK + 1 - OOM Error +*/ + +int print_explain_row(select_result_sink *result, + uint8 options, + uint select_number, + const char *select_type, + const char *table_name, + //const char *partitions, (todo) + enum join_type jtype, + const char *possible_keys, + const char *index, + const char *key_len, + const char *ref, + ha_rows rows, + const char *extra) +{ + const CHARSET_INFO *cs= system_charset_info; + Item *item_null= new Item_null(); + List<Item> item_list; + Item *item; + + item_list.push_back(new Item_int((int32) select_number)); + item_list.push_back(new Item_string(select_type, + strlen(select_type), cs)); + item_list.push_back(new Item_string(table_name, + strlen(table_name), cs)); + if (options & DESCRIBE_PARTITIONS) + item_list.push_back(item_null); // psergey-todo: produce proper value + + const char *jtype_str= join_type_str[jtype]; + item_list.push_back(new Item_string(jtype_str, + strlen(jtype_str), cs)); + + item= possible_keys? new Item_string(possible_keys, strlen(possible_keys), + cs) : item_null; + item_list.push_back(item); + + /* 'index */ + item= index ? new Item_string(index, strlen(index), cs) : item_null; + item_list.push_back(item); + + /* 'key_len */ + item= key_len ? new Item_string(key_len, strlen(key_len), cs) : item_null; + item_list.push_back(item); + + /* 'ref' */ + item= ref ? new Item_string(ref, strlen(ref), cs) : item_null; + item_list.push_back(item); + + /* 'rows' */ + item_list.push_back(new Item_int(rows, + MY_INT64_NUM_DECIMAL_DIGITS)); + /* 'filtered' */ + if (options & DESCRIBE_EXTENDED) + item_list.push_back(item_null); + + /* 'Extra' */ + item_list.push_back(new Item_string(extra, strlen(extra), cs)); + + if (result->send_data(item_list)) + return 1; + return 0; +} + + int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, SELECT_LEX *select_lex, uint8 explain_flags) { @@ -22261,69 +22467,122 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, } -/** - EXPLAIN handling. +/* + Append MRR information from quick select to the given string +*/ + +void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res) +{ + char mrr_str_buf[128]; + mrr_str_buf[0]=0; + int len; + handler *h= quick->head->file; + len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf, + sizeof(mrr_str_buf)); + if (len > 0) + { + //res->append(STRING_WITH_LEN("; ")); + res->append(mrr_str_buf, len); + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +void QPF_table_access::push_extra(enum Extra_tag extra_tag) +{ + extra_tags.append(extra_tag); +} - Produce lines explaining execution of *this* select (not including children - selects) - @param on_the_fly TRUE <=> we're being executed on-the-fly, so don't make - modifications to any select's data structures +void append_possible_keys(String *str, TABLE *table, key_map possible_keys) +{ + uint j; + for (j=0 ; j < table->s->keys ; j++) + { + if (possible_keys.is_set(j)) + { + if (str->length()) + str->append(','); + str->append(table->key_info[j].name, + strlen(table->key_info[j].name), + system_charset_info); + } + } +} + +/* + Save Query Plan Footprint + + @note + Currently, this function may be called multiple times */ -int JOIN::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 JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, + bool distinct, const char *message) { - List<Item> field_list; - List<Item> item_list; + QPF_node *qp_node; + const bool on_the_fly= true; + JOIN *join= this; /* Legacy: this code used to be a non-member function */ THD *thd=join->thd; - Item *item_null= new Item_null(); - CHARSET_INFO *cs= system_charset_info; + const CHARSET_INFO *cs= system_charset_info; int quick_type; int error= 0; - DBUG_ENTER("JOIN::print_explain"); + DBUG_ENTER("JOIN::save_qpf"); DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s", (ulong)join->select_lex, join->select_lex->type, message ? message : "NULL")); - DBUG_ASSERT(on_the_fly? have_query_plan == QEP_AVAILABLE: TRUE); + DBUG_ASSERT(have_query_plan == QEP_AVAILABLE); /* Don't log this into the slow query log */ - if (!on_the_fly) - { - thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); - join->unit->offset_limit_cnt= 0; - } - - /* - NOTE: the number/types of items pushed into item_list must be in sync with - EXPLAIN column types as they're "defined" in THD::send_explain_fields() - */ if (message) { - if (print_explain_message_line(result, join->select_lex, on_the_fly, - explain_flags, message)) - error= 1; + QPF_select *qp_sel; + qp_node= qp_sel= new (output->mem_root) QPF_select; + join->select_lex->set_explain_type(on_the_fly); + qp_sel->select_id= join->select_lex->select_number; + qp_sel->select_type= join->select_lex->type; + qp_sel->message= message; + /* Setting qp_sel->message means that all other members are invalid */ + output->add_node(qp_sel); } else if (join->select_lex == join->unit->fake_select_lex) { - if (print_fake_select_lex_join(result, on_the_fly, - join->select_lex, - explain_flags)) - error= 1; +#if 0 + select_lex->set_explain_type(on_the_fly); + QPF_union *qp_union= new (output->mem_root) QPF_union; + qp_node= qp_union; + + SELECT_LEX *child; + for (child= select_lex->master_unit()->first_select(); child; + child=child->next_select()) + { + qp_union->add_select(child->select_number); + } + + qp_union->fake_select_type= select_lex->type; + qp_union->using_filesort= + test(select_lex->master_unit()->global_parameters->order_list.first); + + output->add_node(qp_union); +#endif } else if (!join->select_lex->master_unit()->derived || join->select_lex->master_unit()->derived->is_materialized_derived()) { + QPF_select *qp_sel; + qp_node= qp_sel= new (output->mem_root) QPF_select; table_map used_tables=0; - //if (!join->select_lex->type) + if (on_the_fly) join->select_lex->set_explain_type(on_the_fly); - bool printing_materialize_nest= FALSE; + //bool printing_materialize_nest= FALSE; uint select_id= join->select_lex->select_number; + + qp_sel->select_id= join->select_lex->select_number; + qp_sel->select_type= join->select_lex->type; + JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab; @@ -22333,18 +22592,15 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, { JOIN_TAB *first_sibling= tab->bush_root_tab->bush_children->start; select_id= first_sibling->emb_sj_nest->sj_subq_pred->get_identifier(); - printing_materialize_nest= TRUE; + //printing_materialize_nest= TRUE; } - + TABLE *table=tab->table; TABLE_LIST *table_list= tab->table->pos_in_table_list; - char buff[512]; - char buff1[512], buff2[512], buff3[512], buff4[512]; + char buff2[512], buff3[512], buff4[512]; char keylen_str_buf[64]; my_bool key_read; - String extra(buff, sizeof(buff),cs); char table_name_buffer[SAFE_NAME_LEN]; - String tmp1(buff1,sizeof(buff1),cs); String tmp2(buff2,sizeof(buff2),cs); String tmp3(buff3,sizeof(buff3),cs); String tmp4(buff4,sizeof(buff4),cs); @@ -22353,8 +22609,6 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, uint key_len= 0; bool is_hj= tab->type == JT_HASH || tab->type ==JT_HASH_NEXT; - extra.length(0); - tmp1.length(0); tmp2.length(0); tmp3.length(0); tmp4.length(0); @@ -22376,28 +22630,20 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, tab= pre_sort_join_tab; } - item_list.empty(); + QPF_table_access *qpt= new (output->mem_root) QPF_table_access; + qp_sel->add_table(qpt); + /* id */ - item_list.push_back(new Item_uint((uint32)select_id)); + if (tab->bush_root_tab) + qpt->sjm_nest_select_id= select_id; + else + qpt->sjm_nest_select_id= 0; + /* select_type */ - const char* stype= printing_materialize_nest? "MATERIALIZED" : - join->select_lex->type; - item_list.push_back(new Item_string(stype, strlen(stype), cs)); - - enum join_type tab_type= tab->type; - if ((tab->type == JT_ALL || tab->type == JT_HASH) && - tab->select && tab->select->quick && tab->use_quick != 2) - { - quick= tab->select->quick; - quick_type= tab->select->quick->get_type(); - if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) || - (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) - tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE; - else - tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE; - } + //const char* stype= printing_materialize_nest? "MATERIALIZED" : + // join->select_lex->type; + //item_list.push_back(new Item_string(stype, strlen(stype), cs)); + qp_sel->select_type= join->select_lex->type; /* table */ if (table->derived_select_number) @@ -22406,7 +22652,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1, "<derived%u>", table->derived_select_number); - item_list.push_back(new Item_string(table_name_buffer, len, cs)); + qpt->table_name.copy(table_name_buffer, len, cs); } else if (tab->bush_children) { @@ -22416,59 +22662,58 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, sizeof(table_name_buffer)-1, "<subquery%d>", ctab->emb_sj_nest->sj_subq_pred->get_identifier()); - item_list.push_back(new Item_string(table_name_buffer, len, cs)); + qpt->table_name.copy(table_name_buffer, len, cs); } else { TABLE_LIST *real_table= table->pos_in_table_list; - item_list.push_back(new Item_string(real_table->alias, - strlen(real_table->alias), cs)); + qpt->table_name.copy(real_table->alias, strlen(real_table->alias), cs); } + /* "partitions" column */ - if (explain_flags & DESCRIBE_PARTITIONS) { #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info; if (!table->derived_select_number && (part_info= table->part_info)) { - Item_string *item_str= new Item_string(cs); - make_used_partitions_str(part_info, &item_str->str_value); - item_list.push_back(item_str); + make_used_partitions_str(part_info, &qpt->used_partitions); + qpt->used_partitions_set= true; } else - item_list.push_back(item_null); + qpt->used_partitions_set= false; #else /* just produce empty column if partitioning is not compiled in */ - item_list.push_back(item_null); + qpt->used_partitions_set= false; #endif } + /* "type" column */ - item_list.push_back(new Item_string(join_type_str[tab_type], - strlen(join_type_str[tab_type]), - cs)); - /* Build "possible_keys" value and add it to item_list */ - if (!tab->keys.is_clear_all()) - { - uint j; - for (j=0 ; j < table->s->keys ; j++) - { - if (tab->keys.is_set(j)) - { - if (tmp1.length()) - tmp1.append(','); - tmp1.append(table->key_info[j].name, - strlen(table->key_info[j].name), - system_charset_info); - } - } + enum join_type tab_type= tab->type; + if ((tab->type == JT_ALL || tab->type == JT_HASH) && + tab->select && tab->select->quick && tab->use_quick != 2) + { + quick= tab->select->quick; + quick_type= tab->select->quick->get_type(); + if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) || + (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || + (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || + (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) + tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE; + else + tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE; } - if (tmp1.length()) - item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs)); - else - item_list.push_back(item_null); + qpt->type= tab_type; + + /* Build "possible_keys" value */ + qpt->possible_keys= tab->keys; + append_possible_keys(&qpt->possible_keys_str, table, tab->keys); - /* Build "key", "key_len", and "ref" values and add them to item_list */ + /* Build "key", "key_len", and "ref" */ + + // tmp2 holds key_name + // tmp3 holds key_length + // tmp4 holds ref? if (tab_type == JT_NEXT) { key_info= table->key_info+tab->index; @@ -22479,6 +22724,7 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, key_info= tab->get_keyinfo_by_key_no(tab->ref.key); key_len= tab->ref.key_length; } + if (key_info) { register uint length; @@ -22506,11 +22752,13 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, } } } + if (is_hj && tab_type != JT_HASH) { tmp2.append(':'); tmp3.append(':'); } + if (tab_type == JT_HASH_NEXT) { register uint length; @@ -22520,23 +22768,36 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, length= (longlong10_to_str(key_len, keylen_str_buf, 10) - keylen_str_buf); tmp3.append(keylen_str_buf, length, cs); - } + } + if (tab->type != JT_CONST && tab->select && quick) tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3); + if (key_info || (tab->select && quick)) { if (tmp2.length()) - item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs)); + { + qpt->key.copy(tmp2); + qpt->key_set= true; + } else - item_list.push_back(item_null); + qpt->key_set= false; + if (tmp3.length()) - item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs)); + { + qpt->key_len.copy(tmp3); + qpt->key_len_set= true; + } else - item_list.push_back(item_null); + qpt->key_len_set= false; + if (key_info && tab_type != JT_NEXT) - item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs)); + { + qpt->ref.copy(tmp4); + qpt->ref_set= true; + } else - item_list.push_back(item_null); + qpt->ref_set= false; } else { @@ -22561,51 +22822,52 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, tmp2.append(tmp_buff, strlen(tmp_buff), cs); } if (tmp2.length()) - item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs)); + { + qpt->key.copy(tmp2); + qpt->key_set= true; + } else - item_list.push_back(item_null); + qpt->key_set= false; } else - item_list.push_back(item_null); - item_list.push_back(item_null); - item_list.push_back(item_null); + qpt->key_set= false; + + qpt->key_len_set= false; + qpt->ref_set= false; } - /* Add "rows" field to item_list. */ + /* "rows" */ + if (table_list /* SJM bushes don't have table_list */ && table_list->schema_table) { - /* in_rows */ - if (explain_flags & DESCRIBE_EXTENDED) - item_list.push_back(item_null); - /* rows */ - item_list.push_back(item_null); + /* I_S tables have rows=extra=NULL */ + qpt->rows_set= false; + qpt->filtered_set= false; } else { ha_rows examined_rows= tab->get_examined_rows(); - item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, - MY_INT64_NUM_DECIMAL_DIGITS)); + qpt->rows_set= true; + qpt->rows= examined_rows; - /* Add "filtered" field to item_list. */ - if (explain_flags & DESCRIBE_EXTENDED) + /* "filtered" */ + float f= 0.0; + if (examined_rows) { - float f= 0.0; - if (examined_rows) - { - double pushdown_cond_selectivity= tab->cond_selectivity; - if (pushdown_cond_selectivity == 1.0) - f= (float) (100.0 * tab->records_read / examined_rows); - else - f= (float) (100.0 * pushdown_cond_selectivity); - } - set_if_smaller(f, 100.0); - item_list.push_back(new Item_float(f, 2)); + double pushdown_cond_selectivity= tab->cond_selectivity; + if (pushdown_cond_selectivity == 1.0) + f= (float) (100.0 * tab->records_read / examined_rows); + else + f= (float) (100.0 * pushdown_cond_selectivity); } + set_if_smaller(f, 100.0); + qpt->filtered_set= true; + qpt->filtered= f; } - /* Build "Extra" field and add it to item_list. */ + /* Build "Extra" field and save it */ key_read=table->key_read; if ((tab_type == JT_NEXT || tab_type == JT_CONST) && table->covering_keys.is_set(tab->index)) @@ -22615,24 +22877,17 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, key_read=1; if (tab->info) - item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs)); + { + qpt->push_extra(tab->info); + } else if (tab->packed_info & TAB_INFO_HAVE_VALUE) { if (tab->packed_info & TAB_INFO_USING_INDEX) - extra.append(STRING_WITH_LEN("; Using index")); + qpt->push_extra(ET_USING_INDEX); if (tab->packed_info & TAB_INFO_USING_WHERE) - extra.append(STRING_WITH_LEN("; Using where")); + qpt->push_extra(ET_USING_WHERE); if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL) - extra.append(STRING_WITH_LEN("; Full scan on NULL key")); - /* Skip initial "; "*/ - const char *str= extra.ptr(); - uint32 len= extra.length(); - if (len) - { - str += 2; - len -= 2; - } - item_list.push_back(new Item_string(str, len, cs)); + qpt->push_extra(ET_FULL_SCAN_ON_NULL_KEY); } else { @@ -22644,28 +22899,24 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno && table->file->pushed_idx_cond) - extra.append(STRING_WITH_LEN("; Using index condition")); + qpt->push_extra(ET_USING_INDEX_CONDITION); else if (tab->cache_idx_cond) - extra.append(STRING_WITH_LEN("; Using index condition(BKA)")); + qpt->push_extra(ET_USING_INDEX_CONDITION_BKA); if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) { - extra.append(STRING_WITH_LEN("; Using ")); - tab->select->quick->add_info_string(&extra); + qpt->push_extra(ET_USING); + tab->select->quick->add_info_string(&qpt->quick_info); } if (tab->select) { if (tab->use_quick == 2) { - /* 4 bits per 1 hex digit + terminating '\0' */ - char buf[MAX_KEY / 4 + 1]; - extra.append(STRING_WITH_LEN("; Range checked for each " - "record (index map: 0x")); - extra.append(tab->keys.print(buf)); - extra.append(')'); + qpt->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD); + qpt->range_checked_map= tab->keys; } else if (tab->select->cond) { @@ -22677,16 +22928,19 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) && pushed_cond) { - extra.append(STRING_WITH_LEN("; Using where with pushed " - "condition")); + qpt->push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION); + /* + psergey-todo: what to do? This was useful with NDB only. + if (explain_flags & DESCRIBE_EXTENDED) { extra.append(STRING_WITH_LEN(": ")); ((COND *)pushed_cond)->print(&extra, QT_ORDINARY); } + */ } else - extra.append(STRING_WITH_LEN("; Using where")); + qpt->push_extra(ET_USING_WHERE); } } if (table_list /* SJM bushes don't have table_list */ && @@ -22694,19 +22948,20 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) { if (!table_list->table_open_method) - extra.append(STRING_WITH_LEN("; Skip_open_table")); + qpt->push_extra(ET_SKIP_OPEN_TABLE); else if (table_list->table_open_method == OPEN_FRM_ONLY) - extra.append(STRING_WITH_LEN("; Open_frm_only")); + qpt->push_extra(ET_OPEN_FRM_ONLY); else - extra.append(STRING_WITH_LEN("; Open_full_table")); + qpt->push_extra(ET_OPEN_FULL_TABLE); + /* psergey-note: the following has a bug.*/ if (table_list->has_db_lookup_value && table_list->has_table_lookup_value) - extra.append(STRING_WITH_LEN("; Scanned 0 databases")); + qpt->push_extra(ET_SCANNED_0_DATABASES); else if (table_list->has_db_lookup_value || table_list->has_table_lookup_value) - extra.append(STRING_WITH_LEN("; Scanned 1 database")); + qpt->push_extra(ET_SCANNED_1_DATABASE); else - extra.append(STRING_WITH_LEN("; Scanned all databases")); + qpt->push_extra(ET_SCANNED_ALL_DATABASES); } if (key_read) { @@ -22714,69 +22969,52 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, { QUICK_GROUP_MIN_MAX_SELECT *qgs= (QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick; - extra.append(STRING_WITH_LEN("; Using index for group-by")); - qgs->append_loose_scan_type(&extra); + qpt->push_extra(ET_USING_INDEX_FOR_GROUP_BY); + qpt->loose_scan_is_scanning= qgs->loose_scan_is_scanning(); } else - extra.append(STRING_WITH_LEN("; Using index")); + qpt->push_extra(ET_USING_INDEX); } if (table->reginfo.not_exists_optimize) - extra.append(STRING_WITH_LEN("; Not exists")); + qpt->push_extra(ET_NOT_EXISTS); - /* - if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE && - !(((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags & - HA_MRR_USE_DEFAULT_IMPL)) - { - extra.append(STRING_WITH_LEN("; Using MRR")); - } - */ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE) { - char mrr_str_buf[128]; - mrr_str_buf[0]=0; - int len; - uint mrr_flags= - ((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags; - len= table->file->multi_range_read_explain_info(mrr_flags, - mrr_str_buf, - sizeof(mrr_str_buf)); - if (len > 0) - { - extra.append(STRING_WITH_LEN("; ")); - extra.append(mrr_str_buf, len); - } + explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick), + &qpt->mrr_type); + if (qpt->mrr_type.length() > 0) + qpt->push_extra(ET_USING_MRR); } if (need_tmp_table) { need_tmp_table=0; - extra.append(STRING_WITH_LEN("; Using temporary")); + qp_sel->using_temporary= true; } if (need_order) { need_order=0; - extra.append(STRING_WITH_LEN("; Using filesort")); + qp_sel->using_filesort= true; } if (distinct & test_all_bits(used_tables, join->select_list_used_tables)) - extra.append(STRING_WITH_LEN("; Distinct")); + qpt->push_extra(ET_DISTINCT); if (tab->loosescan_match_tab) { - extra.append(STRING_WITH_LEN("; LooseScan")); + qpt->push_extra(ET_LOOSESCAN); } if (tab->first_weedout_table) - extra.append(STRING_WITH_LEN("; Start temporary")); + qpt->push_extra(ET_START_TEMPORARY); if (tab->check_weed_out_table) - extra.append(STRING_WITH_LEN("; End temporary")); + qpt->push_extra(ET_END_TEMPORARY); else if (tab->do_firstmatch) { if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1) - extra.append(STRING_WITH_LEN("; FirstMatch")); + qpt->push_extra(ET_FIRST_MATCH); else { - extra.append(STRING_WITH_LEN("; FirstMatch(")); + qpt->push_extra(ET_FIRST_MATCH); TABLE *prev_table=tab->do_firstmatch->table; if (prev_table->derived_select_number) { @@ -22785,11 +23023,10 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, int len= my_snprintf(namebuf, sizeof(namebuf)-1, "<derived%u>", prev_table->derived_select_number); - extra.append(namebuf, len); + qpt->firstmatch_table_name.append(namebuf, len); } else - extra.append(prev_table->pos_in_table_list->alias); - extra.append(STRING_WITH_LEN(")")); + qpt->firstmatch_table_name.append(prev_table->pos_in_table_list->alias); } } @@ -22797,26 +23034,16 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, { if (tab->ref.cond_guards[part]) { - extra.append(STRING_WITH_LEN("; Full scan on NULL key")); + qpt->push_extra(ET_FULL_SCAN_ON_NULL_KEY); break; } } if (tab->cache) { - extra.append(STRING_WITH_LEN("; Using join buffer")); - tab->cache->print_explain_comment(&extra); + qpt->push_extra(ET_USING_JOIN_BUFFER); + tab->cache->save_qpf(&qpt->bka_type); } - - /* Skip initial "; "*/ - const char *str= extra.ptr(); - uint32 len= extra.length(); - if (len) - { - str += 2; - len -= 2; - } - item_list.push_back(new Item_string(str, len, cs)); } if (saved_join_tab) @@ -22824,16 +23051,42 @@ int JOIN::print_explain(select_result_sink *result, uint8 explain_flags, // For next iteration used_tables|=table->map; - if (result->send_data(item_list)) - error= 1; } + output->add_node(qp_sel); } + + 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)) + { + qp_node->add_child(unit->first_select()->select_number); + } + } + DBUG_RETURN(error); } /* - See st_select_lex::print_explain() for the SHOW EXPLAIN counterpart + This function servers as "shortcut point" for EXPLAIN queries. + + For UNIONs and JOINs, EXPLAIN statement executes just like its SELECT + statement would execute, except that JOIN::exec() will call select_describe() + instead of actually executing the query. + + The purpose of select_describe() is: + - update the query plan with info about last-minute choices made at the start + of JOIN::exec + - Invoke "pseudo-execution" for the children subqueries. + + Overall, select_describe() is a legacy of old EXPLAIN implementation and + should be removed. */ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, @@ -22842,10 +23095,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, THD *thd=join->thd; select_result *result=join->result; DBUG_ENTER("select_describe"); - join->error= join->print_explain(result, thd->lex->describe, - FALSE, /* Not on-the-fly */ - need_tmp_table, need_order, distinct, - message); + + // Update the QPF: + QPF_select *qp; + if ((qp= thd->lex->query_plan_footprint->get_select(join->select_lex->select_number))) + { + qp->using_temporary= need_tmp_table; + qp->using_filesort= need_order; + } for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit(); unit; |