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
);
f1 f5
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:
set optimizer_switch=@subselect_sj_mat_tmp;
set @subselect_mat_test_optimizer_switch_value=null;
......
......@@ -1764,5 +1764,28 @@ FROM t4 , t5
);
f1 f5
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:
set optimizer_switch=@subselect_sj_mat_tmp;
......@@ -1427,7 +1427,30 @@ ON ( t2.f5 ) IN (
);
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:
set optimizer_switch=@subselect_sj_mat_tmp;
......
......@@ -616,7 +616,7 @@ int Item_in_subselect::optimize(double *out_rows, double *cost)
thd->lex->current_select= join->select_lex;
if ((res= join->optimize()))
DBUG_RETURN(res);
//psergey-todo: subq predicate can be made forced const!
/* Calculate #rows and cost of join execution */
join->get_partial_cost_and_fanout(join->table_count - join->const_tables,
table_map(-1),
......
......@@ -449,6 +449,7 @@ class Item_in_subselect :public Item_exists_subselect
double jtbm_read_time;
double jtbm_record_count;
bool is_jtbm_merged;
bool is_jtbm_const_tab;
/*
TRUE<=>this is a flattenable semi-join, false overwise.
......
......@@ -866,8 +866,10 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
for (i=tables=lock_count=0 ; i < count ; 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();
lock_count++;
......@@ -895,7 +897,9 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
TABLE *table;
enum thr_lock_type lock_type;
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;
lock_type= table->reginfo.lock_type;
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)
List_iterator_fast<TABLE_LIST> si(subq_lex->leaf_tables);
while ((tl= si++))
{
tl->table->tablenr= table_no;
tl->table->map= ((table_map)1) << table_no;
tl->set_tablenr(table_no);
if (tl->is_jtbm())
tl->jtbm_table_no= tl->table->tablenr;
tl->jtbm_table_no= table_no;
SELECT_LEX *old_sl= tl->select_lex;
tl->select_lex= parent_join->select_lex;
for (TABLE_LIST *emb= tl->embedding;
......@@ -1430,21 +1429,22 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
double rows;
double read_time;
DBUG_ENTER("convert_subq_to_jtbm");
bool optimization_delayed= TRUE;
subq_pred->set_strategy(SUBS_MATERIALIZATION);
if (subq_pred->optimize(&rows, &read_time))
DBUG_RETURN(TRUE);
// if (subq_pred->optimize(&rows, &read_time)) psergey-fix
// DBUG_RETURN(TRUE);
subq_pred->jtbm_read_time= read_time;
subq_pred->jtbm_record_count=rows;
subq_pred->is_jtbm_merged= TRUE;
/* psergey-fix
if (subq_pred->engine->engine_type() != subselect_engine::HASH_SJ_ENGINE)
{
*remove_item= FALSE;
DBUG_RETURN(FALSE);
}
*/
*remove_item= TRUE;
......@@ -1481,7 +1481,18 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
tl->next_local= jtbm;
/* 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*)subq_pred->engine);
jtbm->table= hash_sj_engine->tmp_table;
......
......@@ -3606,12 +3606,12 @@ bool st_select_lex::save_leaf_tables(THD *thd)
{
if (leaf_tables_exec.push_back(table))
return 1;
table->tablenr_exec= table->table->tablenr;
table->map_exec= table->table->map;
table->tablenr_exec= table->get_tablenr();
table->map_exec= table->get_map();
if (join && (join->select_options & SELECT_DESCRIBE))
table->maybe_null_exec= 0;
else
table->maybe_null_exec= table->table->maybe_null;
table->maybe_null_exec= table->table? table->table->maybe_null: 0;
}
if (arena)
thd->restore_active_arena(arena, &backup);
......
......@@ -798,6 +798,39 @@ 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
inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
{
......@@ -817,29 +850,58 @@ inject_jtbm_conds(JOIN *join, List<TABLE_LIST> *join_list, Item **join_where)
double rows;
double read_time;
//DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION));
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.
subselect_hash_sj_engine *hash_sj_engine=
((subselect_hash_sj_engine*)item->engine);
//repeat of convert_subq_to_jtbm:
table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= 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);
setup_table_map(table->table, table, table->jtbm_table_no);
Item *sj_conds= hash_sj_engine->semi_join_conds;
}
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);
//repeat of convert_subq_to_jtbm:
table->table= hash_sj_engine->tmp_table;
table->table->pos_in_table_list= table;
setup_table_map(table->table, table, table->jtbm_table_no);
(*join_where)= and_items(*join_where, sj_conds);
if (!(*join_where)->fixed)
(*join_where)->fix_fields(join->thd, join_where);
//parent_join->select_lex->where= parent_join->conds;
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);
//parent_join->select_lex->where= parent_join->conds;
}
}
if ((nested_join= table->nested_join))
......@@ -3120,6 +3182,14 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
set_position(join,const_count++,s,(KEYUSE*) 0);
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;
......@@ -9629,8 +9699,16 @@ void JOIN_TAB::cleanup()
if (table->pos_in_table_list &&
table->pos_in_table_list->jtbm_subselect)
{
end_read_record(&read_record);
table->pos_in_table_list->jtbm_subselect->cleanup();
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);
table->pos_in_table_list->jtbm_subselect->cleanup();
}
DBUG_VOID_RETURN;
}
/*
......@@ -11904,7 +11982,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
{
if (!table->prep_on_expr)
table->prep_on_expr= table->on_expr;
used_tables= table->table->map;
used_tables= table->get_map();
if (conds)
not_null_tables= conds->not_null_tables();
}
......@@ -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();
}
else
table->dep_tables&= ~table->table->map;
table->dep_tables&= ~table->get_map();
}
if (prev_table)
......@@ -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;
table_map prev_used_tables= prev_table->nested_join ?
prev_table->nested_join->used_tables :
prev_table->table->map;
prev_table->get_map();
/*
If on expression contains only references to inner 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)
/* Skip materialized derived tables/views. */
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)
{
if ((error=join_read_system(tab)))
......
......@@ -5922,6 +5922,8 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
int TABLE_LIST::fetch_number_of_rows()
{
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)
{
......
......@@ -1377,6 +1377,26 @@ struct TABLE_LIST
select_union *derived_result;
/* Stub used for materialized derived tables. */
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
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