diff --git a/mysql-test/r/subselect_partial_match.result b/mysql-test/r/subselect_partial_match.result index 9d604cc1507..744e56eb2a2 100644 --- a/mysql-test/r/subselect_partial_match.result +++ b/mysql-test/r/subselect_partial_match.result @@ -1,8 +1,8 @@ +set @save_optimizer_switch=@@optimizer_switch; drop table if exists t1, t2; # # LP BUG#608744 # -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch="materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=off"; create table t1 (a1 char(1), a2 char(1)); insert into t1 values (NULL, 'b'); @@ -11,7 +11,6 @@ insert into t2 values ('a','b'), ('c', 'd'); select * from t1 where (a1, a2) NOT IN (select b1, b2 from t2); a1 a2 drop table t1,t2; -set @@optimizer_switch=@save_optimizer_switch; # # LP BUG#601156 # @@ -21,7 +20,6 @@ INSERT INTO t1 VALUES (4,NULL); CREATE TABLE t2 (b1 int DEFAULT NULL, b2 int DEFAULT NULL); INSERT INTO t2 VALUES (6,NULL); INSERT INTO t2 VALUES (NULL,0); -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch='materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on'; EXPLAIN EXTENDED SELECT * FROM (SELECT * FROM t1 WHERE a1 NOT IN (SELECT b2 FROM t2)) table1; @@ -31,11 +29,9 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where (not((`test`.`t1`.`a1`,`test`.`t1`.`a1` in ( (select `test`.`t2`.`b2` from `test`.`t2` ), (`test`.`t1`.`a1` in on distinct_key where ((`test`.`t1`.`a1` = ``.`b2`))))))) DROP TABLE t1, t2; -set @@optimizer_switch=@save_optimizer_switch; # # LP BUG#613009 Crash in Ordered_key::get_field_idx # -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch='materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=off'; create table t1 (a1 char(3) DEFAULT NULL, a2 char(3) DEFAULT NULL); insert into t1 values (NULL, 'a21'), (NULL, 'a22'); @@ -46,7 +42,6 @@ id select_type table type possible_keys key key_len ref rows Extra select * from t1 where (a1, a2) not in (select a1, a2 from t1); a1 a2 drop table t1; -set @@optimizer_switch=@save_optimizer_switch; # # LP BUG#680058 void Ordered_key::add_key(rownum_t): # Assertion `key_buff_elements && cur_key_idx < key_buff_elements' failed @@ -55,10 +50,53 @@ create table t1 (f1 char(1), f2 char(1)); insert into t1 values ('t', '0'), ('0', 't'); create table t2 (f3 char(1), f4 char(1)); insert into t2 values ('t', NULL), ('t', NULL), ('d', 'y'); -set @save_optimizer_switch=@@optimizer_switch; -SET @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,semijoin=off'; +set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,semijoin=off'; select * from t1 where (f1, f2) not in (select * from t2); f1 f2 0 t -set @@optimizer_switch=@save_optimizer_switch; drop table t1, t2; +# +# LP BUG#809245 Second assertion `bit < (map)->n_bits' with partial_match_merge +# +CREATE TABLE t1 (d varchar(32)) ; +INSERT INTO t1 VALUES ('r'); +CREATE TABLE t2 ( a int, c varchar(32)) ; +INSERT INTO t2 VALUES (5,'r'); +CREATE TABLE t3 ( a int NOT NULL , d varchar(32)) ; +INSERT INTO t3 VALUES (10,'g'); +set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,in_to_exists=off'; +EXPLAIN SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( +SELECT t3.d , t2.c +FROM t3 LEFT JOIN t2 ON t3.a = t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 1 +2 SUBQUERY t3 system NULL NULL NULL NULL 1 +2 SUBQUERY t2 system NULL NULL NULL NULL 1 +SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( +SELECT t3.d , t2.c +FROM t3 LEFT JOIN t2 ON t3.a = t2.a); +d +r +set @@optimizer_switch='materialization=off,in_to_exists=on'; +EXPLAIN SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( +SELECT t3.d , t2.c +FROM t3 LEFT JOIN t2 ON t3.a = t2.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 1 +2 DEPENDENT SUBQUERY t3 system NULL NULL NULL NULL 1 +2 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 1 +SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( +SELECT t3.d , t2.c +FROM t3 LEFT JOIN t2 ON t3.a = t2.a); +d +r +drop table t1, t2, t3; +set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/subselect_partial_match.test b/mysql-test/t/subselect_partial_match.test index c5167f827ab..553596bf2ca 100644 --- a/mysql-test/t/subselect_partial_match.test +++ b/mysql-test/t/subselect_partial_match.test @@ -3,6 +3,8 @@ # MWL#68: Subquery optimization: Efficient NOT IN execution with NULLs # +set @save_optimizer_switch=@@optimizer_switch; + --disable_warnings drop table if exists t1, t2; --enable_warnings @@ -10,7 +12,6 @@ drop table if exists t1, t2; --echo # --echo # LP BUG#608744 --echo # -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch="materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=off"; create table t1 (a1 char(1), a2 char(1)); insert into t1 values (NULL, 'b'); @@ -18,7 +19,6 @@ create table t2 (b1 char(1), b2 char(2)); insert into t2 values ('a','b'), ('c', 'd'); select * from t1 where (a1, a2) NOT IN (select b1, b2 from t2); drop table t1,t2; -set @@optimizer_switch=@save_optimizer_switch; --echo # @@ -32,20 +32,17 @@ CREATE TABLE t2 (b1 int DEFAULT NULL, b2 int DEFAULT NULL); INSERT INTO t2 VALUES (6,NULL); INSERT INTO t2 VALUES (NULL,0); -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch='materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=on'; EXPLAIN EXTENDED SELECT * FROM (SELECT * FROM t1 WHERE a1 NOT IN (SELECT b2 FROM t2)) table1; DROP TABLE t1, t2; -set @@optimizer_switch=@save_optimizer_switch; --echo # --echo # LP BUG#613009 Crash in Ordered_key::get_field_idx --echo # -set @save_optimizer_switch=@@optimizer_switch; set @@optimizer_switch='materialization=on,semijoin=off,partial_match_rowid_merge=on,partial_match_table_scan=off'; create table t1 (a1 char(3) DEFAULT NULL, a2 char(3) DEFAULT NULL); @@ -53,7 +50,6 @@ insert into t1 values (NULL, 'a21'), (NULL, 'a22'); explain select * from t1 where (a1, a2) not in (select a1, a2 from t1); select * from t1 where (a1, a2) not in (select a1, a2 from t1); drop table t1; -set @@optimizer_switch=@save_optimizer_switch; --echo # --echo # LP BUG#680058 void Ordered_key::add_key(rownum_t): @@ -65,8 +61,52 @@ insert into t1 values ('t', '0'), ('0', 't'); create table t2 (f3 char(1), f4 char(1)); insert into t2 values ('t', NULL), ('t', NULL), ('d', 'y'); -set @save_optimizer_switch=@@optimizer_switch; -SET @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,semijoin=off'; +set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,semijoin=off'; select * from t1 where (f1, f2) not in (select * from t2); -set @@optimizer_switch=@save_optimizer_switch; drop table t1, t2; + + +--echo # +--echo # LP BUG#809245 Second assertion `bit < (map)->n_bits' with partial_match_merge +--echo # + +CREATE TABLE t1 (d varchar(32)) ; +INSERT INTO t1 VALUES ('r'); + +CREATE TABLE t2 ( a int, c varchar(32)) ; +INSERT INTO t2 VALUES (5,'r'); + +CREATE TABLE t3 ( a int NOT NULL , d varchar(32)) ; +INSERT INTO t3 VALUES (10,'g'); + +set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,in_to_exists=off'; + +EXPLAIN SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( + SELECT t3.d , t2.c + FROM t3 LEFT JOIN t2 ON t3.a = t2.a); + +SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( + SELECT t3.d , t2.c + FROM t3 LEFT JOIN t2 ON t3.a = t2.a); + +set @@optimizer_switch='materialization=off,in_to_exists=on'; + +EXPLAIN SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( + SELECT t3.d , t2.c + FROM t3 LEFT JOIN t2 ON t3.a = t2.a); + +SELECT * +FROM t1 +WHERE (t1.d , t1.d) NOT IN ( + SELECT t3.d , t2.c + FROM t3 LEFT JOIN t2 ON t3.a = t2.a); + +drop table t1, t2, t3; + +set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index f7c778dbdbf..3ce82bf6dac 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3864,8 +3864,7 @@ subselect_hash_sj_engine::get_strategy_using_data() bitmap_set_bit(&non_null_key_parts, i); --count_partial_match_columns; } - if (result_sink->get_null_count_of_col(i) == - tmp_table->file->stats.records) + if (result_sink->get_null_count_of_col(i) == tmp_table->file->stats.records) ++count_null_only_columns; } @@ -5172,7 +5171,6 @@ void subselect_partial_match_engine::print(String *str, /* @param non_null_key_parts @param partial_match_key_parts A union of all single-column NULL key parts. - @param count_partial_match_columns Number of NULL keyparts (set bits above). @retval FALSE the engine was initialized successfully @retval TRUE there was some (memory allocation) error during initialization, @@ -5189,24 +5187,24 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, rownum_t cur_rownum= 0; select_materialize_with_stats *result_sink= (select_materialize_with_stats *) result; - uint cur_keyid= 0; + uint cur_keyid; Item_in_subselect *item_in= (Item_in_subselect*) item; int error; - if (keys_count == 0) + if (merge_keys_count == 0) { /* There is nothing to initialize, we will only do regular lookups. */ return FALSE; } DBUG_ASSERT(!covering_null_row_width || (covering_null_row_width && - keys_count == 1 && + merge_keys_count == 1 && non_null_key_parts)); /* Allocate buffers to hold the merged keys and the mapping between rowids and row numbers. */ - if (!(merge_keys= (Ordered_key**) thd->alloc(keys_count * + if (!(merge_keys= (Ordered_key**) thd->alloc(merge_keys_count * sizeof(Ordered_key*))) || !(row_num_to_rowid= (uchar*) my_malloc((size_t)(row_count * rowid_length), MYF(MY_WME)))) @@ -5215,14 +5213,16 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, /* Create the only non-NULL key if there is any. */ if (non_null_key_parts) { - non_null_key= new Ordered_key(cur_keyid, tmp_table, item_in->left_expr, + non_null_key= new Ordered_key(0, tmp_table, item_in->left_expr, 0, 0, 0, row_num_to_rowid); if (non_null_key->init(non_null_key_parts)) return TRUE; - merge_keys[cur_keyid]= non_null_key; - merge_keys[cur_keyid]->first(); - ++cur_keyid; + merge_keys[0]= non_null_key; + merge_keys[0]->first(); + cur_keyid= 1; } + else + cur_keyid= 0; /* If there is a covering NULL row, the only key that is needed is the @@ -5231,9 +5231,8 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, */ if (!covering_null_row_width) { - if (bitmap_init_memroot(&matching_keys, keys_count, thd->mem_root) || - bitmap_init_memroot(&matching_outer_cols, keys_count, thd->mem_root) || - bitmap_init_memroot(&null_only_columns, keys_count, thd->mem_root)) + if (bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) || + bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root)) return TRUE; /* @@ -5242,31 +5241,25 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, */ for (uint i= 0; i < partial_match_key_parts->n_bits; i++) { - if (!bitmap_is_set(partial_match_key_parts, i)) + /* Skip columns that have no NULLs, or contain only NULLs. */ + if (!bitmap_is_set(partial_match_key_parts, i) || + result_sink->get_null_count_of_col(i) == row_count) continue; - if (result_sink->get_null_count_of_col(i) == row_count) - { - bitmap_set_bit(&null_only_columns, cur_keyid); - continue; - } - else - { - merge_keys[cur_keyid]= new Ordered_key( + merge_keys[cur_keyid]= new Ordered_key( cur_keyid, tmp_table, item_in->left_expr->element_index(i), result_sink->get_null_count_of_col(i), result_sink->get_min_null_of_col(i), result_sink->get_max_null_of_col(i), row_num_to_rowid); - if (merge_keys[cur_keyid]->init(i)) - return TRUE; - merge_keys[cur_keyid]->first(); - } + if (merge_keys[cur_keyid]->init(i)) + return TRUE; + merge_keys[cur_keyid]->first(); ++cur_keyid; } } - DBUG_ASSERT(cur_keyid == keys_count); + DBUG_ASSERT(cur_keyid == merge_keys_count); /* Populate the indexes with data from the temporary table. */ if (tmp_table->file->ha_rnd_init_with_error(1)) @@ -5307,7 +5300,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, non_null_key->add_key(cur_rownum); } - for (uint i= (non_null_key ? 1 : 0); i < keys_count; i++) + for (uint i= (non_null_key ? 1 : 0); i < merge_keys_count; i++) { /* Check if the first and only indexed column contains NULL in the curent @@ -5324,14 +5317,14 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, tmp_table->file->ha_rnd_end(); /* Sort all the keys by their NULL selectivity. */ - my_qsort(merge_keys, keys_count, sizeof(Ordered_key*), + my_qsort(merge_keys, merge_keys_count, sizeof(Ordered_key*), (qsort_cmp) cmp_keys_by_null_selectivity); /* Sort the keys in each of the indexes. */ - for (uint i= 0; i < keys_count; i++) + for (uint i= 0; i < merge_keys_count; i++) merge_keys[i]->sort_keys(); - if (init_queue(&pq, keys_count, 0, FALSE, + if (init_queue(&pq, merge_keys_count, 0, FALSE, subselect_rowid_merge_engine::cmp_keys_by_cur_rownum, NULL, 0, 0)) return TRUE; @@ -5343,10 +5336,10 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, subselect_rowid_merge_engine::~subselect_rowid_merge_engine() { /* None of the resources below is allocated if there are no ordered keys. */ - if (keys_count) + if (merge_keys_count) { my_free((char*) row_num_to_rowid, MYF(0)); - for (uint i= 0; i < keys_count; i++) + for (uint i= 0; i < merge_keys_count; i++) delete merge_keys[i]; delete_queue(&pq); if (tmp_table->file->inited == handler::RND) @@ -5404,6 +5397,10 @@ subselect_rowid_merge_engine::cmp_keys_by_cur_rownum(void *arg, Check if certain table row contains a NULL in all columns for which there is no match in the corresponding value index. + @note + There is no need to check the columns that contain only NULLs, because + those are guaranteed to match. + @retval TRUE if a NULL row exists @retval FALSE otherwise */ @@ -5411,16 +5408,14 @@ subselect_rowid_merge_engine::cmp_keys_by_cur_rownum(void *arg, bool subselect_rowid_merge_engine::test_null_row(rownum_t row_num) { Ordered_key *cur_key; - uint cur_id; - for (uint i = 0; i < keys_count; i++) + for (uint i = 0; i < merge_keys_count; i++) { cur_key= merge_keys[i]; - cur_id= cur_key->get_keyid(); - if (bitmap_is_set(&matching_keys, cur_id)) + if (bitmap_is_set(&matching_keys, cur_key->get_keyid())) { /* - The key 'i' (with id 'cur_keyid') already matches a value in row 'row_num', - thus we skip it as it can't possibly match a NULL. + The key 'i' (with id 'cur_keyid') already matches a value in row + 'row_num', thus we skip it as it can't possibly match a NULL. */ continue; } @@ -5481,7 +5476,7 @@ bool subselect_rowid_merge_engine::partial_match() Do not add the non_null_key, since it was already processed above. */ bitmap_clear_all(&matching_outer_cols); - for (uint i= test(non_null_key); i < keys_count; i++) + for (uint i= test(non_null_key); i < merge_keys_count; i++) { DBUG_ASSERT(merge_keys[i]->get_column_count() == 1); if (merge_keys[i]->get_search_key(0)->null_value) @@ -5519,7 +5514,6 @@ bool subselect_rowid_merge_engine::partial_match() min_key= (Ordered_key*) queue_remove_top(&pq); min_row_num= min_key->current(); - bitmap_copy(&matching_keys, &null_only_columns); bitmap_set_bit(&matching_keys, min_key->get_keyid()); bitmap_union(&matching_keys, &matching_outer_cols); if (min_key->next_same()) @@ -5555,7 +5549,6 @@ bool subselect_rowid_merge_engine::partial_match() { min_key= cur_key; min_row_num= cur_row_num; - bitmap_copy(&matching_keys, &null_only_columns); bitmap_set_bit(&matching_keys, min_key->get_keyid()); bitmap_union(&matching_keys, &matching_outer_cols); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 26a45a14dbe..42be1635b6f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -1191,11 +1191,6 @@ protected: outer reference. */ MY_BITMAP matching_outer_cols; - /* - Columns that consist of only NULLs. Such columns match any value. - Computed once per query execution. - */ - MY_BITMAP null_only_columns; /* Indexes of row numbers, sorted by . If an index may contain NULLs, the NULLs are stored efficiently in a bitmap. @@ -1205,13 +1200,13 @@ protected: non-NULL columns, it is contained in keys[0]. */ Ordered_key **merge_keys; - /* The number of elements in keys. */ - uint keys_count; + /* The number of elements in merge_keys. */ + uint merge_keys_count; /* An index on all non-NULL columns of 'tmp_table'. The index has the logical form: <[v_i1 | ... | v_ik], rownum>. It allows to find the row number where the columns c_i1,...,c1_k contain the values v_i1,...,v_ik. - If such an index exists, it is always the first element of 'keys'. + If such an index exists, it is always the first element of 'merge_keys'. */ Ordered_key *non_null_key; /* @@ -1236,7 +1231,7 @@ protected: public: subselect_rowid_merge_engine(THD *thd_arg, subselect_uniquesubquery_engine *engine_arg, - TABLE *tmp_table_arg, uint keys_count_arg, + TABLE *tmp_table_arg, uint merge_keys_count_arg, uint covering_null_row_width_arg, Item_subselect *item_arg, select_result_interceptor *result_arg, @@ -1244,7 +1239,7 @@ public: :subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg, result_arg, equi_join_conds_arg, covering_null_row_width_arg), - keys_count(keys_count_arg), non_null_key(NULL) + merge_keys_count(merge_keys_count_arg), non_null_key(NULL) {} ~subselect_rowid_merge_engine(); bool init(MY_BITMAP *non_null_key_parts, MY_BITMAP *partial_match_key_parts);