Commit 8e25dcfc authored by Sergey Petrunya's avatar Sergey Petrunya

BUG#868908: Crash in check_simple_equality() with semijoin + materialization + prepared statement

- Part 1 of the fix: for semi-join merged subqueries, calling child_join->optimize() until we're done with all
  PS-lifetime optimizations in the parent.
parent 264aaf11
...@@ -1728,6 +1728,29 @@ FROM t4 , t5 ...@@ -1728,6 +1728,29 @@ FROM t4 , t5
); );
f1 f5 f1 f5
DROP TABLE t1, t2, t3, t4, t5; DROP TABLE t1, t2, t3, t4, t5;
#
# BUG#868908: Crash in check_simple_equality() with semijoin + materialization + prepared statement
#
CREATE TABLE t1 ( a int );
CREATE TABLE t3 ( b int, c int) ;
CREATE TABLE t2 ( a int ) ;
CREATE TABLE t4 ( a int , c int) ;
PREPARE st1 FROM "
SELECT STRAIGHT_JOIN *
FROM t1
WHERE ( 3 ) IN (
SELECT t3.b
FROM t3
LEFT JOIN (
t2 STRAIGHT_JOIN t4 ON ( t4.c = t2.a )
) ON ( t4.a = t3.c )
);
";
EXECUTE st1;
a
EXECUTE st1;
a
DROP TABLE t1,t2,t3,t4;
# This must be at the end: # This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp; set optimizer_switch=@subselect_sj_mat_tmp;
set @subselect_mat_test_optimizer_switch_value=null; set @subselect_mat_test_optimizer_switch_value=null;
......
...@@ -1764,5 +1764,28 @@ FROM t4 , t5 ...@@ -1764,5 +1764,28 @@ FROM t4 , t5
); );
f1 f5 f1 f5
DROP TABLE t1, t2, t3, t4, t5; DROP TABLE t1, t2, t3, t4, t5;
#
# BUG#868908: Crash in check_simple_equality() with semijoin + materialization + prepared statement
#
CREATE TABLE t1 ( a int );
CREATE TABLE t3 ( b int, c int) ;
CREATE TABLE t2 ( a int ) ;
CREATE TABLE t4 ( a int , c int) ;
PREPARE st1 FROM "
SELECT STRAIGHT_JOIN *
FROM t1
WHERE ( 3 ) IN (
SELECT t3.b
FROM t3
LEFT JOIN (
t2 STRAIGHT_JOIN t4 ON ( t4.c = t2.a )
) ON ( t4.a = t3.c )
);
";
EXECUTE st1;
a
EXECUTE st1;
a
DROP TABLE t1,t2,t3,t4;
# This must be at the end: # This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp; set optimizer_switch=@subselect_sj_mat_tmp;
...@@ -1427,7 +1427,30 @@ ON ( t2.f5 ) IN ( ...@@ -1427,7 +1427,30 @@ ON ( t2.f5 ) IN (
); );
DROP TABLE t1, t2, t3, t4, t5; DROP TABLE t1, t2, t3, t4, t5;
--echo #
--echo # BUG#868908: Crash in check_simple_equality() with semijoin + materialization + prepared statement
--echo #
CREATE TABLE t1 ( a int );
CREATE TABLE t3 ( b int, c int) ;
CREATE TABLE t2 ( a int ) ;
CREATE TABLE t4 ( a int , c int) ;
PREPARE st1 FROM "
SELECT STRAIGHT_JOIN *
FROM t1
WHERE ( 3 ) IN (
SELECT t3.b
FROM t3
LEFT JOIN (
t2 STRAIGHT_JOIN t4 ON ( t4.c = t2.a )
) ON ( t4.a = t3.c )
);
";
EXECUTE st1;
EXECUTE st1;
DROP TABLE t1,t2,t3,t4;
--echo # This must be at the end: --echo # This must be at the end:
set optimizer_switch=@subselect_sj_mat_tmp; set optimizer_switch=@subselect_sj_mat_tmp;
......
...@@ -616,7 +616,7 @@ int Item_in_subselect::optimize(double *out_rows, double *cost) ...@@ -616,7 +616,7 @@ int Item_in_subselect::optimize(double *out_rows, double *cost)
thd->lex->current_select= join->select_lex; thd->lex->current_select= join->select_lex;
if ((res= join->optimize())) if ((res= join->optimize()))
DBUG_RETURN(res); DBUG_RETURN(res);
//psergey-todo: subq predicate can be made forced const!
/* Calculate #rows and cost of join execution */ /* Calculate #rows and cost of join execution */
join->get_partial_cost_and_fanout(join->table_count - join->const_tables, join->get_partial_cost_and_fanout(join->table_count - join->const_tables,
table_map(-1), table_map(-1),
......
...@@ -449,6 +449,7 @@ public: ...@@ -449,6 +449,7 @@ public:
double jtbm_read_time; double jtbm_read_time;
double jtbm_record_count; double jtbm_record_count;
bool is_jtbm_merged; bool is_jtbm_merged;
bool is_jtbm_const_tab;
/* /*
TRUE<=>this is a flattenable semi-join, false overwise. TRUE<=>this is a flattenable semi-join, false overwise.
......
...@@ -867,7 +867,9 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, ...@@ -867,7 +867,9 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
{ {
TABLE *t= table_ptr[i]; TABLE *t= table_ptr[i];
if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE)
if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE &&
t->s->tmp_table != INTERNAL_TMP_TABLE)
{ {
tables+= t->file->lock_count(); tables+= t->file->lock_count();
lock_count++; lock_count++;
...@@ -895,7 +897,9 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, ...@@ -895,7 +897,9 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
TABLE *table; TABLE *table;
enum thr_lock_type lock_type; enum thr_lock_type lock_type;
THR_LOCK_DATA **locks_start; THR_LOCK_DATA **locks_start;
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) table= table_ptr[i];
if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE ||
table->s->tmp_table == INTERNAL_TMP_TABLE)
continue; continue;
lock_type= table->reginfo.lock_type; lock_type= table->reginfo.lock_type;
DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT);
......
...@@ -1266,10 +1266,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) ...@@ -1266,10 +1266,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
List_iterator_fast<TABLE_LIST> si(subq_lex->leaf_tables); List_iterator_fast<TABLE_LIST> si(subq_lex->leaf_tables);
while ((tl= si++)) while ((tl= si++))
{ {
tl->table->tablenr= table_no; tl->set_tablenr(table_no);
tl->table->map= ((table_map)1) << table_no;
if (tl->is_jtbm()) if (tl->is_jtbm())
tl->jtbm_table_no= tl->table->tablenr; tl->jtbm_table_no= table_no;
SELECT_LEX *old_sl= tl->select_lex; SELECT_LEX *old_sl= tl->select_lex;
tl->select_lex= parent_join->select_lex; tl->select_lex= parent_join->select_lex;
for (TABLE_LIST *emb= tl->embedding; for (TABLE_LIST *emb= tl->embedding;
...@@ -1430,21 +1429,22 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, ...@@ -1430,21 +1429,22 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
double rows; double rows;
double read_time; double read_time;
DBUG_ENTER("convert_subq_to_jtbm"); DBUG_ENTER("convert_subq_to_jtbm");
bool optimization_delayed= TRUE;
subq_pred->set_strategy(SUBS_MATERIALIZATION); subq_pred->set_strategy(SUBS_MATERIALIZATION);
if (subq_pred->optimize(&rows, &read_time)) // if (subq_pred->optimize(&rows, &read_time)) psergey-fix
DBUG_RETURN(TRUE); // DBUG_RETURN(TRUE);
subq_pred->jtbm_read_time= read_time; subq_pred->jtbm_read_time= read_time;
subq_pred->jtbm_record_count=rows; subq_pred->jtbm_record_count=rows;
subq_pred->is_jtbm_merged= TRUE; subq_pred->is_jtbm_merged= TRUE;
/* psergey-fix
if (subq_pred->engine->engine_type() != subselect_engine::HASH_SJ_ENGINE) if (subq_pred->engine->engine_type() != subselect_engine::HASH_SJ_ENGINE)
{ {
*remove_item= FALSE; *remove_item= FALSE;
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
*/
*remove_item= TRUE; *remove_item= TRUE;
...@@ -1481,7 +1481,18 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, ...@@ -1481,7 +1481,18 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
tl->next_local= jtbm; tl->next_local= jtbm;
/* A theory: no need to re-connect the next_global chain */ /* A theory: no need to re-connect the next_global chain */
if (optimization_delayed)
{
DBUG_ASSERT(parent_join->table_count < MAX_TABLES);
jtbm->jtbm_table_no= parent_join->table_count;
create_subquery_temptable_name(tbl_alias,
subq_pred->unit->first_select()->select_number);
jtbm->alias= tbl_alias;
parent_join->table_count++;
DBUG_RETURN(FALSE);
}
subselect_hash_sj_engine *hash_sj_engine= subselect_hash_sj_engine *hash_sj_engine=
((subselect_hash_sj_engine*)subq_pred->engine); ((subselect_hash_sj_engine*)subq_pred->engine);
jtbm->table= hash_sj_engine->tmp_table; jtbm->table= hash_sj_engine->tmp_table;
......
...@@ -3606,12 +3606,12 @@ bool st_select_lex::save_leaf_tables(THD *thd) ...@@ -3606,12 +3606,12 @@ bool st_select_lex::save_leaf_tables(THD *thd)
{ {
if (leaf_tables_exec.push_back(table)) if (leaf_tables_exec.push_back(table))
return 1; return 1;
table->tablenr_exec= table->table->tablenr; table->tablenr_exec= table->get_tablenr();
table->map_exec= table->table->map; table->map_exec= table->get_map();
if (join && (join->select_options & SELECT_DESCRIBE)) if (join && (join->select_options & SELECT_DESCRIBE))
table->maybe_null_exec= 0; table->maybe_null_exec= 0;
else else
table->maybe_null_exec= table->table->maybe_null; table->maybe_null_exec= table->table? table->table->maybe_null: 0;
} }
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
......
...@@ -798,6 +798,39 @@ err: ...@@ -798,6 +798,39 @@ err:
} }
/*
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 void
inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where) inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
{ {
...@@ -817,17 +850,45 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where) ...@@ -817,17 +850,45 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
double rows; double rows;
double read_time; double read_time;
//DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
subq_pred->optimize(&rows, &read_time); subq_pred->optimize(&rows, &read_time);
subq_pred->jtbm_read_time= read_time; subq_pred->jtbm_read_time= read_time;
subq_pred->jtbm_record_count=rows; subq_pred->jtbm_record_count=rows;
subq_pred->is_jtbm_merged= TRUE; 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 *hash_sj_engine=
((subselect_hash_sj_engine*)item->engine); ((subselect_hash_sj_engine*)item->engine);
//repeat of convert_subq_to_jtbm: //repeat of convert_subq_to_jtbm:
table->table= hash_sj_engine->tmp_table; table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= table; table->table->pos_in_table_list= table;
...@@ -841,6 +902,7 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where) ...@@ -841,6 +902,7 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
(*join_where)->fix_fields(join->thd, join_where); (*join_where)->fix_fields(join->thd, join_where);
//parent_join->select_lex->where= parent_join->conds; //parent_join->select_lex->where= parent_join->conds;
} }
}
if ((nested_join= table->nested_join)) if ((nested_join= table->nested_join))
{ {
...@@ -3120,6 +3182,14 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, ...@@ -3120,6 +3182,14 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
set_position(join,const_count++,s,(KEYUSE*) 0); set_position(join,const_count++,s,(KEYUSE*) 0);
no_rows_const_tables |= table->map; no_rows_const_tables |= table->map;
} }
/* SJ-Materialization handling: */
if (table->pos_in_table_list->jtbm_subselect &&
table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab)
{
set_position(join,const_count++,s,(KEYUSE*) 0);
no_rows_const_tables |= table->map;
}
} }
stat_vector[i]=0; stat_vector[i]=0;
...@@ -9628,9 +9698,17 @@ void JOIN_TAB::cleanup() ...@@ -9628,9 +9698,17 @@ void JOIN_TAB::cleanup()
preread_init_done= FALSE; preread_init_done= FALSE;
if (table->pos_in_table_list && if (table->pos_in_table_list &&
table->pos_in_table_list->jtbm_subselect) table->pos_in_table_list->jtbm_subselect)
{
if (table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab)
{
free_tmp_table(join->thd, table);
table= NULL;
}
else
{ {
end_read_record(&read_record); end_read_record(&read_record);
table->pos_in_table_list->jtbm_subselect->cleanup(); table->pos_in_table_list->jtbm_subselect->cleanup();
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/* /*
...@@ -11904,7 +11982,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, ...@@ -11904,7 +11982,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
{ {
if (!table->prep_on_expr) if (!table->prep_on_expr)
table->prep_on_expr= table->on_expr; table->prep_on_expr= table->on_expr;
used_tables= table->table->map; used_tables= table->get_map();
if (conds) if (conds)
not_null_tables= conds->not_null_tables(); not_null_tables= conds->not_null_tables();
} }
...@@ -11961,7 +12039,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, ...@@ -11961,7 +12039,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
table->embedding->on_expr_dep_tables|= table->on_expr->used_tables(); table->embedding->on_expr_dep_tables|= table->on_expr->used_tables();
} }
else else
table->dep_tables&= ~table->table->map; table->dep_tables&= ~table->get_map();
} }
if (prev_table) if (prev_table)
...@@ -11974,7 +12052,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, ...@@ -11974,7 +12052,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
prev_table->dep_tables|= table->on_expr_dep_tables; prev_table->dep_tables|= table->on_expr_dep_tables;
table_map prev_used_tables= prev_table->nested_join ? table_map prev_used_tables= prev_table->nested_join ?
prev_table->nested_join->used_tables : prev_table->nested_join->used_tables :
prev_table->table->map; prev_table->get_map();
/* /*
If on expression contains only references to inner tables If on expression contains only references to inner tables
we still make the inner tables dependent on the outer tables. we still make the inner tables dependent on the outer tables.
...@@ -15525,6 +15603,12 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) ...@@ -15525,6 +15603,12 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
/* Skip materialized derived tables/views. */ /* Skip materialized derived tables/views. */
DBUG_RETURN(0); DBUG_RETURN(0);
} }
else if (tab->table->pos_in_table_list->jtbm_subselect &&
tab->table->pos_in_table_list->jtbm_subselect->is_jtbm_const_tab)
{
/* Row will not be found */
DBUG_RETURN(-1);
}
else if (tab->type == JT_SYSTEM) else if (tab->type == JT_SYSTEM)
{ {
if ((error=join_read_system(tab))) if ((error=join_read_system(tab)))
......
...@@ -5922,6 +5922,8 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) ...@@ -5922,6 +5922,8 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
int TABLE_LIST::fetch_number_of_rows() int TABLE_LIST::fetch_number_of_rows()
{ {
int error= 0; int error= 0;
if (jtbm_subselect) /* psergey-todo: how did we work before? */
return 0; /*psergey-todo: check if we still need to set it? */
if (is_materialized_derived() && !fill_me) if (is_materialized_derived() && !fill_me)
{ {
......
...@@ -1377,6 +1377,26 @@ struct TABLE_LIST ...@@ -1377,6 +1377,26 @@ struct TABLE_LIST
select_union *derived_result; select_union *derived_result;
/* Stub used for materialized derived tables. */ /* Stub used for materialized derived tables. */
table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map map; /* ID bit of table (1,2,4,8,16...) */
table_map get_map()
{
return jtbm_subselect? table_map(1) << jtbm_table_no : table->map;
}
uint get_tablenr()
{
return jtbm_subselect? jtbm_table_no : table->tablenr;
}
void set_tablenr(uint new_tablenr)
{
if (jtbm_subselect)
{
jtbm_table_no= new_tablenr;
}
if (table)
{
table->tablenr= new_tablenr;
table->map= table_map(1) << new_tablenr;
}
}
/* /*
Reference from aux_tables to local list entry of main select of Reference from aux_tables to local list entry of main select of
multi-delete statement: multi-delete statement:
......
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