diff options
-rw-r--r-- | sql/opt_qpf.cc | 201 | ||||
-rw-r--r-- | sql/opt_qpf.h | 95 | ||||
-rw-r--r-- | sql/opt_range.cc | 100 | ||||
-rw-r--r-- | sql/opt_range.h | 22 | ||||
-rw-r--r-- | sql/sql_select.cc | 108 |
5 files changed, 379 insertions, 147 deletions
diff --git a/sql/opt_qpf.cc b/sql/opt_qpf.cc index 8155f28acb7..d3fd88ce9bd 100644 --- a/sql/opt_qpf.cc +++ b/sql/opt_qpf.cc @@ -1,6 +1,18 @@ /* - TODO MP AB copyright -*/ + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation @@ -309,6 +321,11 @@ int QPF_table_access::print_explain(select_result_sink *output, uint8 explain_fl uint select_id, const char *select_type, bool using_temporary, bool using_filesort) { + const CHARSET_INFO *cs= system_charset_info; + const char *hash_key_prefix= "#hash#"; + bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT || + type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE); + List<Item> item_list; Item *item_null= new Item_null(); //const CHARSET_INFO *cs= system_charset_info; @@ -349,14 +366,62 @@ int QPF_table_access::print_explain(select_result_sink *output, uint8 explain_fl item_list.push_back(item_null); /* `key` */ - if (key_set) - push_string(&item_list, &key); + StringBuffer<64> key_str; + if (key.key_name) + { + if (is_hj) + key_str.append(hash_key_prefix, strlen(hash_key_prefix), cs); + + key_str.append(key.key_name); + + if (is_hj && type != JT_HASH) + key_str.append(':'); + } + + if (quick_info) + { + StringBuffer<64> buf2; + quick_info->print_key(&buf2); + key_str.append(buf2); + } + if (type == JT_HASH_NEXT) + key_str.append(hash_next_key.key_name); + + if (key_str.length() > 0) + push_string(&item_list, &key_str); else item_list.push_back(item_null); /* `key_len` */ - if (key_len_set) - push_string(&item_list, &key_len); + StringBuffer<64> key_len_str; + + if (key.key_len != (uint)-1) + { + char buf[64]; + size_t length; + length= longlong10_to_str(key.key_len, buf, 10) - buf; + key_len_str.append(buf, length); + if (is_hj && type != JT_HASH) + key_len_str.append(':'); + } + + if (quick_info) + { + StringBuffer<64> buf2; + quick_info->print_key_len(&buf2); + key_len_str.append(buf2); + } + + if (type == JT_HASH_NEXT) + { + char buf[64]; + size_t length; + length= longlong10_to_str(hash_next_key.key_len, buf, 10) - buf; + key_len_str.append(buf, length); + } + + if (key_len_str.length() > 0) + push_string(&item_list, &key_len_str); else item_list.push_back(item_null); @@ -416,7 +481,6 @@ int QPF_table_access::print_explain(select_result_sink *output, uint8 explain_fl extra_buf.append(STRING_WITH_LEN("Using filesort")); } - const CHARSET_INFO *cs= system_charset_info; item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs)); if (output->send_data(item_list)) @@ -476,7 +540,7 @@ void QPF_table_access::append_tag_name(String *str, enum Extra_tag tag) { // quick select str->append(STRING_WITH_LEN("Using ")); - str->append(quick_info); + quick_info->print_extra(str); break; } case ET_RANGE_CHECKED_FOR_EACH_RECORD: @@ -535,6 +599,127 @@ void QPF_table_access::append_tag_name(String *str, enum Extra_tag tag) } +/* + This is called for top-level QPF_quick_select only. The point of this + function is: + - index_merge should print $index_merge_type (child, ...) + - 'range' should not print anything. +*/ + +void QPF_quick_select::print_extra(String *str) +{ + if (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) + { + /* print nothing */ + } + else + print_extra_recursive(str); +} + + +void QPF_quick_select::print_extra_recursive(String *str) +{ + if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE || + quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC) + { + str->append(range.key_name); + } + else + { + str->append(get_name_by_type()); + str->append('('); + List_iterator_fast<QPF_quick_select> it (children); + QPF_quick_select* child; + bool first= true; + while ((child = it++)) + { + if (first) + first= false; + else + str->append(','); + + child->print_extra_recursive(str); + } + str->append(')'); + } +} + + +const char * QPF_quick_select::get_name_by_type() +{ + switch (quick_type) { + case QUICK_SELECT_I::QS_TYPE_INDEX_MERGE: + return "sort_union"; + case QUICK_SELECT_I::QS_TYPE_ROR_UNION: + return "union"; + case QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT: + return "intersect"; + case QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT: + return "sort_intersect"; + default: + DBUG_ASSERT(0); + return "Oops"; + } +} + + +/* + This prints a comma-separated list of used indexes, ignoring nesting +*/ + +void QPF_quick_select::print_key(String *str) +{ + if (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) + { + if (str->length() > 0) + str->append(','); + str->append(range.key_name); + } + else + { + List_iterator_fast<QPF_quick_select> it (children); + QPF_quick_select* child; + while ((child = it++)) + { + child->print_key(str); + } + } +} + + +/* + This prints a comma-separated list of used key_lengths, ignoring nesting +*/ + +void QPF_quick_select::print_key_len(String *str) +{ + if (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) + { + char buf[64]; + size_t length; + length= longlong10_to_str(range.key_len, buf, 10) - buf; + if (str->length() > 0) + str->append(','); + str->append(buf, length); + } + else + { + List_iterator_fast<QPF_quick_select> it (children); + QPF_quick_select* child; + while ((child = it++)) + { + child->print_key_len(str); + } + } +} + + int QPF_delete::print_explain(QPF_query *query, select_result_sink *output, uint8 explain_flags) { diff --git a/sql/opt_qpf.h b/sql/opt_qpf.h index f8f0004c669..2ab32967c8b 100644 --- a/sql/opt_qpf.h +++ b/sql/opt_qpf.h @@ -1,3 +1,20 @@ +/* + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + /************************************************************************************** Query Plan Footprint (QPF) structures @@ -286,6 +303,41 @@ typedef struct st_qpf_bka_type /* + Data about how an index is used by some access method +*/ +class QPF_index_use : public Sql_alloc +{ +public: + const char *key_name; + uint key_len; + /* will add #keyparts here if we implement EXPLAIN FORMAT=JSON */ +}; + + +/* + QPF for quick range selects, as well as index_merge select +*/ +class QPF_quick_select : public Sql_alloc +{ +public: + int quick_type; + + /* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */ + QPF_index_use range; + + /* Used in all other cases */ + List<QPF_quick_select> children; + + void print_extra(String *str); + void print_key(String *str); + void print_key_len(String *str); +private: + void print_extra_recursive(String *str); + const char *get_name_by_type(); +}; + + +/* Query Plan Footprint for a JOIN_TAB. */ class QPF_table_access : public Sql_alloc @@ -302,29 +354,33 @@ public: int sjm_nest_select_id; /* id and 'select_type' are cared-of by the parent QPF_select */ - TABLE *table; - StringBuffer<64> table_name; + StringBuffer<32> table_name; enum join_type type; - StringBuffer<64> used_partitions; + StringBuffer<32> used_partitions; bool used_partitions_set; - - key_map possible_keys; - StringBuffer<64> possible_keys_str; - /* Not used? */ - uint key_no; - uint key_length; - - bool key_set; /* not set means 'NULL' should be printed */ - StringBuffer<64> key; - - bool key_len_set; /* not set means 'NULL' should be printed */ - StringBuffer<64> key_len; + /* Empty strings means "NULL" will be printed */ + StringBuffer<32> possible_keys_str; + + /* + 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. + */ + QPF_index_use key; + + /* + when type==JT_HASH_NEXT, this stores the real index. + */ + QPF_index_use hash_next_key; + bool ref_set; /* not set means 'NULL' should be printed */ - StringBuffer<64> ref; + StringBuffer<32> ref; bool rows_set; /* not set means 'NULL' should be printed */ ha_rows rows; @@ -339,7 +395,7 @@ public: Dynamic_array<enum Extra_tag> extra_tags; // Valid if ET_USING tag is present - StringBuffer<64> quick_info; + QPF_quick_select *quick_info; // Valid if ET_USING_INDEX_FOR_GROUP_BY is present bool loose_scan_is_scanning; @@ -348,13 +404,12 @@ public: key_map range_checked_map; // valid with ET_USING_MRR - StringBuffer<64> mrr_type; + StringBuffer<32> mrr_type; // valid with ET_USING_JOIN_BUFFER QPF_BKA_TYPE bka_type; - //TABLE *firstmatch_table; - StringBuffer<64> firstmatch_table_name; + StringBuffer<32> firstmatch_table_name; int print_explain(select_result_sink *output, uint8 explain_flags, uint select_id, const char *select_type, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 27913f0aa8e..c216a035794 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -11940,78 +11940,106 @@ void QUICK_SELECT_I::add_key_name(String *str, bool *first) } -void QUICK_RANGE_SELECT::add_info_string(String *str) +void QUICK_RANGE_SELECT::save_info(QPF_quick_select *qpf) { - bool first= TRUE; - - add_key_name(str, &first); + qpf->quick_type= QS_TYPE_RANGE; + qpf->range.key_name= head->key_info[index].name; + qpf->range.key_len= max_used_key_length; +} + + +void QUICK_GROUP_MIN_MAX_SELECT::save_info(QPF_quick_select *qpf) +{ + qpf->quick_type= QS_TYPE_GROUP_MIN_MAX; + qpf->range.key_name= head->key_info[index].name; + qpf->range.key_len= max_used_key_length; } -void QUICK_INDEX_MERGE_SELECT::add_info_string(String *str) + +void QUICK_INDEX_SORT_SELECT::save_info(QPF_quick_select *qpf) { + qpf->quick_type= get_type(); + QUICK_RANGE_SELECT *quick; - bool first= TRUE; + QPF_quick_select *child_qpf; List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); - - str->append(STRING_WITH_LEN("sort_union(")); while ((quick= it++)) { - quick->add_key_name(str, &first); + child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + quick->save_info(child_qpf); } + if (pk_quick_select) - pk_quick_select->add_key_name(str, &first); - str->append(')'); + { + child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + pk_quick_select->save_info(child_qpf); + } } -void QUICK_INDEX_INTERSECT_SELECT::add_info_string(String *str) +/* + Same as QUICK_INDEX_SORT_SELECT::save_info(), but primary key is printed + first +*/ +void QUICK_INDEX_INTERSECT_SELECT::save_info(QPF_quick_select *qpf) { - QUICK_RANGE_SELECT *quick; - bool first= TRUE; - List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); + qpf->quick_type= get_type(); + QPF_quick_select *child_qpf; - str->append(STRING_WITH_LEN("sort_intersect(")); if (pk_quick_select) - pk_quick_select->add_key_name(str, &first); + { + child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + pk_quick_select->save_info(child_qpf); + } + + QUICK_RANGE_SELECT *quick; + List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); while ((quick= it++)) { - quick->add_key_name(str, &first); + child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + quick->save_info(child_qpf); } - str->append(')'); + } -void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str) + +void QUICK_ROR_INTERSECT_SELECT::save_info(QPF_quick_select *qpf) { - bool first= TRUE; + qpf->quick_type= get_type(); + QUICK_SELECT_WITH_RECORD *qr; List_iterator_fast<QUICK_SELECT_WITH_RECORD> it(quick_selects); - - str->append(STRING_WITH_LEN("intersect(")); while ((qr= it++)) { - qr->quick->add_key_name(str, &first); + QPF_quick_select *child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + qr->quick->save_info(child_qpf); } + if (cpk_quick) - cpk_quick->add_key_name(str, &first); - str->append(')'); + { + QPF_quick_select *child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + cpk_quick->save_info(child_qpf); + } } -void QUICK_ROR_UNION_SELECT::add_info_string(String *str) +void QUICK_ROR_UNION_SELECT::save_info(QPF_quick_select *qpf) { + qpf->quick_type= get_type(); + QUICK_SELECT_I *quick; - bool first= TRUE; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); - - str->append(STRING_WITH_LEN("union(")); while ((quick= it++)) { - if (first) - first= FALSE; - else - str->append(','); - quick->add_info_string(str); + QPF_quick_select *child_qpf= new QPF_quick_select; + qpf->children.push_back(child_qpf); + quick->save_info(child_qpf); } - str->append(')'); } diff --git a/sql/opt_range.h b/sql/opt_range.h index d701c7f9201..eeb4e4b77ad 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -52,7 +52,7 @@ typedef struct st_key_part { Field::imagetype image_type; } KEY_PART; - +class QPF_quick_select; /* A "MIN_TUPLE < tbl.key_tuple < MAX_TUPLE" interval. @@ -345,13 +345,8 @@ public: void add_key_name(String *str, bool *first); - /* - Append text representation of quick select structure (what and how is - merged) to str. The result is added to "Extra" field in EXPLAIN output. - This function is implemented only by quick selects that merge other quick - selects output and/or can produce output suitable for merging. - */ - virtual void add_info_string(String *str) {} + /* Save information about quick select's query plan */ + virtual void save_info(QPF_quick_select *qpf)= 0; /* Return 1 if any index used by this quick select @@ -478,7 +473,7 @@ public: { file->position(record); } int get_type() { return QS_TYPE_RANGE; } void add_keys_and_lengths(String *key_names, String *used_lengths); - void add_info_string(String *str); + void save_info(QPF_quick_select *qpf); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif @@ -615,6 +610,7 @@ public: #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif + void save_info(QPF_quick_select *qpf); bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range); @@ -663,7 +659,6 @@ public: int get_next(); int get_type() { return QS_TYPE_INDEX_MERGE; } void add_keys_and_lengths(String *key_names, String *used_lengths); - void add_info_string(String *str); }; class QUICK_INDEX_INTERSECT_SELECT : public QUICK_INDEX_SORT_SELECT @@ -679,7 +674,7 @@ public: int get_next(); int get_type() { return QS_TYPE_INDEX_INTERSECT; } void add_keys_and_lengths(String *key_names, String *used_lengths); - void add_info_string(String *str); + void save_info(QPF_quick_select *qpf); }; @@ -717,7 +712,7 @@ public: bool unique_key_range() { return false; } int get_type() { return QS_TYPE_ROR_INTERSECT; } void add_keys_and_lengths(String *key_names, String *used_lengths); - void add_info_string(String *str); + void save_info(QPF_quick_select *qpf); bool is_keys_used(const MY_BITMAP *fields); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); @@ -796,7 +791,7 @@ public: bool unique_key_range() { return false; } int get_type() { return QS_TYPE_ROR_UNION; } void add_keys_and_lengths(String *key_names, String *used_lengths); - void add_info_string(String *str); + void save_info(QPF_quick_select *qpf); bool is_keys_used(const MY_BITMAP *fields); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); @@ -945,6 +940,7 @@ public: #endif bool is_agg_distinct() { return have_agg_distinct; } bool loose_scan_is_scanning() { return is_index_scan; } + void save_info(QPF_quick_select *qpf); }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 97f59d80ae2..236ddbe8ed3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11172,7 +11172,8 @@ void JOIN::cleanup(bool full) if (full) { /* Save it again */ -#if 0 psergey-todo: remove? +#if 0 + psergey-todo: remove? 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 && @@ -22576,20 +22577,12 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, TABLE *table=tab->table; TABLE_LIST *table_list= tab->table->pos_in_table_list; - char buff2[512], buff3[512], buff4[512]; - char keylen_str_buf[64]; + char buff4[512]; my_bool key_read; char table_name_buffer[SAFE_NAME_LEN]; - String tmp2(buff2,sizeof(buff2),cs); - String tmp3(buff3,sizeof(buff3),cs); String tmp4(buff4,sizeof(buff4),cs); - char hash_key_prefix[]= "#hash#"; KEY *key_info= 0; uint key_len= 0; - bool is_hj= tab->type == JT_HASH || tab->type ==JT_HASH_NEXT; - - tmp2.length(0); - tmp3.length(0); tmp4.length(0); quick_type= -1; QUICK_SELECT_I *quick= NULL; @@ -22611,6 +22604,9 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, QPF_table_access *qpt= new (output->mem_root) QPF_table_access; qp_sel->add_table(qpt); + qpt->key.key_name= NULL; + qpt->key.key_len= (uint)-1; + qpt->quick_info= NULL; /* id */ if (tab->bush_root_tab) @@ -22685,13 +22681,10 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, 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" */ - // tmp2 holds key_name - // tmp3 holds key_length // tmp4 holds ref if (tab_type == JT_NEXT) { @@ -22703,16 +22696,22 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, key_info= tab->get_keyinfo_by_key_no(tab->ref.key); key_len= tab->ref.key_length; } + + /* + In STRAIGHT_JOIN queries, there can be join tabs with JT_CONST type + that still have quick selects. + */ + if (tab->select && tab->select->quick && tab_type != JT_CONST) + { + qpt->quick_info= new QPF_quick_select; + tab->select->quick->save_info(qpt->quick_info); + } - if (key_info) + if (key_info) /* 'index' or 'ref' access */ { - register uint length; - if (is_hj) - tmp2.append(hash_key_prefix, strlen(hash_key_prefix), cs); - tmp2.append(key_info->name, strlen(key_info->name), cs); - length= (longlong10_to_str(key_len, keylen_str_buf, 10) - - keylen_str_buf); - tmp3.append(keylen_str_buf, length, cs); + qpt->key.key_name= key_info->name; + qpt->key.key_len= key_len; + if (tab->ref.key_parts && tab_type != JT_FT) { store_key **ref=tab->ref.key_copy; @@ -22731,45 +22730,15 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, } } } - - if (is_hj && tab_type != JT_HASH) - { - tmp2.append(':'); - tmp3.append(':'); - } - - if (tab_type == JT_HASH_NEXT) + + if (tab_type == JT_HASH_NEXT) /* full index scan + hash join */ { - register uint length; - key_info= table->key_info+tab->index; - key_len= key_info->key_length; - tmp2.append(key_info->name, strlen(key_info->name), cs); - length= (longlong10_to_str(key_len, keylen_str_buf, 10) - - keylen_str_buf); - tmp3.append(keylen_str_buf, length, cs); + qpt->hash_next_key.key_name= table->key_info[tab->index].name; + qpt->hash_next_key.key_len= table->key_info[tab->index].key_length; } - if (tab->type != JT_CONST && tab->select && quick) - tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3); - - if (key_info || (tab->select && quick)) + if (key_info) { - if (tmp2.length()) - { - qpt->key.copy(tmp2); - qpt->key_set= true; - } - else - qpt->key_set= false; - - if (tmp3.length()) - { - qpt->key_len.copy(tmp3); - qpt->key_len_set= true; - } - else - qpt->key_len_set= false; - if (key_info && tab_type != JT_NEXT) { qpt->ref.copy(tmp4); @@ -22786,37 +22755,37 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, { const char *tmp_buff; int f_idx; + StringBuffer<64> key_name_buf; if (table_list->has_db_lookup_value) { + /* The "key" has the name of the column referring to the database */ f_idx= table_list->schema_table->idx_field1; tmp_buff= table_list->schema_table->fields_info[f_idx].field_name; - tmp2.append(tmp_buff, strlen(tmp_buff), cs); + key_name_buf.append(tmp_buff, strlen(tmp_buff), cs); } if (table_list->has_table_lookup_value) { if (table_list->has_db_lookup_value) - tmp2.append(','); + key_name_buf.append(','); + f_idx= table_list->schema_table->idx_field2; tmp_buff= table_list->schema_table->fields_info[f_idx].field_name; - tmp2.append(tmp_buff, strlen(tmp_buff), cs); + key_name_buf.append(tmp_buff, strlen(tmp_buff), cs); } - if (tmp2.length()) + + size_t len; + if ((len= key_name_buf.length())) { - qpt->key.copy(tmp2); - qpt->key_set= true; + char *ptr= (char*)thd->alloc(len+1); + memcpy(ptr, key_name_buf.c_ptr_safe(), len+1); + qpt->key.key_name= ptr; + qpt->key.key_len= -1; } - else - qpt->key_set= false; } - else - qpt->key_set= false; - - qpt->key_len_set= false; qpt->ref_set= false; } /* "rows" */ - if (table_list /* SJM bushes don't have table_list */ && table_list->schema_table) { @@ -22888,7 +22857,6 @@ int JOIN::save_qpf(QPF_query *output, bool need_tmp_table, bool need_order, quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) { qpt->push_extra(ET_USING); - tab->select->quick->add_info_string(&qpt->quick_info); } if (tab->select) { |