Commit efb57a8e authored by Sergey Petrunya's avatar Sergey Petrunya

Merge

parents 7229af30 05e01274
......@@ -1765,6 +1765,7 @@ MIN(a)
1
DROP TABLE t1,t2,t3;
#
#
# BUG#902632: Crash or invalid read at st_join_table::cleanup, st_table::disable_keyread
#
CREATE TABLE t1 ( a INT );
......@@ -1780,6 +1781,20 @@ AND b IN ( SELECT c FROM t3 GROUP BY c )
);
a
DROP TABLE t1,t2,t3;
#
# BUG#901506: Crash in TABLE_LIST::print on EXPLAIN EXTENDED
#
CREATE TABLE t1 ( a INT, KEY(a) );
INSERT INTO t1 VALUES (8);
EXPLAIN EXTENDED
SELECT * FROM t1
WHERE a IN ( SELECT MIN(a) FROM t1 );
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 system NULL NULL NULL NULL 1 100.00
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
Note 1003 select 8 AS `a` from `test`.`t1` where <expr_cache><8>(<in_optimizer>(8,<exists>(select min(`test`.`t1`.`a`) from `test`.`t1` having (<cache>(8) = <ref_null_helper>(min(`test`.`t1`.`a`))))))
DROP TABLE t1;
# This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp;
set @subselect_mat_test_optimizer_switch_value=null;
......
......@@ -1801,6 +1801,7 @@ MIN(a)
1
DROP TABLE t1,t2,t3;
#
#
# BUG#902632: Crash or invalid read at st_join_table::cleanup, st_table::disable_keyread
#
CREATE TABLE t1 ( a INT );
......@@ -1816,5 +1817,20 @@ AND b IN ( SELECT c FROM t3 GROUP BY c )
);
a
DROP TABLE t1,t2,t3;
#
# BUG#901506: Crash in TABLE_LIST::print on EXPLAIN EXTENDED
#
CREATE TABLE t1 ( a INT, KEY(a) );
INSERT INTO t1 VALUES (8);
EXPLAIN EXTENDED
SELECT * FROM t1
WHERE a IN ( SELECT MIN(a) FROM t1 );
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 system a NULL NULL NULL 1 100.00
1 PRIMARY <subquery2> system NULL NULL NULL NULL 1 100.00
2 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
Note 1003 select 8 AS `a` from <materialize> (select min(`test`.`t1`.`a`) from `test`.`t1`) join `test`.`t1` where 1
DROP TABLE t1;
# This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp;
......@@ -1465,6 +1465,7 @@ SELECT MIN(a) FROM t1, t2 WHERE b IN (SELECT c FROM t3 GROUP BY c);
DROP TABLE t1,t2,t3;
--echo #
--echo #
--echo # BUG#902632: Crash or invalid read at st_join_table::cleanup, st_table::disable_keyread
--echo #
......@@ -1482,6 +1483,18 @@ SELECT * FROM t1 WHERE EXISTS (
);
DROP TABLE t1,t2,t3;
--echo #
--echo # BUG#901506: Crash in TABLE_LIST::print on EXPLAIN EXTENDED
--echo #
CREATE TABLE t1 ( a INT, KEY(a) );
INSERT INTO t1 VALUES (8);
EXPLAIN EXTENDED
SELECT * FROM t1
WHERE a IN ( SELECT MIN(a) FROM t1 );
DROP TABLE t1;
--echo # This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp;
......@@ -448,11 +448,44 @@ class Item_in_subselect :public Item_exists_subselect
Same as above, but they also allow to scan the materialized table.
*/
bool sjm_scan_allowed;
/*
JoinTaB Materialization (JTBM) members
*/
/*
TRUE <=> This subselect has been converted into non-mergeable semi-join
table.
*/
bool is_jtbm_merged;
/* (Applicable if is_jtbm_merged==TRUE) Time required to run the materialized join */
double jtbm_read_time;
/* (Applicable if is_jtbm_merged==TRUE) Number of output rows in materialized join */
double jtbm_record_count;
bool is_jtbm_merged;
/*
(Applicable if is_jtbm_merged==TRUE) TRUE <=> The materialized subselect is
a degenerate subselect which produces 0 or 1 rows, which we know at
optimization phase.
Examples:
1. subquery has "Impossible WHERE":
SELECT * FROM ot WHERE ot.column IN (SELECT it.col FROM it WHERE 2 > 3)
2. Subquery produces one row which opt_sum.cc is able to get with one lookup:
SELECT * FROM ot WHERE ot.column IN (SELECT MAX(it.key_col) FROM it)
*/
bool is_jtbm_const_tab;
/*
(Applicable if is_jtbm_const_tab==TRUE) Whether the subquery has produced
the row (or not)
*/
bool jtbm_const_row_found;
/*
TRUE<=>this is a flattenable semi-join, false overwise.
*/
......@@ -744,6 +777,9 @@ class subselect_single_select_engine: public subselect_engine
friend class subselect_hash_sj_engine;
friend class Item_in_subselect;
friend bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
Item **join_where);
};
......
......@@ -4623,6 +4623,234 @@ enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab)
}
/*
Create a dummy temporary table, useful only for the sake of having a
TABLE* object with map,tablenr and maybe_null properties.
This is used by non-mergeable semi-join materilization code to handle
degenerate cases where materialized subquery produced "Impossible WHERE"
and thus wasn't materialized.
*/
TABLE *create_dummy_tmp_table(THD *thd)
{
DBUG_ENTER("create_dummy_tmp_table");
TABLE *table;
TMP_TABLE_PARAM sjm_table_param;
sjm_table_param.init();
sjm_table_param.field_count= 1;
List<Item> sjm_table_cols;
Item *column_item= new Item_int(1);
sjm_table_cols.push_back(column_item);
if (!(table= create_tmp_table(thd, &sjm_table_param,
sjm_table_cols, (ORDER*) 0,
TRUE /* distinct */,
1, /*save_sum_fields*/
thd->options | TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
(char*)"dummy", TRUE /* Do not open */)))
{
DBUG_RETURN(NULL);
}
DBUG_RETURN(table);
}
/*
A class that is used to catch one single tuple that is sent to the join
output, and save it in Item_cache element(s).
It is very similar to select_singlerow_subselect but doesn't require a
Item_singlerow_subselect item.
*/
class select_value_catcher :public select_subselect
{
public:
select_value_catcher(Item_subselect *item_arg)
:select_subselect(item_arg)
{}
int send_data(List<Item> &items);
int setup(List<Item> *items);
bool assigned; /* TRUE <=> we've caught a value */
uint n_elements; /* How many elements we get */
Item_cache **row; /* Array of cache elements */
};
int select_value_catcher::setup(List<Item> *items)
{
assigned= FALSE;
n_elements= items->elements;
if (!(row= (Item_cache**) sql_alloc(sizeof(Item_cache*)*n_elements)))
return TRUE;
Item *sel_item;
List_iterator<Item> li(*items);
for (uint i= 0; (sel_item= li++); i++)
{
if (!(row[i]= Item_cache::get_cache(sel_item)))
return TRUE;
row[i]->setup(sel_item);
}
return FALSE;
}
int select_value_catcher::send_data(List<Item> &items)
{
DBUG_ENTER("select_value_catcher::send_data");
DBUG_ASSERT(!assigned);
DBUG_ASSERT(items.elements == n_elements);
if (unit->offset_limit_cnt)
{ // Using limit offset,count
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
Item *val_item;
List_iterator_fast<Item> li(items);
for (uint i= 0; (val_item= li++); i++)
{
row[i]->store(val_item);
row[i]->cache_value();
}
assigned= TRUE;
DBUG_RETURN(0);
}
/*
Setup JTBM join tabs for execution
*/
bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
Item **join_where)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
List_iterator<TABLE_LIST> li(*join_list);
DBUG_ENTER("setup_jtbm_semi_joins");
while ((table= li++))
{
Item_in_subselect *item;
if ((item= table->jtbm_subselect))
{
Item_in_subselect *subq_pred= item;
double rows;
double read_time;
/*
Perform optimization of the subquery, so that we know estmated
- cost of materialization process
- how many records will be in the materialized temp.table
*/
if (subq_pred->optimize(&rows, &read_time))
DBUG_RETURN(TRUE);
subq_pred->jtbm_read_time= read_time;
subq_pred->jtbm_record_count=rows;
JOIN *subq_join= subq_pred->unit->first_select()->join;
if (!subq_join->tables_list || !subq_join->table_count)
{
/*
A special case; subquery's join is degenerate, and it either produces
0 or 1 record. Examples of both cases:
select * from ot where col in (select ... from it where 2>3)
select * from ot where col in (select min(it.key) from it)
in this case, the subquery predicate has not been setup for
materialization. In particular, there is no materialized temp.table.
We'll now need to
1. Check whether 1 or 0 records are produced, setup this as a
constant join tab.
2. Create a dummy temporary table, because all of the join
optimization code relies on TABLE object being present (here we
follow a bad tradition started by derived tables)
*/
DBUG_ASSERT(subq_pred->engine->engine_type() ==
subselect_engine::SINGLE_SELECT_ENGINE);
subselect_single_select_engine *engine=
(subselect_single_select_engine*)subq_pred->engine;
select_value_catcher *new_sink;
if (!(new_sink= new select_value_catcher(subq_pred)))
DBUG_RETURN(TRUE);
if (new_sink->setup(&engine->select_lex->join->fields_list) ||
engine->select_lex->join->change_result(new_sink) ||
engine->exec())
{
DBUG_RETURN(TRUE);
}
subq_pred->is_jtbm_const_tab= TRUE;
if (new_sink->assigned)
{
subq_pred->jtbm_const_row_found= TRUE;
/*
Subselect produced one row, which is saved in new_sink->row.
Inject "left_expr[i] == row[i] equalities into parent's WHERE.
*/
Item *eq_cond;
for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
{
eq_cond= new Item_func_eq(subq_pred->left_expr->element_index(i),
new_sink->row[i]);
if (!eq_cond || eq_cond->fix_fields(join->thd, &eq_cond))
DBUG_RETURN(1);
(*join_where)= and_items(*join_where, eq_cond);
}
}
else
{
/* Subselect produced no rows. Just set the flag, */
subq_pred->jtbm_const_row_found= FALSE;
}
/* Set up a dummy TABLE*, optimizer code needs JOIN_TABs to have TABLE */
TABLE *dummy_table;
if (!(dummy_table= create_dummy_tmp_table(join->thd)))
DBUG_RETURN(1);
table->table= dummy_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
}
else
{
DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
subq_pred->is_jtbm_const_tab= FALSE;
subselect_hash_sj_engine *hash_sj_engine=
((subselect_hash_sj_engine*)item->engine);
table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
Item *sj_conds= hash_sj_engine->semi_join_conds;
(*join_where)= and_items(*join_where, sj_conds);
if (!(*join_where)->fixed)
(*join_where)->fix_fields(join->thd, join_where);
}
}
if ((nested_join= table->nested_join))
{
if (setup_jtbm_semi_joins(join, &nested_join->join_list, join_where))
DBUG_RETURN(TRUE);
}
}
DBUG_RETURN(FALSE);
}
/**
Choose an optimal strategy to execute an IN/ALL/ANY subquery predicate
based on cost.
......@@ -4932,7 +5160,15 @@ bool JOIN::choose_tableless_subquery_plan()
*/
}
if (subs_predicate->is_in_predicate())
/*
For IN subqueries, use IN->EXISTS transfomation, unless the subquery
has been converted to a JTBM semi-join. In that case, just leave
everything as-is, setup_jtbm_semi_joins() has special handling for cases
like this.
*/
if (subs_predicate->is_in_predicate() &&
!(subs_predicate->substype() == Item_subselect::IN_SUBS &&
((Item_in_subselect*)subs_predicate)->is_jtbm_merged))
{
Item_in_subselect *in_subs;
in_subs= (Item_in_subselect*) subs_predicate;
......
......@@ -10,6 +10,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join);
bool convert_join_subqueries_to_semijoins(JOIN *join);
int pull_out_semijoin_tables(JOIN *join);
bool optimize_semijoin_nests(JOIN *join, table_map all_table_map);
bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
Item **join_where);
// used by Loose_scan_opt
ulonglong get_bound_sj_equalities(TABLE_LIST *sj_nest,
......
......@@ -798,117 +798,6 @@ bool JOIN::prepare_stage2()
}
/*
Create a dummy temporary table, useful only for the sake of having a
TABLE* object with map,tablenr and maybe_null properties.
This is used by non-mergeable semi-join materilization code to handle
degenerate cases where materialized subquery produced "Impossible WHERE"
and thus wasn't materialized.
*/
TABLE *create_dummy_tmp_table(THD *thd)
{
DBUG_ENTER("create_dummy_tmp_table");
TABLE *table;
TMP_TABLE_PARAM sjm_table_param;
sjm_table_param.init();
sjm_table_param.field_count= 1;
List<Item> sjm_table_cols;
Item *column_item= new Item_int(1);
sjm_table_cols.push_back(column_item);
if (!(table= create_tmp_table(thd, &sjm_table_param,
sjm_table_cols, (ORDER*) 0,
TRUE /* distinct */,
1, /*save_sum_fields*/
thd->options | TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
(char*)"dummy", TRUE /* Do not open */)))
{
DBUG_RETURN(NULL);
}
DBUG_RETURN(table);
}
void
setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
{
TABLE_LIST *table;
NESTED_JOIN *nested_join;
List_iterator<TABLE_LIST> li(*join_list);
DBUG_ENTER("setup_jtbm_semi_joins");
while ((table= li++))
{
Item_in_subselect *item;
if ((item= table->jtbm_subselect))
{
Item_in_subselect *subq_pred= item;
double rows;
double read_time;
subq_pred->optimize(&rows, &read_time);
subq_pred->jtbm_read_time= read_time;
subq_pred->jtbm_record_count=rows;
subq_pred->is_jtbm_merged= TRUE;
JOIN *subq_join= subq_pred->unit->first_select()->join;
if (!subq_join->tables_list || !subq_join->table_count)
{
/*
This is an empty and constant table.
TODO: what if this is not empty but still constant?
We'll need to check the equality but there's no materializatnion
table?
A: create an IN-equality from
- left_expr
- right_expr. Q: how can right-expr exist in the context of
parent select? We don't have refs from outside to inside!
A: create/check in the context of the child select?
for injection, check how in->exists is performed.
*/
subq_pred->is_jtbm_const_tab= TRUE;
TABLE *dummy_table= create_dummy_tmp_table(join->thd);
table->table= dummy_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
}
else
{
DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
subq_pred->is_jtbm_const_tab= FALSE;
subselect_hash_sj_engine *hash_sj_engine=
((subselect_hash_sj_engine*)item->engine);
table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
Item *sj_conds= hash_sj_engine->semi_join_conds;
(*join_where)= and_items(*join_where, sj_conds);
if (!(*join_where)->fixed)
(*join_where)->fix_fields(join->thd, join_where);
}
}
if ((nested_join= table->nested_join))
{
setup_jtbm_semi_joins(join, &nested_join->join_list, join_where);
}
}
DBUG_VOID_RETURN;
}
/**
global select optimisation.
......@@ -1030,7 +919,8 @@ JOIN::optimize()
thd->restore_active_arena(arena, &backup);
}
setup_jtbm_semi_joins(this, join_list, &conds);
if (setup_jtbm_semi_joins(this, join_list, &conds))
DBUG_RETURN(1);
conds= optimize_cond(this, conds, join_list, &cond_value, &cond_equal);
......@@ -15658,7 +15548,12 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
tab->table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab)
{
/* Row will not be found */
DBUG_RETURN(-1);
int res;
if (tab->table->pos_in_table_list->jtbm_subselect->jtbm_const_row_found)
res= 0;
else
res= -1;
DBUG_RETURN(res);
}
else if (tab->type == JT_SYSTEM)
{
......@@ -21454,6 +21349,14 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
str->append(')');
}
else if (jtbm_subselect)
{
if (jtbm_subselect->is_jtbm_const_tab)
{
str->append(STRING_WITH_LEN(" <materialize> ("));
jtbm_subselect->engine->print(str, query_type);
str->append(')');
}
else
{
str->append(STRING_WITH_LEN(" <materialize> ("));
subselect_hash_sj_engine *hash_engine;
......@@ -21461,6 +21364,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
hash_engine->materialize_engine->print(str, query_type);
str->append(')');
}
}
else
{
const char *cmp_name; // Name to compare with alias
......
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