mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
@ -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;
|
||||
|
21
sql/item.h
21
sql/item.h
@ -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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
/* 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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ protected:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
|
||||
// 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 @@ skip_conversion:
|
||||
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.*/
|
||||
/* 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;
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
/* 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= get_tmp_table_lookup_cost(thd, inner_record_count_1,
|
||||
rowlen);
|
||||
/*
|
||||
The cost of executing the subquery and storing its result in an indexed
|
||||
temporary table.
|
||||
*/
|
||||
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.
|
||||
Restore a query execution 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
|
||||
@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 @@ private:
|
||||
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 @@ protected:
|
||||
};
|
||||
|
||||
/* 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();
|
||||
|
||||
|
Reference in New Issue
Block a user