diff options
author | unknown <timour@askmonty.org> | 2012-05-17 13:46:05 +0300 |
---|---|---|
committer | unknown <timour@askmonty.org> | 2012-05-17 13:46:05 +0300 |
commit | da5214831d3d0f0880b4068a06ce062ca703293f (patch) | |
tree | 68eb72f263dbf5719ba926505b3cfbbd8afd52c9 /sql | |
parent | ddd3e261b253856720bd9dc2343a655ecc297e81 (diff) | |
download | mariadb-git-da5214831d3d0f0880b4068a06ce062ca703293f.tar.gz |
Fix for bug lp:944706, task MDEV-193
The patch enables back constant subquery execution during
query optimization after it was disabled during the development
of MWL#89 (cost-based choice of IN-TO-EXISTS vs MATERIALIZATION).
The main idea is that constant subqueries are allowed to be executed
during optimization if their execution is not expensive.
The approach is as follows:
- Constant subqueries are recursively optimized in the beginning of
JOIN::optimize of the outer query. This is done by the new method
JOIN::optimize_constant_subqueries(). This is done so that the cost
of executing these queries can be estimated.
- Optimization of the outer query proceeds normally. During this phase
the optimizer may request execution of non-expensive constant subqueries.
Each place where the optimizer may potentially execute an expensive
expression is guarded with the predicate Item::is_expensive().
- The implementation of Item_subselect::is_expensive has been extended
to use the number of examined rows (estimated by the optimizer) as a
way to determine whether the subquery is expensive or not.
- The new system variable "expensive_subquery_limit" controls how many
examined rows are considered to be not expensive. The default is 100.
In addition, multiple changes were needed to make this solution work
in the light of the changes made by MWL#89. These changes were needed
to fix various crashes and wrong results, and legacy bugs discovered
during development.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.h | 12 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 10 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 4 | ||||
-rw-r--r-- | sql/item_strfunc.h | 2 | ||||
-rw-r--r-- | sql/item_subselect.cc | 42 | ||||
-rw-r--r-- | sql/item_subselect.h | 8 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 47 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_delete.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.cc | 26 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 | ||||
-rw-r--r-- | sql/sql_select.cc | 169 | ||||
-rw-r--r-- | sql/sql_select.h | 3 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/sys_vars.cc | 7 | ||||
-rw-r--r-- | sql/table.cc | 8 |
16 files changed, 264 insertions, 86 deletions
diff --git a/sql/item.h b/sql/item.h index b3801e6c488..df220118745 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1395,21 +1395,21 @@ public: { return cmp_context == IMPOSSIBLE_RESULT || item->cmp_context == cmp_context; } - /* + /** Test whether an expression is expensive to compute. Used during optimization to avoid computing expensive expressions during this phase. Also used to force temp tables when sorting on expensive functions. - TODO: + @todo Normally we should have a method: cost Item::execution_cost(), where 'cost' is either 'double' or some structure of various cost parameters. - NOTE - This function is now used to prevent evaluation of materialized IN - subquery predicates before it is allowed. grep for - DontEvaluateMaterializedSubqueryTooEarly to see the uses. + @note + This function is now used to prevent evaluation of expensive subquery + predicates during the optimization phase. It also prevents evaluation + of predicates that are not computable at this moment. */ virtual bool is_expensive() { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d3251bc1fb3..b45b138ced2 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5539,7 +5539,15 @@ void Item_equal::add_const(Item *c, Item *f) else { Item_func_eq *func= new Item_func_eq(c, const_item); - func->set_cmp_func(); + if (func->set_cmp_func()) + { + /* + Setting a comparison function fails when trying to compare + incompatible charsets. Charset compatibility is checked earlier, + except for constant subqueries where we may do it here. + */ + return; + } func->quick_fix_field(); cond_false= !func->val_int(); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index b9740c7af03..875fdc3b34f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -370,9 +370,9 @@ public: Item_bool_func2(Item *a,Item *b) :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), abort_on_null(FALSE) {} void fix_length_and_dec(); - void set_cmp_func() + int set_cmp_func() { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); + return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); } optimize_type select_optimize() const { return OPTIMIZE_OP; } virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index ad854b02765..b71408bd5f5 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -829,7 +829,7 @@ public: { DBUG_ASSERT(args[0]->fixed); conv_charset= cs; - if (cache_if_const && args[0]->const_item() && !args[0]->with_subselect) + if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive()) { uint errors= 0; String tmp, *str= args[0]->val_str(&tmp); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 276f35fe301..1367037d17e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -522,6 +522,48 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, */ } + +/** + Determine if a subquery is expensive to execute during query optimization. + + @details The cost of execution of a subquery is estimated based on an + estimate of the number of rows the subquery will access during execution. + This measure is used instead of JOIN::read_time, because it is considered + to be much more reliable than the cost estimate. + + @return true if the subquery is expensive + @return false otherwise +*/ +bool Item_subselect::is_expensive() +{ + double examined_rows= 0; + + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + JOIN *cur_join= sl->join; + if (!cur_join) + continue; + + /* If a subquery is not optimized we cannot estimate its cost. */ + if (!cur_join->join_tab) + return true; + + if (sl->first_inner_unit()) + { + /* + Subqueries that contain subqueries are considered expensive. + @todo: accumulate the cost of subqueries. + */ + return true; + } + + examined_rows+= cur_join->get_examined_rows(); + } + + return (examined_rows > thd->variables.expensive_subquery_limit); +} + + bool Item_subselect::walk(Item_processor processor, bool walk_subquery, uchar *argument) { diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 1a4c8a25a01..746813407c2 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -209,7 +209,7 @@ public: */ bool is_evaluated() const; bool is_uncacheable() const; - bool is_expensive() { return TRUE; } + bool is_expensive(); /* Used by max/min subquery to initialize value presence registration @@ -235,7 +235,7 @@ public: @retval TRUE if the predicate is expensive @retval FALSE otherwise */ - bool is_expensive_processor(uchar *arg) { return TRUE; } + bool is_expensive_processor(uchar *arg) { return is_expensive(); } /** Get the SELECT_LEX structure associated with this Item. @@ -581,6 +581,10 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); void fix_after_pullout(st_select_lex *new_parent, Item **ref); + bool const_item() const + { + return Item_subselect::const_item() && left_expr->const_item(); + } void update_used_tables(); bool setup_mat_engine(); bool init_left_expr_cache(); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 9bf1aaf4039..3e21187e6fa 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4872,7 +4872,43 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) bool JOIN::optimize_unflattened_subqueries() { - return select_lex->optimize_unflattened_subqueries(); + return select_lex->optimize_unflattened_subqueries(false); +} + +/** + Optimize all constant subqueries of a query that were not flattened into + a semijoin. + + @details + Similar to other constant conditions, constant subqueries can be used in + various constant optimizations. Having optimized constant subqueries before + these constant optimizations, makes it possible to estimate if a subquery + is "cheap" enough to be executed during the optimization phase. + + Constant subqueries can be optimized and evaluated independent of the outer + query, therefore if const_only = true, this method can be called early in + the optimization phase of the outer query. + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool JOIN::optimize_constant_subqueries() +{ + ulonglong save_options= select_lex->options; + bool res; + /* + Constant subqueries may be executed during the optimization phase. + In EXPLAIN mode the optimizer doesn't initialize many of the data structures + needed for execution. In order to make it possible to execute subqueries + during optimization, constant subqueries must be optimized for execution, + not for EXPLAIN. + */ + select_lex->options&= ~SELECT_DESCRIBE; + res= select_lex->optimize_unflattened_subqueries(true); + select_lex->options= save_options; + return res; } @@ -5273,7 +5309,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables) by the IN predicate. */ outer_join= unit->outer_select() ? unit->outer_select()->join : NULL; - if (outer_join && outer_join->table_count > 0) + /* + Get the cost of the outer join if: + (1) It has at least one table, and + (2) It has been already optimized (if there is no join_tab, then the + outer join has not been optimized yet). + */ + if (outer_join && outer_join->table_count > 0 && // (1) + outer_join->join_tab) // (2) { /* TODO: diff --git a/sql/sql_class.h b/sql/sql_class.h index c6c46975076..ee472e2e7e6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -482,6 +482,7 @@ typedef struct system_variables ulonglong group_concat_max_len; ha_rows select_limit; ha_rows max_join_size; + ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; ulong lock_wait_timeout; ulong join_cache_level; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index cc358eca440..5128b1284dd 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -120,7 +120,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); const_cond= (!conds || conds->const_item()); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 6fd6e53424f..fb5307f9e3a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3405,7 +3405,23 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) } -bool st_select_lex::optimize_unflattened_subqueries() +/** + Optimize all subqueries that have not been flattened into semi-joins. + + @details + This functionality is a method of SELECT_LEX instead of JOIN because + SQL statements as DELETE/UPDATE do not have a corresponding JOIN object. + + @see JOIN::optimize_unflattened_subqueries + + @param const_only Restrict subquery optimization to constant subqueries + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool st_select_lex::optimize_unflattened_subqueries(bool const_only) { for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) { @@ -3415,11 +3431,17 @@ bool st_select_lex::optimize_unflattened_subqueries() { if (subquery_predicate->substype() == Item_subselect::IN_SUBS) { - Item_in_subselect *in_subs=(Item_in_subselect*)subquery_predicate; + Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate; if (in_subs->is_jtbm_merged) continue; } + if (const_only && !subquery_predicate->const_item()) + { + /* Skip non-constant subqueries if the caller asked so. */ + continue; + } + bool empty_union_result= true; /* If the subquery is a UNION, optimize all the subqueries in the UNION. If diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f1ee6cf22ec..922386151f8 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -997,12 +997,7 @@ public: void clear_index_hints(void) { index_hints= NULL; } bool is_part_of_union() { return master_unit()->is_union(); } - /* - Optimize all subqueries that have not been flattened into semi-joins. - This functionality is a method of SELECT_LEX instead of JOIN because - some SQL statements as DELETE do not have a corresponding JOIN object. - */ - bool optimize_unflattened_subqueries(); + bool optimize_unflattened_subqueries(bool const_only); /* Set the EXPLAIN type for this subquery. */ void set_explain_type(); bool handle_derived(LEX *lex, uint phases); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 916e521d3b9..933679a0d9c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -986,7 +986,10 @@ JOIN::optimize() } eval_select_list_used_tables(); - + + if (optimize_constant_subqueries()) + DBUG_RETURN(1); + table_count= select_lex->leaf_tables.elements; if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ @@ -1273,6 +1276,12 @@ JOIN::optimize() { conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds, cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } conds->update_used_tables(); DBUG_EXECUTE("where", print_where(conds, @@ -1293,6 +1302,12 @@ JOIN::optimize() *tab->on_expr_ref, tab->cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } (*tab->on_expr_ref)->update_used_tables(); } } @@ -6592,6 +6607,33 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables, /** + Estimate the number of rows that query execution will read. + + @todo This is a very pessimistic upper bound. Use join selectivity + when available to produce a more realistic number. +*/ + +double JOIN::get_examined_rows() +{ + /* Each constant table examines one row, and the result is at most one row. */ + ha_rows examined_rows= const_tables; + uint i= const_tables; + double prev_fanout= 1; + + if (table_count == const_tables) + return examined_rows; + + examined_rows+= join_tab[i++].get_examined_rows(); + for (; i < table_count ; i++) + { + prev_fanout *= best_positions[i-1].records_read; + examined_rows+= join_tab[i].get_examined_rows() * prev_fanout; + } + return examined_rows; +} + + +/** Find a good, possibly optimal, query execution plan (QEP) by a possibly exhaustive search. @@ -8011,36 +8053,15 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) row_limit= unit->select_limit_cnt; do_send_rows= row_limit ? 1 : 0; - join_tab->use_join_cache= FALSE; - join_tab->cache=0; /* No caching */ + bzero(join_tab, sizeof(JOIN_TAB)); join_tab->table=temp_table; - join_tab->cache_select= 0; - join_tab->select=0; - join_tab->select_cond= 0; // Avoid valgrind warning join_tab->set_select_cond(NULL, __LINE__); - join_tab->quick=0; join_tab->type= JT_ALL; /* Map through all records */ join_tab->keys.init(); join_tab->keys.set_all(); /* test everything in quick */ - join_tab->info=0; - join_tab->on_expr_ref=0; - join_tab->last_inner= 0; - join_tab->first_unmatched= 0; join_tab->ref.key = -1; - join_tab->not_used_in_distinct=0; join_tab->read_first_record= join_init_read_record; - join_tab->preread_init_done= FALSE; join_tab->join= this; - join_tab->ref.key_parts= 0; - join_tab->keep_current_rowid= FALSE; - join_tab->flush_weedout_table= join_tab->check_weed_out_table= NULL; - join_tab->do_firstmatch= NULL; - join_tab->loosescan_match_tab= NULL; - join_tab->emb_sj_nest= NULL; - join_tab->pre_idx_push_select_cond= NULL; - join_tab->bush_root_tab= NULL; - join_tab->bush_children= NULL; - join_tab->last_leaf_in_bush= FALSE; bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); temp_table->status=0; temp_table->null_row=0; @@ -10225,6 +10246,51 @@ double JOIN_TAB::scan_time() return res; } + +/** + Estimate the number of rows that a an access method will read from a table. + + @todo: why not use JOIN_TAB::found_records +*/ + +ha_rows JOIN_TAB::get_examined_rows() +{ + ha_rows examined_rows; + + if (select && select->quick) + examined_rows= select->quick->records; + else if (type == JT_NEXT || type == JT_ALL || + type == JT_HASH || type ==JT_HASH_NEXT) + { + if (limit) + { + /* + @todo This estimate is wrong, a LIMIT query may examine much more rows + than the LIMIT itself. + */ + examined_rows= limit; + } + else + { + if (table->is_filled_at_execution()) + examined_rows= records; + else + { + /* + handler->info(HA_STATUS_VARIABLE) has been called in + make_join_statistics() + */ + examined_rows= table->file->stats.records; + } + } + } + else + examined_rows= (ha_rows) records_read; + + return examined_rows; +} + + /** Initialize the join_tab before reading. Currently only derived table/view materialization is done here. @@ -11204,9 +11270,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, if (!item) { Item_func_eq *eq_item; - if ((eq_item= new Item_func_eq(orig_left_item, orig_right_item))) + if (!(eq_item= new Item_func_eq(orig_left_item, orig_right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); item= eq_item; } @@ -11299,9 +11365,9 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row, if (!is_converted) { Item_func_eq *eq_item; - if (!(eq_item= new Item_func_eq(left_item, right_item))) + if (!(eq_item= new Item_func_eq(left_item, right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); eq_list->push_back(eq_item); } @@ -11987,9 +12053,8 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, eq_item= new Item_func_eq(field_item->real_item(), head_item); - if (!eq_item) + if (!eq_item || eq_item->set_cmp_func()) return 0; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); } current_sjm= field_sjm; @@ -12076,7 +12141,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, Item_equal::get_first() for details. @return - The transformed condition + The transformed condition, or NULL in case of error */ static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab, @@ -18544,6 +18609,7 @@ check_reverse_order: tab->ref.key_parts= 0; if (select_limit < table->file->stats.records) tab->limit= select_limit; + table->disable_keyread(); } } else if (tab->type != JT_ALL) @@ -21269,10 +21335,17 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } 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)); + TABLE_LIST *real_table= table->pos_in_table_list; + /* + Internal temporary tables have no corresponding table reference + object. Such a table may appear in EXPLAIN when a subquery that needs + a temporary table has been executed, and JOIN::exec replaced the + original JOIN with a plan to access the data in the temp table + (made by JOIN::make_simple_join). + */ + const char *tab_name= real_table ? real_table->alias : + "internal_tmp_table"; + item_list.push_back(new Item_string(tab_name, strlen(tab_name), cs)); } /* "partitions" column */ if (join->thd->lex->describe & DESCRIBE_PARTITIONS) @@ -21430,32 +21503,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - ha_rows examined_rows; - if (tab->select && tab->select->quick) - examined_rows= tab->select->quick->records; - else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj) - { - if (tab->limit) - examined_rows= tab->limit; - else - { - if (tab->table->is_filled_at_execution()) - { - examined_rows= tab->records; - } - else - { - /* - handler->info(HA_STATUS_VARIABLE) has been called in - make_join_statistics() - */ - examined_rows= tab->table->file->stats.records; - } - } - } - else - examined_rows=(ha_rows)tab->records_read; - + ha_rows examined_rows= tab->get_examined_rows(); + item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, MY_INT64_NUM_DECIMAL_DIGITS)); diff --git a/sql/sql_select.h b/sql/sql_select.h index 08ddc27e204..ee91928fa15 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -512,6 +512,7 @@ typedef struct st_join_table { return (is_hash_join_key_no(key) ? hj_key : table->key_info+key); } double scan_time(); + ha_rows get_examined_rows(); bool preread_init(); bool is_sjm_nest() { return test(bush_children); } @@ -1281,6 +1282,7 @@ public: bool alloc_func_list(); bool flatten_subqueries(); bool optimize_unflattened_subqueries(); + bool optimize_constant_subqueries(); bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields, bool before_group_by, bool recompute= FALSE); @@ -1380,6 +1382,7 @@ public: void get_prefix_cost_and_fanout(uint n_tables, double *read_time_arg, double *record_count_arg); + double get_examined_rows(); /* defined in opt_subselect.cc */ bool transform_max_min_subquery(); /* True if this JOIN is a subquery under an IN predicate. */ diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7d5fe875d64..f2b6c5c9f92 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -368,7 +368,7 @@ int mysql_update(THD *thd, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); if (select_lex->inner_refs_list.elements && diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ca434821800..65b21273348 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3780,4 +3780,9 @@ static Sys_var_ulong Sys_debug_binlog_fsync_sleep( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1)); #endif - +static Sys_var_harows Sys_expensive_subquery_limit( + "expensive_subquery_limit", + "The maximum number of rows a subquery may examine in order to be " + "executed during optimization and used for constant optimization", + SESSION_VAR(expensive_subquery_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, HA_POS_ERROR), DEFAULT(100), BLOCK_SIZE(1)); diff --git a/sql/table.cc b/sql/table.cc index 40304dc6fdc..bc7ad0e5831 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5985,7 +5985,13 @@ void TABLE::use_index(int key_to_save) bool TABLE::is_filled_at_execution() { - return test(pos_in_table_list->jtbm_subselect || + /* + pos_in_table_list == NULL for internal temporary tables because they + do not have a corresponding table reference. Such tables are filled + during execution. + */ + return test(!pos_in_table_list || + pos_in_table_list->jtbm_subselect || pos_in_table_list->is_active_sjm()); } |