summaryrefslogtreecommitdiff
path: root/sql/sql_select.cc
diff options
context:
space:
mode:
authorunknown <timour@askmonty.org>2011-04-01 15:42:59 +0300
committerunknown <timour@askmonty.org>2011-04-01 15:42:59 +0300
commit619a16bffccd190a43c9b3c2d136f30215d29bdf (patch)
treec65993c2f00d2f39abbc3f24f39c0fe860062fca /sql/sql_select.cc
parentd5adc29d1c39027c827074f936d3f28e71f87800 (diff)
parent3d8aa98c7136f7fa19f42c4f75ece49fb5c9aefb (diff)
downloadmariadb-git-619a16bffccd190a43c9b3c2d136f30215d29bdf.tar.gz
MWL#89
- Auto-merge with 5.3 main. - Changed the test for LP BUG#719198 so that an two more queries were added, and removed a query that produces a wrong result due to an unrelated problem. The wrong result is submitted as a separate bug.
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r--sql/sql_select.cc746
1 files changed, 468 insertions, 278 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 012ddea421a..f80cc4935bc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -55,21 +55,19 @@ static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, COND *conds,
static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
JOIN_TAB *join_tab,
uint tables, COND *conds,
- COND_EQUAL *cond_equal,
table_map table_map, SELECT_LEX *select_lex,
st_sargable_param **sargables);
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse);
static int sort_keyuse(KEYUSE *a,KEYUSE *b);
static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
table_map used_tables);
-bool choose_plan(JOIN *join,table_map join_tables);
-
void best_access_path(JOIN *join, JOIN_TAB *s,
table_map remaining_tables, uint idx,
bool disable_jbuf, double record_count,
POSITION *pos, POSITION *loose_scan_pos);
static void optimize_straight_join(JOIN *join, table_map join_tables);
static bool greedy_search(JOIN *join, table_map remaining_tables,
- uint depth, uint prune_level);
+ uint depth, uint prune_level);
static bool best_extension_by_limited_search(JOIN *join,
table_map remaining_tables,
uint idx, double record_count,
@@ -86,7 +84,6 @@ static int join_tab_cmp_embedded_first(const void *emb, const void* ptr1, const
static bool find_best(JOIN *join,table_map rest_tables,uint index,
double record_count,double read_time);
static uint cache_record_length(JOIN *join,uint index);
-static double prev_record_reads(JOIN *join, uint idx, table_map found_ref);
static bool get_best_combination(JOIN *join);
static store_key *get_store_key(THD *thd,
KEYUSE *keyuse, table_map used_tables,
@@ -174,13 +171,16 @@ static int join_ft_read_first(JOIN_TAB *tab);
static int join_ft_read_next(READ_RECORD *info);
int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info);
-static COND *make_cond_for_table(Item *cond,table_map table,
+static COND *make_cond_for_table(THD *thd, Item *cond,table_map table,
table_map used_table,
+ uint join_tab_idx_arg,
bool exclude_expensive_cond,
bool retain_ref_cond);
-static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond,
+static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond,
+ Item *cond,
table_map tables,
table_map used_table,
+ uint join_tab_idx_arg,
bool exclude_expensive_cond,
bool retain_ref_cond);
@@ -235,8 +235,6 @@ static bool update_sum_func(Item_sum **func);
static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
bool distinct, const char *message=NullS);
static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
-void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg,
- double *record_count_arg);
static uint make_join_orderinfo(JOIN *join);
static int
join_read_record_no_init(JOIN_TAB *tab);
@@ -580,6 +578,13 @@ JOIN::prepare(Item ***rref_pointer_array,
thd->where="having clause";
thd->lex->allow_sum_func|= 1 << select_lex_arg->nest_level;
select_lex->having_fix_field= 1;
+ /*
+ Wrap alone field in HAVING clause in case it will be outer field of subquery
+ which need persistent pointer on it, but having could be changed by optimizer
+ */
+ if (having->type() == Item::REF_ITEM &&
+ ((Item_ref *)having)->ref_type() == Item_ref::REF)
+ wrap_ident(thd, &having);
bool having_fix_rc= (!having->fixed &&
(having->fix_fields(thd, &having) ||
having->check_cols(1)));
@@ -882,6 +887,7 @@ JOIN::optimize()
"Impossible HAVING" : "Impossible WHERE";
tables= 0;
error= 0;
+ choose_tableless_subquery_plan();
goto setup_subq_exit;
}
}
@@ -926,12 +932,13 @@ JOIN::optimize()
*/
if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
{
- if (res == HA_ERR_KEY_NOT_FOUND)
+ if (res == HA_ERR_KEY_NOT_FOUND || res < 0)
{
DBUG_PRINT("info",("No matching min/max row"));
zero_result_cause= "No matching min/max row";
tables= 0;
error=0;
+ choose_tableless_subquery_plan();
goto setup_subq_exit;
}
if (res > 1)
@@ -940,14 +947,7 @@ JOIN::optimize()
DBUG_PRINT("error",("Error from opt_sum_query"));
DBUG_RETURN(1);
}
- if (res < 0)
- {
- DBUG_PRINT("info",("No matching min/max row"));
- zero_result_cause= "No matching min/max row";
- tables= 0;
- error=0;
- goto setup_subq_exit;
- }
+
DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away";
tables_list= 0; // All tables resolved
@@ -965,24 +965,22 @@ JOIN::optimize()
if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
{
COND *table_independent_conds=
- make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE, FALSE);
+ make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES,
+ FALSE, FALSE);
DBUG_EXECUTE("where",
print_where(table_independent_conds,
"where after opt_sum_query()",
QT_ORDINARY););
conds= table_independent_conds;
}
- goto setup_subq_exit;
}
}
if (!tables_list)
{
DBUG_PRINT("info",("No tables"));
error= 0;
- /* Create all structures needed for materialized subquery execution. */
- if (setup_subquery_materialization())
- DBUG_RETURN(1);
- DBUG_RETURN(0);
+ choose_tableless_subquery_plan();
+ goto setup_subq_exit;
}
error= -1; // Error is sent to client
sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
@@ -1374,8 +1372,7 @@ JOIN::optimize()
if (!(select_options & SELECT_DESCRIBE))
init_ftfuncs(thd, select_lex, test(order));
- /* Create all structures needed for materialized subquery execution. */
- if (setup_subquery_materialization())
+ if (optimize_unflattened_subqueries())
DBUG_RETURN(1);
int res;
@@ -1479,6 +1476,34 @@ JOIN::optimize()
if (join_tab->is_using_loose_index_scan())
tmp_table_param.precomputed_group_by= TRUE;
+ error= 0;
+ DBUG_RETURN(0);
+
+setup_subq_exit:
+ /*
+ Even with zero matching rows, subqueries in the HAVING clause may
+ need to be evaluated if there are aggregate functions in the query.
+ */
+ if (optimize_unflattened_subqueries())
+ DBUG_RETURN(1);
+ error= 0;
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Create and initialize objects neeed for the execution of a query plan.
+ Evaluate constant expressions not evaluated during optimization.
+*/
+
+int JOIN::init_execution()
+{
+ DBUG_ENTER("JOIN::init_execution");
+
+ DBUG_ASSERT(optimized);
+ DBUG_ASSERT(!(select_options & SELECT_DESCRIBE));
+ initialized= true;
+
/* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp)
{
@@ -1511,7 +1536,7 @@ JOIN::optimize()
select_options,
tmp_rows_limit,
(char *) "")))
- {
+ {
DBUG_RETURN(1);
}
@@ -1597,19 +1622,6 @@ JOIN::optimize()
DBUG_RETURN(-1); /* purecov: inspected */
}
- error= 0;
- DBUG_RETURN(0);
-
-setup_subq_exit:
- /*
- Even with zero matching rows, subqueries in the HAVING clause may
- need to be evaluated if there are aggregate functions in the
- query. If we have planned to materialize the subquery, we need to
- set it up properly before prematurely leaving optimize().
- */
- if (setup_subquery_materialization())
- DBUG_RETURN(1);
- error= 0;
DBUG_RETURN(0);
}
@@ -1958,6 +1970,16 @@ JOIN::exec()
if (tables)
thd->limit_found_rows= 0;
+ /*
+ Evaluate expensive constant conditions that were not evaluated during
+ optimization. Do not evaluate them for EXPLAIN statements as these
+ condtions may be arbitrarily costly, and because the optimize phase
+ might not have produced a complete executable plan for EXPLAINs.
+ */
+ if (exec_const_cond && !(select_options & SELECT_DESCRIBE) &&
+ !exec_const_cond->val_int())
+ zero_result_cause= "Impossible WHERE noticed after reading const tables";
+
if (zero_result_cause)
{
(void) return_zero_rows(this, result, select_lex->leaf_tables,
@@ -1965,10 +1987,31 @@ JOIN::exec()
send_row_on_empty_set(),
select_options,
zero_result_cause,
- having);
+ having ? having : tmp_having);
DBUG_VOID_RETURN;
}
+ /*
+ Evaluate all constant expressions with subqueries in the ORDER/GROUP clauses
+ to make sure that all subqueries return a single row. The evaluation itself
+ will trigger an error if that is not the case.
+ */
+ if (exec_const_order_group_cond.elements &&
+ !(select_options & SELECT_DESCRIBE))
+ {
+ List_iterator_fast<Item> const_item_it(exec_const_order_group_cond);
+ Item *cur_const_item;
+ while ((cur_const_item= const_item_it++))
+ {
+ cur_const_item->val_str(&cur_const_item->str_value);
+ if (thd->is_error())
+ {
+ error= thd->is_error();
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+
if ((this->select_lex->options & OPTION_SCHEMA_TABLE) &&
get_schema_tables_result(this, PROCESSED_BY_JOIN_EXEC))
DBUG_VOID_RETURN;
@@ -2007,6 +2050,9 @@ JOIN::exec()
DBUG_VOID_RETURN;
}
+ if (!initialized && init_execution())
+ DBUG_VOID_RETURN;
+
JOIN *curr_join= this;
List<Item> *curr_all_fields= &all_fields;
List<Item> *curr_fields_list= &fields_list;
@@ -2337,9 +2383,10 @@ JOIN::exec()
table_map used_tables= (curr_join->const_table_map |
curr_table->table->map);
- Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
+ Item* sort_table_cond= make_cond_for_table(thd, curr_join->tmp_having,
used_tables,
- (table_map)0, FALSE, FALSE);
+ (table_map)0, MAX_TABLES,
+ FALSE, FALSE);
if (sort_table_cond)
{
if (!curr_table->select)
@@ -2375,9 +2422,10 @@ JOIN::exec()
DBUG_EXECUTE("where",print_where(curr_table->select->cond,
"select and having",
QT_ORDINARY););
- curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
+ curr_join->tmp_having= make_cond_for_table(thd, curr_join->tmp_having,
~ (table_map) 0,
- ~used_tables, FALSE, FALSE);
+ ~used_tables, MAX_TABLES,
+ FALSE, FALSE);
DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
"having after sort",
QT_ORDINARY););
@@ -2727,51 +2775,6 @@ err:
}
-/**
- Setup for execution all subqueries of a query, for which the optimizer
- chose hash semi-join.
-
- @details Iterate over all subqueries of the query, and if they are under an
- IN predicate, and the optimizer chose to compute it via hash semi-join:
- - try to initialize all data structures needed for the materialized execution
- of the IN predicate,
- - if this fails, then perform the IN=>EXISTS transformation which was
- previously blocked during JOIN::prepare.
-
- This method is part of the "code generation" query processing phase.
-
- This phase must be called after substitute_for_best_equal_field() because
- that function may replace items with other items from a multiple equality,
- and we need to reference the correct items in the index access method of the
- IN predicate.
-
- @return Operation status
- @retval FALSE success.
- @retval TRUE error occurred.
-*/
-
-bool JOIN::setup_subquery_materialization()
-{
- for (SELECT_LEX_UNIT *un= select_lex->first_inner_unit(); un;
- un= un->next_unit())
- {
- for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
- {
- Item_subselect *subquery_predicate= sl->master_unit()->item;
- if (subquery_predicate &&
- subquery_predicate->substype() == Item_subselect::IN_SUBS)
- {
- Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
- if (in_subs->exec_method == Item_in_subselect::MATERIALIZATION &&
- in_subs->setup_engine())
- return TRUE;
- }
- }
- }
- return FALSE;
-}
-
-
/*****************************************************************************
Create JOIN_TABS, make a guess about the table types,
Approximate how many records will be used in each table
@@ -2997,10 +3000,14 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
}
if (conds || outer_join)
+ {
if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
- conds, join->cond_equal,
- ~outer_join, join->select_lex, &sargables))
+ conds, ~outer_join, join->select_lex, &sargables))
+ goto error;
+ if (keyuse_array->elements && sort_and_filter_keyuse(keyuse_array))
goto error;
+ DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array););
+ }
join->const_table_map= no_rows_const_tables;
join->const_tables= const_count;
@@ -3307,8 +3314,12 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
{
memcpy((uchar*) join->best_positions,(uchar*) join->positions,
sizeof(POSITION)*join->const_tables);
+ join->record_count= 1.0;
join->best_read=1.0;
}
+ if (join->choose_subquery_plan(all_table_map & ~join->const_table_map))
+ goto error;
+
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join));
@@ -4321,11 +4332,10 @@ static void add_key_fields_for_nj(JOIN *join, TABLE_LIST *nested_join_table,
static bool
update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
- uint tables, COND *cond, COND_EQUAL *cond_equal,
- table_map normal_tables, SELECT_LEX *select_lex,
- SARGABLE_PARAM **sargables)
+ uint tables, COND *cond, table_map normal_tables,
+ SELECT_LEX *select_lex, SARGABLE_PARAM **sargables)
{
- uint and_level,i,found_eq_constant;
+ uint and_level,i;
KEY_FIELD *key_fields, *end, *field;
uint sz;
uint m= max(select_lex->max_equal_elems,1);
@@ -4421,73 +4431,81 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
return TRUE;
}
- /*
- Sort the array of possible keys and remove the following key parts:
- - ref if there is a keypart which is a ref and a const.
- (e.g. if there is a key(a,b) and the clause is a=3 and b=7 and b=t2.d,
- then we skip the key part corresponding to b=t2.d)
- - keyparts without previous keyparts
- (e.g. if there is a key(a,b,c) but only b < 5 (or a=2 and c < 3) is
- used in the query, we drop the partial key parts from consideration).
- Special treatment for ft-keys.
- */
- if (keyuse->elements)
- {
- KEYUSE key_end,*prev,*save_pos,*use;
+ return FALSE;
+}
- my_qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE),
- (qsort_cmp) sort_keyuse);
- bzero((char*) &key_end,sizeof(key_end)); /* Add for easy testing */
- if (insert_dynamic(keyuse,(uchar*) &key_end))
- return TRUE;
+/**
+ Sort the array of possible keys and remove the following key parts:
+ - ref if there is a keypart which is a ref and a const.
+ (e.g. if there is a key(a,b) and the clause is a=3 and b=7 and b=t2.d,
+ then we skip the key part corresponding to b=t2.d)
+ - keyparts without previous keyparts
+ (e.g. if there is a key(a,b,c) but only b < 5 (or a=2 and c < 3) is
+ used in the query, we drop the partial key parts from consideration).
+ Special treatment for ft-keys.
+*/
+
+static bool sort_and_filter_keyuse(DYNAMIC_ARRAY *keyuse)
+{
+ KEYUSE key_end, *prev, *save_pos, *use;
+ uint found_eq_constant, i;
- use=save_pos=dynamic_element(keyuse,0,KEYUSE*);
- prev= &key_end;
- found_eq_constant=0;
- for (i=0 ; i < keyuse->elements-1 ; i++,use++)
+ DBUG_ASSERT(keyuse->elements);
+
+ my_qsort(keyuse->buffer, keyuse->elements, sizeof(KEYUSE),
+ (qsort_cmp) sort_keyuse);
+
+ bzero((char*) &key_end, sizeof(key_end)); /* Add for easy testing */
+ if (insert_dynamic(keyuse, (uchar*) &key_end))
+ return TRUE;
+
+ use= save_pos= dynamic_element(keyuse,0,KEYUSE*);
+ prev= &key_end;
+ found_eq_constant= 0;
+ for (i=0 ; i < keyuse->elements-1 ; i++,use++)
+ {
+ if (!use->is_for_hash_join())
{
- if (!use->is_for_hash_join())
+ if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
+ use->table->const_key_parts[use->key]|= use->keypart_map;
+ if (use->keypart != FT_KEYPART)
{
- if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
- use->table->const_key_parts[use->key]|= use->keypart_map;
- if (use->keypart != FT_KEYPART)
+ if (use->key == prev->key && use->table == prev->table)
{
- if (use->key == prev->key && use->table == prev->table)
- {
- if (prev->keypart+1 < use->keypart ||
- (prev->keypart == use->keypart && found_eq_constant))
- continue; /* remove */
- }
- else if (use->keypart != 0) // First found must be 0
- continue;
+ if (prev->keypart+1 < use->keypart ||
+ (prev->keypart == use->keypart && found_eq_constant))
+ continue; /* remove */
}
-
- prev= use;
- found_eq_constant= !use->used_tables;
- use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
+ else if (use->keypart != 0) // First found must be 0
+ continue;
}
- /*
- Old gcc used a memcpy(), which is undefined if save_pos==use:
- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19410
- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39480
- This also disables a valgrind warning, so better to have the test.
- */
- if (save_pos != use)
- *save_pos= *use;
- /* Save ptr to first use */
- if (!use->table->reginfo.join_tab->keyuse)
- use->table->reginfo.join_tab->keyuse= save_pos;
- save_pos++;
- }
- i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
- VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
- keyuse->elements=i;
- }
- DBUG_EXECUTE("opt", print_keyuse_array(keyuse););
+
+ prev= use;
+ found_eq_constant= !use->used_tables;
+ use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
+ }
+ /*
+ Old gcc used a memcpy(), which is undefined if save_pos==use:
+ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19410
+ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39480
+ This also disables a valgrind warning, so better to have the test.
+ */
+ if (save_pos != use)
+ *save_pos= *use;
+ /* Save ptr to first use */
+ if (!use->table->reginfo.join_tab->keyuse)
+ use->table->reginfo.join_tab->keyuse= save_pos;
+ save_pos++;
+ }
+ i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
+ VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
+ keyuse->elements= i;
+
return FALSE;
}
+
/**
Update some values in keyuse for faster choose_plan() loop.
*/
@@ -4777,8 +4795,8 @@ best_access_path(JOIN *join,
if (!(keyuse->used_tables & ~join->const_table_map))
const_part|= keyuse->keypart_map;
- double tmp2= prev_record_reads(join, idx, (found_ref |
- keyuse->used_tables));
+ double tmp2= prev_record_reads(join->positions, idx,
+ (found_ref | keyuse->used_tables));
if (tmp2 < best_prev_record_reads)
{
best_part_found_ref= keyuse->used_tables & ~join->const_table_map;
@@ -4818,7 +4836,7 @@ best_access_path(JOIN *join,
Really, there should be records=0.0 (yes!)
but 1.0 would be probably safer
*/
- tmp= prev_record_reads(join, idx, found_ref);
+ tmp= prev_record_reads(join->positions, idx, found_ref);
records= 1.0;
}
else
@@ -4833,7 +4851,7 @@ best_access_path(JOIN *join,
max_key_part= (uint) ~0;
if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
{
- tmp = prev_record_reads(join, idx, found_ref);
+ tmp = prev_record_reads(join->positions, idx, found_ref);
records=1.0;
}
else
@@ -5565,6 +5583,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
read_time+= record_count; // We have to make a temp table
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION)*idx);
+ join->record_count= record_count;
join->best_read= read_time;
}
@@ -5746,40 +5765,45 @@ greedy_search(JOIN *join,
}
-/*
- Calculate a cost of given partial join order
+/**
+ Calculate a cost of given partial join order in join->positions.
- SYNOPSIS
- get_partial_join_cost()
- join IN Join to use. join->positions holds the
- partial join order
- idx IN # tables in the partial join order
- read_time_arg OUT Store read time here
- record_count_arg OUT Store record count here
+ @param n_tables[in] # tables in the partial join order after the last
+ constant table
+ @param read_time_arg[out] store read time here
+ @param record_count_arg[out] store record count here
- DESCRIPTION
-
- This is needed for semi-join materialization code. The idea is that
- we detect sj-materialization after we've put all sj-inner tables into
- the join prefix
+ @note
+ When used by semi-join materialization code the idea is that we
+ detect sj-materialization after we've put all sj-inner tables into
+ the join prefix.
prefix-tables semi-join-inner-tables tN
^--we're here
and we'll need to get the cost of prefix-tables prefix again.
+
+ When used with non-flattened subqueries, the method computes the
+ total cost of query plan.
+
+ @returns
+ read_time_arg and record_count_arg contain the computed cost.
*/
-void get_partial_join_cost(JOIN *join, uint n_tables, double *read_time_arg,
- double *record_count_arg)
+void JOIN::get_partial_join_cost(uint n_tables,
+ double *read_time_arg, double *record_count_arg)
{
double record_count= 1;
double read_time= 0.0;
- for (uint i= join->const_tables; i < n_tables + join->const_tables ; i++)
+
+ DBUG_ASSERT(n_tables <= tables);
+
+ for (uint i= const_tables; i < n_tables; i++)
{
- if (join->best_positions[i].records_read)
+ if (best_positions[i].records_read)
{
- record_count *= join->best_positions[i].records_read;
- read_time += join->best_positions[i].read_time;
+ record_count *= best_positions[i].records_read;
+ read_time += best_positions[i].read_time;
}
}
*read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE;
@@ -5787,8 +5811,6 @@ void get_partial_join_cost(JOIN *join, uint n_tables, double *read_time_arg,
}
-
-
/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -6045,6 +6067,7 @@ best_extension_by_limited_search(JOIN *join,
{
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION) * (idx + 1));
+ join->record_count= current_record_count;
join->best_read= current_read_time - 0.001;
}
DBUG_EXECUTE("opt", print_plan(join, idx+1,
@@ -6233,8 +6256,9 @@ int JOIN_TAB::make_scan_filter()
*get_first_inner_table()->on_expr_ref : join->conds;
if (cond &&
- (tmp=make_cond_for_table(cond, join->const_table_map | table->map,
- table->map, FALSE, TRUE)))
+ (tmp= make_cond_for_table(join->thd, cond,
+ join->const_table_map | table->map,
+ table->map, MAX_TABLES, FALSE, TRUE)))
{
DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
if (!(cache_select=
@@ -6346,12 +6370,12 @@ cache_record_length(JOIN *join,uint idx)
Expected number of row combinations
*/
-static double
-prev_record_reads(JOIN *join, uint idx, table_map found_ref)
+double
+prev_record_reads(POSITION *positions, uint idx, table_map found_ref)
{
double found=1.0;
- POSITION *pos_end= join->positions - 1;
- for (POSITION *pos= join->positions + idx - 1; pos != pos_end; pos--)
+ POSITION *pos_end= positions - 1;
+ for (POSITION *pos= positions + idx - 1; pos != pos_end; pos--)
{
if (pos->table->table->map & found_ref)
{
@@ -6853,7 +6877,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
}
-inline void add_cond_and_fix(Item **e1, Item *e2)
+inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2)
{
if (*e1)
{
@@ -6863,7 +6887,7 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
if ((res= new Item_cond_and(*e1, e2)))
{
*e1= res;
- res->quick_fix_field();
+ res->fix_fields(thd, 0);
res->update_used_tables();
}
}
@@ -6967,14 +6991,14 @@ static void add_not_null_conds(JOIN *join)
COND *new_cond= referred_tab->join == join ?
referred_tab->select_cond :
join->outer_ref_cond;
- add_cond_and_fix(&new_cond, notnull);
+ add_cond_and_fix(join->thd, &new_cond, notnull);
if (referred_tab->join == join)
referred_tab->set_select_cond(new_cond, __LINE__);
else
join->outer_ref_cond= new_cond;
}
else
- add_cond_and_fix(tab->first_inner->on_expr_ref, notnull);
+ add_cond_and_fix(join->thd, tab->first_inner->on_expr_ref, notnull);
}
}
}
@@ -7150,24 +7174,27 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
there inside the triggers.
*/
{ // Check const tables
- COND *const_cond=
- make_cond_for_table(cond,
+ join->exec_const_cond=
+ make_cond_for_table(thd, cond,
join->const_table_map,
- (table_map) 0, TRUE, FALSE);
+ (table_map) 0, MAX_TABLES, FALSE, FALSE);
/* Add conditions added by add_not_null_conds(). */
for (uint i= 0 ; i < join->const_tables ; i++)
- add_cond_and_fix(&const_cond, join->join_tab[i].select_cond);
+ add_cond_and_fix(thd, &join->exec_const_cond,
+ join->join_tab[i].select_cond);
- DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
+ DBUG_EXECUTE("where",print_where(join->exec_const_cond,"constants",
+ QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables;
tab < join->join_tab+join->tables ; tab++)
{
if (*tab->on_expr_ref)
{
JOIN_TAB *cond_tab= tab->first_inner;
- COND *tmp= make_cond_for_table(*tab->on_expr_ref,
+ COND *tmp= make_cond_for_table(thd, *tab->on_expr_ref,
join->const_table_map,
- (table_map) 0, FALSE, FALSE);
+ (table_map) 0, MAX_TABLES,
+ FALSE, FALSE);
if (!tmp)
continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
@@ -7183,18 +7210,22 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
cond_tab->select_cond->quick_fix_field();
}
}
- if (const_cond && !const_cond->val_int())
+
+ if (join->exec_const_cond && !join->exec_const_cond->is_expensive() &&
+ !join->exec_const_cond->val_int())
{
- DBUG_PRINT("info",("Found impossible WHERE condition"));
- DBUG_RETURN(1); // Impossible const condition
+ DBUG_PRINT("info",("Found impossible WHERE condition"));
+ join->exec_const_cond= NULL;
+ DBUG_RETURN(1); // Impossible const condition
}
- COND *outer_ref_cond= make_cond_for_table(cond,
+ COND *outer_ref_cond= make_cond_for_table(thd, cond,
OUTER_REF_TABLE_BIT,
- (table_map) 0, FALSE, FALSE);
+ (table_map) 0, MAX_TABLES,
+ FALSE, FALSE);
if (outer_ref_cond)
{
- add_cond_and_fix(&outer_ref_cond, join->outer_ref_cond);
+ add_cond_and_fix(thd, &outer_ref_cond, join->outer_ref_cond);
join->outer_ref_cond= outer_ref_cond;
}
}
@@ -7266,10 +7297,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tmp= NULL;
if (cond)
- tmp= make_cond_for_table(cond, used_tables, current_map, FALSE, FALSE);
+ tmp= make_cond_for_table(thd, cond, used_tables, current_map, i,
+ FALSE, FALSE);
/* Add conditions added by add_not_null_conds(). */
if (tab->select_cond)
- add_cond_and_fix(&tmp, tab->select_cond);
+ add_cond_and_fix(thd, &tmp, tab->select_cond);
is_hj= (tab->type == JT_REF || tab->type == JT_EQ_REF) &&
(join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) &&
@@ -7337,7 +7369,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (thd->variables.engine_condition_pushdown && !first_inner_tab)
{
COND *push_cond=
- make_cond_for_table(tmp, current_map, current_map, FALSE, FALSE);
+ make_cond_for_table(thd, tmp, current_map, current_map,
+ MAX_TABLES, FALSE, FALSE);
if (push_cond)
{
/* Push condition to handler */
@@ -7487,9 +7520,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (*join_tab->on_expr_ref)
{
JOIN_TAB *cond_tab= join_tab->first_inner;
- COND *tmp= make_cond_for_table(*join_tab->on_expr_ref,
+ COND *tmp= make_cond_for_table(thd, *join_tab->on_expr_ref,
join->const_table_map,
- (table_map) 0, FALSE, FALSE);
+ (table_map) 0, MAX_TABLES, FALSE, FALSE);
if (!tmp)
continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
@@ -7508,6 +7541,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
/* Push down non-constant conditions from on expressions */
+ JOIN_TAB *first_tab= join->join_tab+join->const_tables;
JOIN_TAB *last_tab= tab;
while (first_inner_tab && first_inner_tab->last_inner == last_tab)
{
@@ -7519,14 +7553,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
table_map used_tables2= (join->const_table_map |
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
- for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++)
+ for (tab= first_tab; tab <= last_tab ; tab++)
{
current_map= tab->table->map;
used_tables2|= current_map;
- COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
- current_map, FALSE, FALSE);
+ COND *tmp_cond= make_cond_for_table(thd, on_expr, used_tables2,
+ current_map, (tab - first_tab),
+ FALSE, FALSE);
if (tab == first_inner_tab && tab->on_precond)
- add_cond_and_fix(&tmp_cond, tab->on_precond);
+ add_cond_and_fix(thd, &tmp_cond, tab->on_precond);
if (tmp_cond)
{
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
@@ -9034,11 +9069,16 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0; // Must do a temp table to sort
else if (!(order_tables & not_const_tables))
{
- if (order->item[0]->with_subselect &&
- !(join->select_lex->options & SELECT_DESCRIBE))
- order->item[0]->val_str(&order->item[0]->str_value);
+ if (order->item[0]->with_subselect)
+ {
+ /*
+ Delay the evaluation of constant ORDER and/or GROUP expressions that
+ contain subqueries until the execution phase.
+ */
+ join->exec_const_order_group_cond.push_back(order->item[0]);
+ }
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
- continue; // skip const item
+ continue;
}
else
{
@@ -11387,7 +11427,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
}
}
}
- if (cond->const_item())
+ if (cond->const_item() && !cond->is_expensive())
{
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
return (COND*) 0;
@@ -12223,10 +12263,30 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
if (thd->is_fatal_error)
goto err; // Got OOM
- continue; // Some kindf of const item
+ continue; // Some kind of const item
}
if (type == Item::SUM_FUNC_ITEM)
- ((Item_sum *) item)->result_field= new_field;
+ {
+ Item_sum *agg_item= (Item_sum *) item;
+ /*
+ Update the result field only if it has never been set, or if the
+ created temporary table is not to be used for subquery
+ materialization.
+
+ The reason is that for subqueries that require materialization as part
+ of their plan, we create the 'external' temporary table needed for IN
+ execution, after the 'internal' temporary table needed for grouping.
+ Since both the external and the internal temporary tables are created
+ for the same list of SELECT fields of the subquery, setting
+ 'result_field' for each invocation of create_tmp_table overrides the
+ previous value of 'result_field'.
+
+ The condition below prevents the creation of the external temp table
+ to override the 'result_field' that was set for the internal temp table.
+ */
+ if (!agg_item->result_field || !param->materialized_subquery)
+ agg_item->result_field= new_field;
+ }
tmp_from_field++;
reclength+=new_field->pack_length();
if (!(new_field->flags & NOT_NULL_FLAG))
@@ -15433,7 +15493,6 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
}
-
/*
Extract a condition that can be checked after reading given table
@@ -15443,6 +15502,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
tables Tables for which "current field values" are available
used_table Table that we're extracting the condition for (may
also include PSEUDO_TABLE_BITS
+ join_tab_idx_arg The index of the JOIN_TAB this Item is being extracted
+ for. MAX_TABLES if there is no corresponding JOIN_TAB.
exclude_expensive_cond Do not push expensive conditions
retain_ref_cond Retain ref conditions
@@ -15469,34 +15530,27 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
*/
static Item *
-make_cond_for_table(Item *cond, table_map tables, table_map used_table,
- bool exclude_expensive_cond, bool retain_ref_cond)
+make_cond_for_table(THD *thd, Item *cond, table_map tables,
+ table_map used_table,
+ uint join_tab_idx_arg,
+ bool exclude_expensive_cond __attribute__((unused)),
+ bool retain_ref_cond)
{
- return make_cond_for_table_from_pred(cond, cond, tables, used_table,
+ return make_cond_for_table_from_pred(thd, cond, cond, tables, used_table,
+ join_tab_idx_arg,
exclude_expensive_cond,
retain_ref_cond);
}
-
+
static Item *
-make_cond_for_table_from_pred(Item *root_cond, Item *cond,
+make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond,
table_map tables, table_map used_table,
- bool exclude_expensive_cond,
+ uint join_tab_idx_arg,
+ bool exclude_expensive_cond __attribute__((unused)),
bool retain_ref_cond)
{
- if (used_table && !(cond->used_tables() & used_table) &&
- /*
- Exclude constant conditions not checked at optimization time if
- the table we are pushing conditions to is the first one.
- As a result, such conditions are not considered as already checked
- and will be checked at execution time, attached to the first table.
-
- psergey: TODO: "used_table & 1" doesn't make sense in nearly any
- context. Look at setup_table_map(), table bits reflect the order
- the tables were encountered by the parser. Check what we should
- replace this condition with.
- */
- !((used_table & 1) && cond->is_expensive()))
+ if (used_table && !(cond->used_tables() & used_table))
return (COND*) 0; // Already checked
if (cond->type() == Item::COND_ITEM)
{
@@ -15510,8 +15564,9 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
Item *item;
while ((item=li++))
{
- Item *fix=make_cond_for_table_from_pred(root_cond, item,
+ Item *fix=make_cond_for_table_from_pred(thd, root_cond, item,
tables, used_table,
+ join_tab_idx_arg,
exclude_expensive_cond,
retain_ref_cond);
if (fix)
@@ -15524,10 +15579,11 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
return new_cond->argument_list()->head();
default:
/*
- Item_cond_and do not need fix_fields for execution, its parameters
- are fixed or do not need fix_fields, too
+ Call fix_fields to propagate all properties of the children to
+ the new parent Item. This should not be expensive because all
+ children of Item_cond_and should be fixed by now.
*/
- new_cond->quick_fix_field();
+ new_cond->fix_fields(thd, 0);
new_cond->used_tables_cache=
((Item_cond_and*) cond)->used_tables_cache &
tables;
@@ -15543,8 +15599,9 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
Item *item;
while ((item=li++))
{
- Item *fix=make_cond_for_table_from_pred(root_cond, item,
+ Item *fix=make_cond_for_table_from_pred(thd, root_cond, item,
tables, 0L,
+ join_tab_idx_arg,
exclude_expensive_cond,
retain_ref_cond);
if (!fix)
@@ -15552,10 +15609,11 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
new_cond->argument_list()->push_back(fix);
}
/*
- Item_cond_and do not need fix_fields for execution, its parameters
- are fixed or do not need fix_fields, too
+ Call fix_fields to propagate all properties of the children to
+ the new parent Item. This should not be expensive because all
+ children of Item_cond_and should be fixed by now.
*/
- new_cond->quick_fix_field();
+ new_cond->fix_fields(thd, 0);
new_cond->used_tables_cache= ((Item_cond_or*) cond)->used_tables_cache;
new_cond->top_level_item();
return new_cond;
@@ -15567,16 +15625,14 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_count times, we mark each item that we have examined with the result
of the test
*/
- if ((cond->marker == 3 && !retain_ref_cond) ||
- (cond->used_tables() & ~tables) ||
- /*
- When extracting constant conditions, treat expensive conditions as
- non-constant, so that they are not evaluated at optimization time.
- */
- (!used_table && exclude_expensive_cond && cond->is_expensive()))
+ if ((cond->marker == 3 && !retain_ref_cond) ||
+ (cond->used_tables() & ~tables))
return (COND*) 0; // Can't check this yet
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
+ {
+ cond->set_join_tab_idx(join_tab_idx_arg);
return cond; // Not boolean op
+ }
if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
@@ -15597,11 +15653,11 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
}
}
cond->marker=2;
+ cond->set_join_tab_idx(join_tab_idx_arg);
return cond;
}
-
static COND *
make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables,
table_map sjm_tables)
@@ -16815,7 +16871,8 @@ static bool fix_having(JOIN *join, Item **having)
table_map used_tables= join->const_table_map | table->table->map;
DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
- Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables,
+ Item* sort_table_cond= make_cond_for_table(join->thd, *having, used_tables,
+ used_tables, MAX_TABLES,
FALSE, FALSE);
if (sort_table_cond)
{
@@ -16834,8 +16891,9 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE("where",print_where(table->select_cond,
"select and having",
QT_ORDINARY););
- *having= make_cond_for_table(*having,~ (table_map) 0,~used_tables,
- FALSE, FALSE);
+ *having= make_cond_for_table(join->thd, *having,
+ ~ (table_map) 0,~used_tables,
+ MAX_TABLES, FALSE, FALSE);
DBUG_EXECUTE("where",
print_where(*having,"having after make_cond", QT_ORDINARY););
}
@@ -19798,28 +19856,9 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
bool res= 0;
SELECT_LEX *first= unit->first_select();
- for (SELECT_LEX *sl= first;
- sl;
- sl= sl->next_select())
- {
- // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only
- uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN);
- sl->type= (((&thd->lex->select_lex)==sl)?
- (sl->first_inner_unit() || sl->next_select() ?
- "PRIMARY" : "SIMPLE"):
- ((sl == first)?
- ((sl->linkage == DERIVED_TABLE_TYPE) ?
- "DERIVED":
- ((uncacheable & UNCACHEABLE_DEPENDENT) ?
- "DEPENDENT SUBQUERY":
- (uncacheable?"UNCACHEABLE SUBQUERY":
- "SUBQUERY"))):
- ((uncacheable & UNCACHEABLE_DEPENDENT) ?
- "DEPENDENT UNION":
- uncacheable?"UNCACHEABLE UNION":
- "UNION")));
- sl->options|= SELECT_DESCRIBE;
- }
+ for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
+ sl->set_explain_type();
+
if (unit->is_union())
{
unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
@@ -20235,6 +20274,8 @@ bool JOIN::change_result(select_result *res)
{
DBUG_ENTER("JOIN::change_result");
result= res;
+ if (tmp_join)
+ tmp_join->result= res;
if (!procedure && (result->prepare(fields_list, select_lex->master_unit()) ||
result->prepare2()))
{
@@ -20278,5 +20319,154 @@ void JOIN::set_allowed_join_cache_types()
/**
+ Save a query execution plan so that the caller can revert to it if needed,
+ and reset the current query plan so that it can be reoptimized.
+
+ @param save_to The object into which the current query plan state is saved
+*/
+
+void JOIN::save_query_plan(Query_plan_state *save_to)
+{
+ if (keyuse.elements)
+ {
+ DYNAMIC_ARRAY tmp_keyuse;
+ /* Swap the current and the backup keyuse internal arrays. */
+ tmp_keyuse= keyuse;
+ keyuse= save_to->keyuse; /* keyuse is reset to an empty array. */
+ save_to->keyuse= tmp_keyuse;
+
+ for (uint i= 0; i < tables; i++)
+ {
+ save_to->join_tab_keyuse[i]= join_tab[i].keyuse;
+ join_tab[i].keyuse= NULL;
+ save_to->join_tab_checked_keys[i]= join_tab[i].checked_keys;
+ join_tab[i].checked_keys.clear_all();
+ }
+ }
+ memcpy((uchar*) save_to->best_positions, (uchar*) best_positions,
+ sizeof(POSITION) * (tables + 1));
+ memset(best_positions, 0, sizeof(POSITION) * (tables + 1));
+}
+
+
+/**
+ Restore a query execution plan previously saved by the caller.
+
+ @param The object from which the current query plan state is restored.
+*/
+
+void JOIN::restore_query_plan(Query_plan_state *restore_from)
+{
+ if (restore_from->keyuse.elements)
+ {
+ DYNAMIC_ARRAY tmp_keyuse;
+ tmp_keyuse= keyuse;
+ keyuse= restore_from->keyuse;
+ restore_from->keyuse= tmp_keyuse;
+
+ for (uint i= 0; i < tables; i++)
+ {
+ join_tab[i].keyuse= restore_from->join_tab_keyuse[i];
+ join_tab[i].checked_keys= restore_from->join_tab_checked_keys[i];
+ }
+
+ }
+ memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions,
+ sizeof(POSITION) * (tables + 1));
+}
+
+
+/**
+ Reoptimize a query plan taking into account an additional conjunct to the
+ WHERE clause.
+
+ @param added_where An extra conjunct to the WHERE clause to reoptimize with
+ @param join_tables The set of tables to reoptimize
+ @param save_to If != NULL, save here the state of the current query plan
+
+ @notes
+ Given a query plan that was already optimized taking into account some WHERE
+ clause 'C', reoptimize this plan with a new WHERE clause 'C AND added_where'.
+ The reoptimization works as follows:
+
+ 1. Call update_ref_and_keys *only* for the new conditions 'added_where'
+ that are about to be injected into the query.
+ 2. Expand if necessary the original KEYUSE array JOIN::keyuse to
+ accommodate the new REF accesses computed for the 'added_where' condition.
+ 3. Add the new KEYUSEs into JOIN::keyuse.
+ 4. Re-sort and re-filter the JOIN::keyuse array with the newly added
+ KEYUSE elements.
+
+ @retval REOPT_NEW_PLAN there is a new plan.
+ @retval REOPT_OLD_PLAN no new improved plan was produced, use the old one.
+ @retval REOPT_ERROR an irrecovarable error occured during reoptimization.
+*/
+
+JOIN::enum_reopt_result
+JOIN::reoptimize(Item *added_where, table_map join_tables,
+ Query_plan_state *save_to)
+{
+ DYNAMIC_ARRAY added_keyuse;
+ SARGABLE_PARAM *sargables= 0; /* Used only as a dummy parameter. */
+ uint org_keyuse_elements;
+
+ /* Re-run the REF optimizer to take into account the new conditions. */
+ if (update_ref_and_keys(thd, &added_keyuse, join_tab, tables, added_where,
+ ~outer_join, select_lex, &sargables))
+ {
+ delete_dynamic(&added_keyuse);
+ return REOPT_ERROR;
+ }
+
+ if (!added_keyuse.elements)
+ {
+ delete_dynamic(&added_keyuse);
+ return REOPT_OLD_PLAN;
+ }
+
+ if (save_to)
+ save_query_plan(save_to);
+
+ if (!keyuse.buffer &&
+ my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
+ {
+ delete_dynamic(&added_keyuse);
+ return REOPT_ERROR;
+ }
+
+ org_keyuse_elements= save_to ? save_to->keyuse.elements : keyuse.elements;
+ allocate_dynamic(&keyuse, org_keyuse_elements + added_keyuse.elements);
+
+ /* If needed, add the access methods from the original query plan. */
+ if (save_to)
+ {
+ DBUG_ASSERT(!keyuse.elements);
+ memcpy(keyuse.buffer,
+ save_to->keyuse.buffer,
+ (size_t) save_to->keyuse.elements * keyuse.size_of_element);
+ keyuse.elements= save_to->keyuse.elements;
+ }
+
+ /* Add the new access methods to the keyuse array. */
+ memcpy(keyuse.buffer + keyuse.elements * keyuse.size_of_element,
+ added_keyuse.buffer,
+ (size_t) added_keyuse.elements * added_keyuse.size_of_element);
+ keyuse.elements+= added_keyuse.elements;
+ /* added_keyuse contents is copied, and it is no longer needed. */
+ delete_dynamic(&added_keyuse);
+
+ if (sort_and_filter_keyuse(&keyuse))
+ return REOPT_ERROR;
+ optimize_keyuse(this, &keyuse);
+
+ /* Re-run the join optimizer to compute a new query plan. */
+ if (choose_plan(this, join_tables))
+ return REOPT_ERROR;
+
+ return REOPT_NEW_PLAN;
+}
+
+
+/**
@} (end of group Query_Optimizer)
*/