diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index 8eaad6d8034..afd1b14b4e2 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -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; diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index 472cadf04c4..7a6c4952091 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -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; diff --git a/mysql-test/t/subselect_sj_mat.test b/mysql-test/t/subselect_sj_mat.test index 4ddc19f49f5..fc3c7089db9 100644 --- a/mysql-test/t/subselect_sj_mat.test +++ b/mysql-test/t/subselect_sj_mat.test @@ -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; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 2baf530279f..f4dc7c6873a 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -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), diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 28ce0061729..ae096ac1a39 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -449,6 +449,7 @@ public: 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. diff --git a/sql/lock.cc b/sql/lock.cc index 2dc6bc357f4..9fdb30eb1a0 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -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); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f065de31ac5..178d0bac3d3 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1266,10 +1266,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) List_iterator_fast 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; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 117a866555c..50eed3b34dc 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -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); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0ef417c9567..1c484b13233 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -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 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 *join_list, Item **join_where) { @@ -817,29 +850,58 @@ inject_jtbm_conds(JOIN *join, 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? - setup_table_map(table->table, table, table->jtbm_table_no); + 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; - Item *sj_conds= hash_sj_engine->semi_join_conds; + TABLE *dummy_table= create_dummy_tmp_table(join->thd); + table->table= dummy_table; + table->table->pos_in_table_list= table; - (*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; + 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); + + //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); + + 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 &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 *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 *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 *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))) diff --git a/sql/table.cc b/sql/table.cc index 23c43f91d22..28c45a77396 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -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) { diff --git a/sql/table.h b/sql/table.h index 5dd464ac876..376aa9824dc 100644 --- a/sql/table.h +++ b/sql/table.h @@ -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: