Commit 0bee625f authored by unknown's avatar unknown

MWL#89

Post-review fixes. Intermediate commit to address
review points 1.1, 1.2, 1.3, 1.4, 1.5, and 3.1, 3.2, 3.3.
parent fd24b33b
......@@ -386,6 +386,8 @@ Item::Item():
decimals= 0; max_length= 0;
with_subselect= 0;
cmp_context= IMPOSSIBLE_RESULT;
/* Initially this item is not attached to any JOIN_TAB. */
join_tab_idx= MAX_TABLES;
/* Put item in free list so that we can free all items at end */
THD *thd= current_thd;
......@@ -414,6 +416,7 @@ Item::Item():
tables.
*/
Item::Item(THD *thd, Item *item):
join_tab_idx(item->join_tab_idx),
is_expensive_cache(-1),
rsize(0),
str_value(item->str_value),
......@@ -470,6 +473,7 @@ void Item::cleanup()
DBUG_ENTER("Item::cleanup");
fixed=0;
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
name= orig_name;
DBUG_VOID_RETURN;
......
......@@ -491,6 +491,17 @@ typedef void (*Cond_traverser) (const Item *item, void *arg);
class Item {
Item(const Item &); /* Prevent use of these */
void operator=(Item &);
/**
The index in the JOIN::join_tab array of the JOIN_TAB this Item is attached
to. Items are attached (or 'pushed') to JOIN_TABs during optimization by the
make_cond_for_table procedure. During query execution, this item is
evaluated when the join loop reaches the corresponding JOIN_TAB.
If the value of join_tab_idx >= MAX_TABLES, this means that there is no
corresponding JOIN_TAB.
*/
uint join_tab_idx;
public:
static void *operator new(size_t size) throw ()
{ return sql_alloc(size); }
......@@ -1179,6 +1190,16 @@ class Item {
Item* set_expr_cache(THD *thd, List<Item*> &depends_on);
virtual Item *get_cached_item() { return NULL; }
/**
Set the join tab index to the minimal (left-most) JOIN_TAB to which this
Item is attached.
*/
virtual void set_join_tab_idx(uint join_tab_idx_arg)
{
if (join_tab_idx_arg < join_tab_idx)
join_tab_idx= join_tab_idx_arg;
}
virtual uint get_join_tab_idx() { return join_tab_idx; }
};
......
......@@ -268,6 +268,8 @@ class Item_in_optimizer: public Item_bool_func
virtual Item *expr_cache_insert_transformer(uchar *thd_arg);
bool is_expensive_processor(uchar *arg);
bool is_expensive();
void set_join_tab_idx(uint join_tab_idx_arg)
{ args[1]->set_join_tab_idx(join_tab_idx_arg); }
};
class Comp_creator
......
......@@ -172,11 +172,11 @@ Item_subselect::~Item_subselect()
engine= NULL;
}
Item_subselect::trans_res
bool
Item_subselect::select_transformer(JOIN *join)
{
DBUG_ENTER("Item_subselect::select_transformer");
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
......@@ -809,13 +809,17 @@ void Item_singlerow_subselect::reset()
- switch off this optimization for prepare statement,
because we do not rollback this changes.
Make rollback for it, or special name resolving mode in 5.0.
@param join Join object of the subquery (i.e. 'child' join).
@retval false The subquery was transformed
*/
Item_subselect::trans_res
bool
Item_singlerow_subselect::select_transformer(JOIN *join)
{
DBUG_ENTER("Item_singlerow_subselect::select_transformer");
if (changed)
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
SELECT_LEX *select_lex= join->select_lex;
Query_arena *arena= thd->stmt_arena;
......@@ -842,7 +846,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
!arena->is_stmt_prepare_or_first_sp_execute()
)
{
have_to_be_excluded= 1;
if (thd->lex->describe)
{
......@@ -858,9 +861,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
*/
substitution->walk(&Item::remove_dependence_processor, 0,
(uchar *) select_lex->outer_select());
DBUG_RETURN(RES_REDUCE);
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
......@@ -1368,11 +1370,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
@param join Join object of the subquery (i.e. 'child' join).
@details
Rewrite a single-column subquery using rule-based approach. The subquery
Rewrite a single-column subquery using rule-based approach. Given the subquery
oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having)
First, try to convert the subquery to scalar-result subquery in one of
First, try to convert the subquery to a scalar-result subquery in one of
the forms:
- oe $cmp$ (SELECT MAX(...) ) // handled by Item_singlerow_subselect
......@@ -1387,11 +1389,11 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
EXISTS by injecting additional predicates, or will be executed via subquery
materialization in its unmodified form.
@retval RES_OK The subquery was transformed
@retval RES_ERROR Error
@retval false The subquery was transformed
@retval true Error
*/
Item_subselect::trans_res
bool
Item_in_subselect::single_value_transformer(JOIN *join)
{
SELECT_LEX *select_lex= join->select_lex;
......@@ -1405,7 +1407,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (select_lex->item_list.elements > 1)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
/*
......@@ -1425,7 +1427,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (substitution)
{
/* It is second (third, ...) SELECT of UNION => All is done */
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
Item *subs;
......@@ -1470,7 +1472,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
we do not check item->fixed
*/
if (item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
thd->lex->allow_sum_func= save_allow_sum_func;
/* we added aggregate function => we have to change statistic */
count_field_types(select_lex, &join->tmp_table_param, join->all_fields,
......@@ -1487,7 +1489,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
}
/* fix fields is already called for left expression */
substitution= func->create(left_expr, subs);
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
Item* join_having= join->having ? join->having : join->tmp_having;
......@@ -1513,7 +1515,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
/*
......@@ -1533,7 +1535,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
if (!optimizer || optimizer->fix_left(thd, 0))
{
thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
thd->lex->current_select= current;
......@@ -1554,7 +1556,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
// select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
......@@ -1603,11 +1605,11 @@ bool Item_in_subselect::fix_having(Item *having, SELECT_LEX *select_lex)
WHERE subq_where AND trigcond((oe $cmp$ ie) OR (ie IS NULL))
HAVING trigcond(<is_not_null_test>(ie))
@retval RES_OK If the new conditions were created successfully
@retval RES_ERROR Error
@retval false If the new conditions were created successfully
@retval true Error
*/
Item_subselect::trans_res
bool
Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item)
......@@ -1646,7 +1648,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
if (!join_having)
item->name= (char*) in_having_cond;
if (fix_having(item, select_lex))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
*having_item= item;
}
else
......@@ -1666,11 +1668,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
{
if (!(having= new Item_func_trig_cond(having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
having->name= (char*) in_having_cond;
if (fix_having(having, select_lex))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
*having_item= having;
item= new Item_cond_or(item,
......@@ -1683,7 +1685,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
if (!abort_on_null && left_expr->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
/*
......@@ -1693,7 +1695,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
*/
item->name= (char *) in_additional_cond;
if (!item->fixed && item->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
*where_item= item;
}
else
......@@ -1710,12 +1712,12 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
{
if (!(new_having= new Item_func_trig_cond(new_having,
get_cond_guard(0))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
new_having->name= (char*) in_having_cond;
if (fix_having(new_having, select_lex))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
*having_item= new_having;
}
else
......@@ -1723,7 +1725,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
}
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
......@@ -1739,11 +1741,11 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN * join,
additional predicates, or will be executed via subquery materialization in its
unmodified form.
@retval RES_OK The subquery was transformed
@retval RES_ERROR Error
@retval false The subquery was transformed
@retval true Error
*/
Item_subselect::trans_res
bool
Item_in_subselect::row_value_transformer(JOIN *join)
{
SELECT_LEX *select_lex= join->select_lex;
......@@ -1755,7 +1757,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (select_lex->item_list.elements != cols_num)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), cols_num);
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
/*
......@@ -1774,7 +1776,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (!optimizer || optimizer->fix_left(thd, 0))
{
thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
// we will refer to upper level cache array => we have to save it in PS
......@@ -1786,7 +1788,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
//select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
......@@ -1795,18 +1797,51 @@ Item_in_subselect::row_value_transformer(JOIN *join)
subselect into a correlated EXISTS via predicate injection.
@details
There are two cases - either the subquery has aggregates, GROUP BY,
or HAVING, or not. Both cases are described inline in the code.
The correlated predicates are created as follows:
- If the subquery has aggregates, GROUP BY, or HAVING, convert to
(l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having)
=>
EXISTS (SELECT ... HAVING having and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3) and
is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value.
- Otherwise (no aggregates/GROUP BY/HAVING) convert the subquery as follows:
(l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where)
=>
EXISTS (SELECT ... WHERE where and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3)
HAVING is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test registers NULLs values but reject rows.
in case when we do not need correct NULL, we have simplier construction:
EXISTS (SELECT ... WHERE where and
(l1 = v1) and
(l2 = v2) and
(l3 = v3)
@param join[in] Join object of the subquery (i.e. 'child' join).
@param where_item[out] the in-to-exists addition to the where clause
@param having_item[out] the in-to-exists addition to the having clause
@retval RES_OK If the new conditions were created successfully
@retval RES_ERROR Error
@retval false If the new conditions were created successfully
@retval true Error
*/
Item_subselect::trans_res
bool
Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item)
......@@ -1829,19 +1864,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (is_having_used)
{
/*
(l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) =>
EXISTS (SELECT ... HAVING having and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3) and
is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test used to register nulls in case if we have
not found matching to return correct NULL value
TODO: say here explicitly if the order of AND parts matters or not.
*/
/* TODO: say here explicitly if the order of AND parts matters or not. */
Item *item_having_part2= 0;
for (uint i= 0; i < cols_num; i++)
{
......@@ -1853,7 +1876,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
Item *item_eq=
new Item_func_eq(new
Item_ref(&select_lex->context,
......@@ -1865,20 +1888,18 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref(&select_lex->context,
select_lex->ref_pointer_array + i,
(char *)"<no matter>",
(char *)"<list ref>")
);
(char *)"<list ref>"));
Item *item_isnull=
new Item_func_isnull(new
Item_ref(&select_lex->context,
select_lex->ref_pointer_array+i,
(char *)"<no matter>",
(char *)"<list ref>")
);
(char *)"<list ref>"));
Item *col_item= new Item_cond_or(item_eq, item_isnull);
if (!abort_on_null && left_expr->element_index(i)->maybe_null)
{
if (!(col_item= new Item_func_trig_cond(col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
*having_item= and_items(*having_item, col_item);
......@@ -1893,7 +1914,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
{
if (!(item_nnull_test=
new Item_func_trig_cond(item_nnull_test, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
item_having_part2= and_items(item_having_part2, item_nnull_test);
item_having_part2->top_level_item();
......@@ -1902,23 +1923,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
}
else
{
/*
(l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) =>
EXISTS (SELECT ... WHERE where and
(l1 = v1 or is null v1) and
(l2 = v2 or is null v2) and
(l3 = v3 or is null v3)
HAVING is_not_null_test(v1) and
is_not_null_test(v2) and
is_not_null_test(v3))
where is_not_null_test register NULLs values but reject rows
in case when we do not need correct NULL, we have simplier construction:
EXISTS (SELECT ... WHERE where and
(l1 = v1) and
(l2 = v2) and
(l3 = v3)
*/
for (uint i= 0; i < cols_num; i++)
{
Item *item, *item_isnull;
......@@ -1929,7 +1933,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
item=
new Item_func_eq(new
Item_direct_ref(&select_lex->context,
......@@ -1942,8 +1946,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
select_lex->
ref_pointer_array+i,
(char *)"<no matter>",
(char *)"<list ref>")
);
(char *)"<list ref>"));
if (!abort_on_null)
{
Item *having_col_item=
......@@ -1961,8 +1964,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
select_lex->
ref_pointer_array+i,
(char *)"<no matter>",
(char *)"<list ref>")
);
(char *)"<list ref>"));
item= new Item_cond_or(item, item_isnull);
/*
TODO: why we create the above for cases where the right part
......@@ -1971,10 +1973,10 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (left_expr->element_index(i)->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
if (!(having_col_item=
new Item_func_trig_cond(having_col_item, get_cond_guard(i))))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
*having_item= and_items(*having_item, having_col_item);
}
......@@ -1985,7 +1987,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (*where_item)
{
if (!(*where_item)->fixed && (*where_item)->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
(*where_item)->top_level_item();
}
......@@ -1994,15 +1996,15 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
if (!join_having)
(*having_item)->name= (char*) in_having_cond;
if (fix_having(*having_item, select_lex))
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
(*having_item)->top_level_item();
}
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
}
Item_subselect::trans_res
bool
Item_in_subselect::select_transformer(JOIN *join)
{
return select_in_like_transformer(join);
......@@ -2021,7 +2023,7 @@ Item_in_subselect::select_transformer(JOIN *join)
bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
{
Item_subselect::trans_res res;
bool res;
DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE ||
engine->engine_type() == subselect_engine::UNION_ENGINE);
......@@ -2042,7 +2044,7 @@ bool Item_in_subselect::create_in_to_exists_cond(JOIN *join_arg)
res= create_row_in_to_exists_cond(join_arg,
&(join_arg->in_to_exists_where),
&(join_arg->in_to_exists_having));
return (res != RES_OK);
return (res);
}
......@@ -2092,28 +2094,28 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
/**
Prepare IN/ALL/ANY/SOME subquery transformation and call appropriate
Prepare IN/ALL/ANY/SOME subquery transformation and call the appropriate
transformation function.
@param join JOIN object of transforming subquery
@notes
To decide which transformation procedure (scalar or row) applicable here
we have to call fix_fields() for left expression to be able to call
cols() method on it. Also this method make arena management for
we have to call fix_fields() for the left expression to be able to call
cols() method on it. Also this method makes arena management for
underlying transformation methods.
@retval RES_OK OK
@retval RES_ERROR Error
@retval false OK
@retval true Error
*/
Item_subselect::trans_res
bool
Item_in_subselect::select_in_like_transformer(JOIN *join)
{
Query_arena *arena, backup;
SELECT_LEX *current= thd->lex->current_select;
const char *save_where= thd->where;
Item_subselect::trans_res res= RES_ERROR;
bool trans_res= true;
bool result;
DBUG_ENTER("Item_in_subselect::select_in_like_transformer");
......@@ -2132,7 +2134,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
}
if (changed)
DBUG_RETURN(RES_OK);
DBUG_RETURN(false);
thd->where= "IN/ALL/ANY subquery";
......@@ -2172,7 +2174,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
*/
arena= thd->activate_stmt_arena_if_needed(&backup);
if (left_expr->cols() == 1)
res= single_value_transformer(join);
trans_res= single_value_transformer(join);
else
{
/* we do not support row operation for ALL/ANY/SOME */
......@@ -2181,15 +2183,15 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
if (arena)
thd->restore_active_arena(arena, &backup);
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(RES_ERROR);
DBUG_RETURN(true);
}
res= row_value_transformer(join);
trans_res= row_value_transformer(join);
}
if (arena)
thd->restore_active_arena(arena, &backup);
err:
thd->where= save_where;
DBUG_RETURN(res);
DBUG_RETURN(trans_res);
}
......@@ -2381,7 +2383,7 @@ bool Item_in_subselect::init_cond_guards()
}
Item_subselect::trans_res
bool
Item_allany_subselect::select_transformer(JOIN *join)
{
DBUG_ENTER("Item_allany_subselect::select_transformer");
......
......@@ -119,7 +119,6 @@ class Item_subselect :public Item_result_field
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
bool is_correlated;
enum trans_res {RES_OK, RES_REDUCE, RES_ERROR};
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS};
......@@ -148,7 +147,7 @@ class Item_subselect :public Item_result_field
eliminated= FALSE;
null_value= 1;
}
virtual trans_res select_transformer(JOIN *join);
virtual bool select_transformer(JOIN *join);
bool assigned() { return value_assigned; }
void assigned(bool a) { value_assigned= a; }
enum Type type() const;
......@@ -259,7 +258,7 @@ class Item_singlerow_subselect :public Item_subselect
subs_type substype() { return SINGLEROW_SUBS; }
void reset();
trans_res select_transformer(JOIN *join);
bool select_transformer(JOIN *join);
void store(uint i, Item* item);
double val_real();
longlong val_int ();
......@@ -399,16 +398,16 @@ class Item_in_subselect :public Item_exists_subselect
protected:
bool init_cond_guards();
trans_res select_in_like_transformer(JOIN *join);
trans_res single_value_transformer(JOIN *join);
trans_res row_value_transformer(JOIN * join);
bool select_in_like_transformer(JOIN *join);
bool single_value_transformer(JOIN *join);
bool row_value_transformer(JOIN * join);
bool fix_having(Item *having, st_select_lex *select_lex);
trans_res create_single_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item);
trans_res create_row_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item);
bool create_single_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item);
bool create_row_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item);
public:
Item *left_expr;
/* Priority of this predicate in the convert-to-semi-join-nest process. */
......@@ -473,7 +472,7 @@ class Item_in_subselect :public Item_exists_subselect
null_value= 0;
was_null= 0;
}
trans_res select_transformer(JOIN *join);
bool select_transformer(JOIN *join);
bool create_in_to_exists_cond(JOIN *join_arg);
bool inject_in_to_exists_cond(JOIN *join_arg);
......@@ -523,7 +522,7 @@ class Item_allany_subselect :public Item_in_subselect
// only ALL subquery has upper not
subs_type substype() { return all?ALL_SUBS:ANY_SUBS; }
trans_res select_transformer(JOIN *join);
bool select_transformer(JOIN *join);
void create_comp_func(bool invert) { func= func_creator(invert); }
virtual void print(String *str, enum_query_type query_type);
};
......
......@@ -29,6 +29,10 @@ static TABLE_LIST *alloc_join_nest(THD *thd);
static
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist);
static uint get_tmp_table_rec_length(List<Item> &items);
static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count,
uint row_size);
static double get_tmp_table_write_cost(THD *thd, ha_rows row_count,
uint row_size);
bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables);
static SJ_MATERIALIZATION_INFO *
at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab,
......@@ -257,10 +261,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
Transform each subquery predicate according to its overloaded
transformer.
*/
Item_subselect::trans_res trans_res;
if ((trans_res= subselect->select_transformer(join)) !=
Item_subselect::RES_OK)
DBUG_RETURN((trans_res == Item_subselect::RES_ERROR));
if (subselect->select_transformer(join))
DBUG_RETURN(-11);
}
}
DBUG_RETURN(0);
......@@ -502,18 +504,17 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
for (; in_subq!= in_subq_end; in_subq++)
{
JOIN *child_join= (*in_subq)->unit->first_select()->join;
Item_subselect::trans_res res;
(*in_subq)->changed= 0;
(*in_subq)->fixed= 0;
SELECT_LEX *save_select_lex= thd->lex->current_select;
thd->lex->current_select= (*in_subq)->unit->first_select();
res= (*in_subq)->select_transformer(child_join);
bool res= (*in_subq)->select_transformer(child_join);
thd->lex->current_select= save_select_lex;
if (res == Item_subselect::RES_ERROR)
if (res)
DBUG_RETURN(TRUE);
(*in_subq)->changed= 1;
......@@ -1253,17 +1254,16 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
Calculate temporary table parameters and usage costs
*/
uint rowlen= get_tmp_table_rec_length(right_expr_list);
double lookup_cost;
if (rowlen * subjoin_out_rows< join->thd->variables.max_heap_table_size)
lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST;
else
lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
double lookup_cost= get_tmp_table_lookup_cost(join->thd,
subjoin_out_rows, rowlen);
double write_cost= get_tmp_table_write_cost(join->thd,
subjoin_out_rows, rowlen);
/*
Let materialization cost include the cost to write the data into the
temporary table:
*/
sjm->materialization_cost.add_io(subjoin_out_rows, lookup_cost);
sjm->materialization_cost.add_io(subjoin_out_rows, write_cost);
/*
Set the cost to do a full scan of the temptable (will need this to
......@@ -1338,6 +1338,49 @@ static uint get_tmp_table_rec_length(List<Item> &items)
return len;
}
/**
The cost of a lookup into a unique hash/btree index on a temporary table
with 'row_count' rows each of size 'row_size'.
@param thd current query context
@param row_count number of rows in the temp table
@param row_size average size in bytes of the rows
@return the cost of one lookup
*/
static double get_tmp_table_lookup_cost(THD *thd, ha_rows row_count, uint row_size)
{
if (row_count * row_size > thd->variables.max_heap_table_size)
return (double) DISK_TEMPTABLE_LOOKUP_COST;
else
return (double) HEAP_TEMPTABLE_LOOKUP_COST;
}
/**
The cost of writing a row into a temporary table with 'row_count' unique
rows each of size 'row_size'.
@param thd current query context
@param row_count number of rows in the temp table
@param row_size average size in bytes of the rows
@return the cost of writing one row
*/
static double get_tmp_table_write_cost(THD *thd, ha_rows row_count, uint row_size)
{
double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size);
/*
TODO:
This is an optimistic estimate. Add additional costs resulting from
actually writing the row to memory/disk and possible index reorganization.
*/
return lookup_cost;
}
//psergey-todo: is the below a kind of table elimination??
/*
Check if table's KEYUSE elements have an eq_ref(outer_tables) candidate
......@@ -1867,15 +1910,15 @@ void advance_sj_state(JOIN *join, table_map remaining_tables,
- sj_inner_fanout*sj_outer_fanout lookups.
*/
double one_lookup_cost;
if (sj_outer_fanout*temptable_rec_size >
join->thd->variables.max_heap_table_size)
one_lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
else
one_lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST;
double one_lookup_cost= get_tmp_table_lookup_cost(join->thd,
sj_outer_fanout,
temptable_rec_size);
double one_write_cost= get_tmp_table_write_cost(join->thd,
sj_outer_fanout,
temptable_rec_size);
double write_cost= join->positions[first_tab].prefix_record_count*
sj_outer_fanout * one_lookup_cost;
sj_outer_fanout * one_write_cost;
double full_lookup_cost= join->positions[first_tab].prefix_record_count*
sj_outer_fanout* sj_inner_fanout *
one_lookup_cost;
......@@ -3611,12 +3654,7 @@ bool JOIN::optimize_unflattened_subqueries()
bool JOIN::choose_subquery_plan(table_map join_tables)
{ /* The original QEP of the subquery. */
DYNAMIC_ARRAY save_keyuse; /* Copy of the JOIN::keyuse array. */
POSITION save_best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */
/* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */
KEYUSE *save_join_tab_keyuse[MAX_TABLES];
/* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */
key_map save_join_tab_checked_keys[MAX_TABLES];
Query_plan_state save_qep;
enum_reopt_result reopt_result= REOPT_NONE;
Item_in_subselect *in_subs;
......@@ -3635,9 +3673,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed);
DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed);
save_keyuse.elements= 0;
save_keyuse.buffer= NULL;
/*
Compute and compare the costs of materialization and in-exists if both
strategies are possible and allowed by the user (checked during the prepare
......@@ -3660,8 +3695,19 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
double in_exists_strategy_cost;
if (outer_join)
outer_join->get_partial_join_cost(outer_join->tables,
{
/*
Make_cond_for_table is called for predicates only in the WHERE/ON
clauses. In all other cases, predicates are not pushed to any
JOIN_TAB, and their joi_tab_idx remains MAX_TABLES. Such predicates
are evaluated for each complete row.
*/
uint partial_plan_len= (in_subs->get_join_tab_idx() == MAX_TABLES) ?
outer_join->tables :
in_subs->get_join_tab_idx() + 1;
outer_join->get_partial_join_cost(partial_plan_len,
&outer_read_time, &outer_record_count);
}
else
{
/*
......@@ -3673,7 +3719,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
}
inner_join->get_partial_join_cost(inner_join->tables,
&inner_read_time_1, &inner_record_count_1);
&inner_read_time_1,
&inner_record_count_1);
/* inner_read_time_1 above is a dummy, get the correct total join cost. */
inner_read_time_1= inner_join->best_read;
if (in_to_exists_where && const_tables != tables)
{
......@@ -3681,18 +3730,16 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Re-optimize and cost the subquery taking into account the IN-EXISTS
conditions.
*/
if (save_query_plan(&save_keyuse, save_best_positions,
save_join_tab_keyuse, save_join_tab_checked_keys))
return TRUE;
reopt_result= reoptimize(in_to_exists_where, join_tables);
if (reopt_result == REOPT_OLD_PLAN)
restore_query_plan(&save_keyuse, save_best_positions,
save_join_tab_keyuse, save_join_tab_checked_keys);
else if (reopt_result == REOPT_ERROR)
reopt_result= reoptimize(in_to_exists_where, join_tables, &save_qep);
if (reopt_result == REOPT_ERROR)
return TRUE;
inner_join->get_partial_join_cost(inner_join->tables,
&inner_read_time_2, &inner_record_count_2);
&inner_read_time_2,
&inner_record_count_2);
/* inner_read_time_2 above is a dummy, get the correct total join cost. */
inner_read_time_2= inner_join->best_read;
}
else
{
......@@ -3705,24 +3752,20 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
/*
1. Compute the cost of the materialization strategy.
*/
double materialization_cost; /* The cost of executing the subquery and */
/* storing its result in an indexed temp table.*/
uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list);
/* The cost of writing one row into the temporary table. */
double write_cost= get_tmp_table_write_cost(thd, inner_record_count_1,
rowlen);
/* The cost of a lookup into the unique index of the materialized table. */
double lookup_cost;
double write_row_cost= 1; /* TODO: what is the real cost to write a row? */
materialization_cost= inner_read_time_1 +
inner_record_count_1 * write_row_cost;
double lookup_cost= get_tmp_table_lookup_cost(thd, inner_record_count_1,
rowlen);
/*
The cost of a hash/btree lookup into a unique index of a materialized
subquery.
TIMOUR: TODO: the block of code below is exact copy/paste from
opt_subselect.cc:optimize_semi_join_nests() - refactor it.
The cost of executing the subquery and storing its result in an indexed
temporary table.
*/
uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list);
if (rowlen * inner_record_count_1 < thd->variables.max_heap_table_size)
lookup_cost= HEAP_TEMPTABLE_LOOKUP_COST;
else
lookup_cost= DISK_TEMPTABLE_LOOKUP_COST;
double materialization_cost= inner_read_time_1 +
write_cost * inner_record_count_1;
materialize_strategy_cost= materialization_cost +
outer_record_count * lookup_cost;
......@@ -3764,8 +3807,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
{
/* Restore the original query plan used for materialization. */
if (reopt_result == REOPT_NEW_PLAN)
restore_query_plan(&save_keyuse, save_best_positions,
save_join_tab_keyuse, save_join_tab_checked_keys);
restore_query_plan(&save_qep);
/* TODO: should we set/unset this flag for both select_lex and its unit? */
in_subs->unit->uncacheable&= ~UNCACHEABLE_DEPENDENT;
......@@ -3789,11 +3831,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
}
else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS)
{
/* Keep the new query plan with injected conditions, delete the old plan. */
if (reopt_result == REOPT_NEW_PLAN)
delete_dynamic(&save_keyuse);
if (reopt_result == REOPT_NONE && in_to_exists_where && const_tables != tables)
if (reopt_result == REOPT_NONE && in_to_exists_where &&
const_tables != tables)
{
/*
The subquery was not reoptimized either because the user allowed only the
......@@ -3805,7 +3844,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
join_tab[i].keyuse= NULL;
join_tab[i].checked_keys.clear_all();
}
if ((reopt_result= reoptimize(in_to_exists_where, join_tables)) ==
if ((reopt_result= reoptimize(in_to_exists_where, join_tables, NULL)) ==
REOPT_ERROR)
return TRUE;
}
......
......@@ -179,10 +179,12 @@ 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,
table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond);
static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables,
table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond);
static Item* part_of_refkey(TABLE *form,Field *field);
......@@ -919,7 +921,7 @@ JOIN::optimize()
if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
{
COND *table_independent_conds=
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE);
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES, FALSE);
DBUG_EXECUTE("where",
print_where(table_independent_conds,
"where after opt_sum_query()",
......@@ -2235,7 +2237,8 @@ JOIN::exec()
Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
used_tables,
(table_map)0, FALSE);
(table_map)0,
MAX_TABLES, FALSE);
if (sort_table_cond)
{
if (!curr_table->select)
......@@ -2266,7 +2269,8 @@ JOIN::exec()
QT_ORDINARY););
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
~ (table_map) 0,
~used_tables, FALSE);
~used_tables,
MAX_TABLES, FALSE);
DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
"having after sort",
QT_ORDINARY););
......@@ -5483,6 +5487,8 @@ void JOIN::get_partial_join_cost(uint n_tables,
double record_count= 1;
double read_time= 0.0;
DBUG_ASSERT(n_tables <= tables && n_tables > 0);
for (uint i= const_tables; i < n_tables; i++)
{
if (best_positions[i].records_read)
......@@ -6674,7 +6680,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->exec_const_cond=
make_cond_for_table(cond,
join->const_table_map,
(table_map) 0, FALSE);
(table_map) 0, MAX_TABLES, FALSE);
DBUG_EXECUTE("where",print_where(join->exec_const_cond, "constants",
QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables;
......@@ -6685,7 +6691,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *cond_tab= tab->first_inner;
COND *tmp= make_cond_for_table(*tab->on_expr_ref,
join->const_table_map,
(table_map) 0, FALSE);
(table_map) 0, MAX_TABLES, FALSE);
if (!tmp)
continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
......@@ -6776,7 +6782,7 @@ 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);
tmp= make_cond_for_table(cond, used_tables, current_map, i, FALSE);
if (cond && !tmp && tab->quick)
{ // Outer join
if (tab->type != JT_ALL)
......@@ -6834,7 +6840,7 @@ 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);
make_cond_for_table(tmp, current_map, current_map, MAX_TABLES, FALSE);
if (push_cond)
{
/* Push condition to handler */
......@@ -6965,7 +6971,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(tmp=make_cond_for_table(cond,
join->const_table_map |
current_map,
current_map, FALSE)))
current_map, MAX_TABLES, FALSE)))
{
DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
tab->cache_select=(SQL_SELECT*)
......@@ -6995,7 +7001,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *cond_tab= join_tab->first_inner;
COND *tmp= make_cond_for_table(*join_tab->on_expr_ref,
join->const_table_map,
(table_map) 0, FALSE);
(table_map) 0, MAX_TABLES, FALSE);
if (!tmp)
continue;
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
......@@ -7014,6 +7020,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)
{
......@@ -7025,12 +7032,13 @@ 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);
current_map,
(tab - first_tab), FALSE);
if (tmp_cond)
{
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
......@@ -14682,6 +14690,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
DESCRIPTION
......@@ -14708,15 +14718,18 @@ 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,
uint join_tab_idx_arg,
bool exclude_expensive_cond __attribute__((unused)))
{
return make_cond_for_table_from_pred(cond, cond, tables, used_table,
join_tab_idx_arg,
exclude_expensive_cond);
}
static Item *
make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables, table_map used_table,
uint join_tab_idx_arg,
bool exclude_expensive_cond __attribute__((unused)))
{
if (cond->type() == Item::COND_ITEM)
......@@ -14733,6 +14746,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{
Item *fix=make_cond_for_table_from_pred(root_cond, item,
tables, used_table,
join_tab_idx_arg,
exclude_expensive_cond);
if (fix)
new_cond->argument_list()->push_back(fix);
......@@ -14765,6 +14779,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{
Item *fix=make_cond_for_table_from_pred(root_cond, item,
tables, 0L,
join_tab_idx_arg,
exclude_expensive_cond);
if (!fix)
return (COND*) 0; // Always true
......@@ -14789,7 +14804,10 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
if (cond->marker == 3 || (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)
......@@ -14810,6 +14828,7 @@ 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;
}
......@@ -15986,7 +16005,7 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables,
FALSE);
MAX_TABLES, FALSE);
if (sort_table_cond)
{
if (!table->select)
......@@ -16004,7 +16023,8 @@ 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);
*having= make_cond_for_table(*having,~ (table_map) 0,~used_tables,
MAX_TABLES, FALSE);
DBUG_EXECUTE("where",
print_where(*having,"having after make_cond", QT_ORDINARY););
}
......@@ -19193,7 +19213,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{
/* TODO: thd may not be set for sub queries, but this should be fixed */
DBUG_ASSERT(thd);
// DBUG_ASSERT(thd);
if (!thd)
thd= current_thd;
......@@ -19359,78 +19379,59 @@ bool JOIN::change_result(select_result *res)
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_keyuse[out] a KEYUSE array to save JOIN::keyuse
@param save_best_positions[out] array to save JOIN::best_positions
@param save_join_tab_keyuse[out] array of KEYUSE pointers to save each
JOIN_TAB::keyuse pointer
@param save_join_tab_checked_keys[out] an array of bitmaps to save
each JOIN_TAB::checked_keys
@retval 0 OK
@retval 1 memory allocation error
@param save_to The object into which the current query plan state is saved
*/
int JOIN::save_query_plan(DYNAMIC_ARRAY *save_keyuse,
POSITION *save_best_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys)
void JOIN::save_query_plan(Query_plan_state *save_to)
{
if (keyuse.elements)
{
DYNAMIC_ARRAY tmp_keyuse;
if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64))
return 1;
// TODO: isn't this allocated by update_ref_and_keys
//if (my_init_dynamic_array(save_keyuse, sizeof(KEYUSE), 20, 64))
// return 1;
/* Swap the current and the backup keyuse arrays. */
tmp_keyuse= keyuse;
keyuse= (*save_keyuse);
(*save_keyuse)= tmp_keyuse;
keyuse= save_to->keyuse;
save_to->keyuse= tmp_keyuse;
for (uint i= 0; i < tables; i++)
{
save_join_tab_keyuse[i]= join_tab[i].keyuse;
save_to->join_tab_keyuse[i]= join_tab[i].keyuse;
join_tab[i].keyuse= NULL;
save_join_tab_checked_keys[i]= join_tab[i].checked_keys;
save_to->join_tab_checked_keys[i]= join_tab[i].checked_keys;
join_tab[i].checked_keys.clear_all();
}
}
memcpy((uchar*) save_best_positions, (uchar*) best_positions,
memcpy((uchar*) save_to->best_positions, (uchar*) best_positions,
sizeof(POSITION) * (tables + 1));
memset(best_positions, 0, sizeof(POSITION) * (tables + 1));
return 0;
}
/**
Restore a query plan previously saved by the caller.
@param save_keyuse a KEYUSE array to restore into JOIN::keyuse
@param save_best_positions array to restore into JOIN::best_positions
@param save_join_tab_keyuse array of KEYUSE pointers to restore each
JOIN_TAB::keyuse pointer
@param save_join_tab_checked_keys an array of bitmaps to restore
each JOIN_TAB::checked_keys
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(DYNAMIC_ARRAY *save_keyuse,
POSITION *save_best_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys)
void JOIN::restore_query_plan(Query_plan_state *restore_from)
{
if (save_keyuse->elements)
if (restore_from->keyuse.elements)
{
DYNAMIC_ARRAY tmp_keyuse;
tmp_keyuse= keyuse;
keyuse= (*save_keyuse);
(*save_keyuse)= tmp_keyuse;
delete_dynamic(save_keyuse);
keyuse= restore_from->keyuse;
restore_from->keyuse= tmp_keyuse;
for (uint i= 0; i < tables; i++)
{
join_tab[i].keyuse= save_join_tab_keyuse[i];
join_tab[i].checked_keys= save_join_tab_checked_keys[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*) save_best_positions,
memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions,
sizeof(POSITION) * (tables + 1));
}
......@@ -19441,8 +19442,7 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
@param added_where An extra conjunct to the WHERE clause to reoptimize with
@param join_tables The set of tables to reoptimize
@param save_best_positions The join order of the original plan to restore to
if needed.
@param save_to If != NULL, save here the state of the current query plan
@notes
Given a query plan that already optimized taking into account some WHERE clause
......@@ -19462,7 +19462,9 @@ void JOIN::restore_query_plan(DYNAMIC_ARRAY *save_keyuse,
@retval REOPT_ERROR an irrecovarable error occured during reoptimization.
*/
JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_tables)
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. */
......@@ -19478,6 +19480,9 @@ JOIN::enum_reopt_result JOIN::reoptimize(Item *added_where, table_map join_table
if (!added_keyuse.elements)
return REOPT_OLD_PLAN;
if (save_to)
save_query_plan(save_to);
/* Add the new access methods to the keyuse array. */
if (!keyuse.buffer &&
my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
......
......@@ -1374,6 +1374,31 @@ class JOIN :public Sql_alloc
JOIN& operator=(const JOIN &rhs); /**< not implemented */
protected:
/**
???
*/
class Query_plan_state {
public:
DYNAMIC_ARRAY keyuse; /* Copy of the JOIN::keyuse array. */
POSITION best_positions[MAX_TABLES+1]; /* Copy of JOIN::best_positions */
/* Copies of the JOIN_TAB::keyuse pointers for each JOIN_TAB. */
KEYUSE *join_tab_keyuse[MAX_TABLES];
/* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */
key_map join_tab_checked_keys[MAX_TABLES];
public:
Query_plan_state()
{
keyuse.elements= 0;
keyuse.buffer= NULL;
}
Query_plan_state(JOIN *join);
~Query_plan_state()
{
delete_dynamic(&keyuse);
}
};
/* Results of reoptimizing a JOIN via JOIN::reoptimize(). */
enum enum_reopt_result {
REOPT_NEW_PLAN, /* there is a new reoptimized plan */
......@@ -1383,13 +1408,10 @@ class JOIN :public Sql_alloc
};
/* Support for plan reoptimization with rewritten conditions. */
enum_reopt_result reoptimize(Item *added_where, table_map join_tables);
int save_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys);
void restore_query_plan(DYNAMIC_ARRAY *save_keyuse, POSITION *save_positions,
KEYUSE **save_join_tab_keyuse,
key_map *save_join_tab_checked_keys);
enum_reopt_result reoptimize(Item *added_where, table_map join_tables,
Query_plan_state *save_to);
void save_query_plan(Query_plan_state *save_to);
void restore_query_plan(Query_plan_state *restore_from);
/* Choose a subquery plan for a table-less subquery. */
bool choose_tableless_subquery_plan();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment