summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <timour@askmonty.org>2012-06-14 23:55:22 +0300
committerunknown <timour@askmonty.org>2012-06-14 23:55:22 +0300
commitcf3a499f541e66e1b8aa96fe16e36a48ed1c0a0e (patch)
treef355d8c09324971c8c04a36d0b3953b6fd2f384a /sql
parent28b4aba40a65006af33cc8e1464ab28643442309 (diff)
parentc2677de7aca09a0ba4b680b5227bda3865ab9290 (diff)
downloadmariadb-git-cf3a499f541e66e1b8aa96fe16e36a48ed1c0a0e.tar.gz
Merge the fix for lp:944706, mdev-193
Diffstat (limited to 'sql')
-rw-r--r--sql/item.h12
-rw-r--r--sql/item_cmpfunc.cc10
-rw-r--r--sql/item_cmpfunc.h4
-rw-r--r--sql/item_strfunc.h2
-rw-r--r--sql/item_subselect.cc42
-rw-r--r--sql/item_subselect.h8
-rw-r--r--sql/opt_subselect.cc47
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_delete.cc2
-rw-r--r--sql/sql_lex.cc87
-rw-r--r--sql/sql_lex.h8
-rw-r--r--sql/sql_select.cc186
-rw-r--r--sql/sql_select.h3
-rw-r--r--sql/sql_update.cc2
-rw-r--r--sql/sys_vars.cc7
-rw-r--r--sql/table.cc8
16 files changed, 343 insertions, 86 deletions
diff --git a/sql/item.h b/sql/item.h
index a9c1153d236..f7f3edda384 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 5f1a863d8fd..ec22fbebd5b 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -5540,7 +5540,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 de62bc49930..c10ce8525d4 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -371,9 +371,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 5458a2fb968..bbd3bf35a2d 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -523,6 +523,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 0e0f61aedd9..415bb059198 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 a5a68d0d306..8d1cbeba5f4 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -4894,7 +4894,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;
}
@@ -5295,7 +5331,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 c88d8211986..f9fb23153b3 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 ba189d89ccb..73047663981 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -3406,7 +3406,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())
{
@@ -3416,12 +3432,19 @@ 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;
+ bool is_correlated_unit= false;
/*
If the subquery is a UNION, optimize all the subqueries in the UNION. If
there is no UNION, then the loop will execute once for the subquery.
@@ -3446,6 +3469,8 @@ bool st_select_lex::optimize_unflattened_subqueries()
inner_join->select_options|= SELECT_DESCRIBE;
}
res= inner_join->optimize();
+ sl->update_correlated_cache();
+ is_correlated_unit|= sl->is_correlated;
inner_join->select_options= save_options;
un->thd->lex->current_select= save_select;
if (empty_union_result)
@@ -3461,6 +3486,9 @@ bool st_select_lex::optimize_unflattened_subqueries()
}
if (empty_union_result)
subquery_predicate->no_rows_in_result();
+ if (!is_correlated_unit)
+ un->uncacheable&= ~UNCACHEABLE_DEPENDENT;
+ subquery_predicate->is_correlated= is_correlated_unit;
}
}
return FALSE;
@@ -3830,6 +3858,61 @@ void SELECT_LEX::update_used_tables()
/**
+ @brief
+ Update is_correlated cache for this select
+
+ @details
+*/
+
+void st_select_lex::update_correlated_cache()
+{
+ TABLE_LIST *tl;
+ List_iterator<TABLE_LIST> ti(leaf_tables);
+
+ is_correlated= false;
+
+ while ((tl= ti++))
+ {
+ if (tl->on_expr)
+ is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
+ for (TABLE_LIST *embedding= tl->embedding ; embedding ;
+ embedding= embedding->embedding)
+ {
+ if (embedding->on_expr)
+ is_correlated|= test(embedding->on_expr->used_tables() &
+ OUTER_REF_TABLE_BIT);
+ }
+ }
+
+ if (join->conds)
+ is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (join->having)
+ is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (join->tmp_having)
+ is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT);
+
+ Item *item;
+ List_iterator_fast<Item> it(join->fields_list);
+ while ((item= it++))
+ is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT);
+
+ for (ORDER *order= group_list.first; order; order= order->next)
+ is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (!master_unit()->is_union())
+ {
+ for (ORDER *order= order_list.first; order; order= order->next)
+ is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+ }
+
+ if (!is_correlated)
+ uncacheable&= ~UNCACHEABLE_DEPENDENT;
+}
+
+
+/**
Set the EXPLAIN type for this subquery.
*/
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7da0cc48298..10be195feba 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);
@@ -1023,6 +1018,7 @@ public:
void mark_as_belong_to_derived(TABLE_LIST *derived);
void increase_derived_records(ha_rows records);
void update_used_tables();
+ void update_correlated_cache();
void mark_const_derived(bool empty);
bool save_leaf_tables(THD *thd);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 223e5c44fcf..6d6acb4eb04 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -271,6 +271,8 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
bool *inherited_fl);
JOIN_TAB *first_depth_first_tab(JOIN* join);
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab);
+JOIN_TAB *first_breadth_first_tab(JOIN *join);
+JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab);
/**
This handles SELECT with and without UNION.
@@ -987,7 +989,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 */
@@ -1274,6 +1279,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,
@@ -1294,6 +1305,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();
}
}
@@ -6615,6 +6632,32 @@ 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()
+{
+ ha_rows examined_rows;
+ double prev_fanout= 1;
+ JOIN_TAB *tab= first_breadth_first_tab(this);
+ JOIN_TAB *prev_tab= tab;
+
+ examined_rows= tab->get_examined_rows();
+
+ while ((tab= next_breadth_first_tab(this, tab)))
+ {
+ prev_fanout *= prev_tab->records_read;
+ examined_rows+= tab->get_examined_rows() * prev_fanout;
+ prev_tab= tab;
+ }
+ return examined_rows;
+}
+
+
+/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -8061,36 +8104,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;
@@ -10288,6 +10310,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.
@@ -10591,6 +10658,22 @@ void JOIN::cleanup(bool full)
tmp_join->tmp_table_param.save_copy_field= 0;
}
tmp_table_param.cleanup();
+
+ if (!join_tab)
+ {
+ List_iterator<TABLE_LIST> li(*join_list);
+ TABLE_LIST *table_ref;
+ while ((table_ref= li++))
+ {
+ if (table_ref->table &&
+ table_ref->jtbm_subselect &&
+ table_ref->jtbm_subselect->is_jtbm_const_tab)
+ {
+ free_tmp_table(thd, table_ref->table);
+ table_ref->table= NULL;
+ }
+ }
+ }
}
DBUG_VOID_RETURN;
}
@@ -11267,9 +11350,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;
}
@@ -11362,9 +11445,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);
}
@@ -12050,9 +12133,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;
@@ -12139,7 +12221,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,
@@ -18613,6 +18695,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)
@@ -21354,10 +21437,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)
@@ -21515,32 +21605,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 c4553148cc6..0ed976ac36a 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 c8af1422388..b244f75a157 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -3768,4 +3768,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());
}