diff --git a/mysql-test/main/index_intersect.result b/mysql-test/main/index_intersect.result index c6332591ff5d2..a6cf0dccd5023 100644 --- a/mysql-test/main/index_intersect.result +++ b/mysql-test/main/index_intersect.result @@ -973,3 +973,69 @@ f1 f4 f5 DROP TABLE t1; SET SESSION optimizer_switch='index_merge_sort_intersection=on'; SET SESSION optimizer_switch='rowid_filter=default'; +# +# MDEV-38327 wrong result with index_merge_sort_intersection and rowid_filter=on +# +CREATE TABLE t1 (c int, b int, a int , d int, PRIMARY KEY (c), KEY ib (b), KEY iad (a,d)); +INSERT INTO t1 +SELECT seq + 1000000, FLOOR(seq / 5) % 1350 + 1000000, FLOOR(seq / 5) % 1350 , seq % 10 FROM seq_1_to_7000 ; +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +set optimizer_switch='index_merge_sort_intersection=on'; +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +drop table t1; +## MDEV-28878 case +CREATE TABLE t1 (f int); +INSERT INTO t1 VALUES (0),(4); +CREATE TABLE t2 (pk int, a int, b varchar(10), PRIMARY KEY (pk), KEY a (a), KEY b (b)); +INSERT INTO t2 VALUES +(1,2,'v'),(2,3,'p'),(3,4,'p'),(4,2,'y'),(5,7,'q'), +(6,4,'a'),(7,1,'d'),(8,5,'a'),(9,5,'z'),(10,1,'t'), +(11,1,'y'),(12,5,'o'),(13,4,'a'),(14,5,'s'),(15,5,'m'); +ANALYZE TABLE t1, t2 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +SET optimizer_switch='rowid_filter=on'; +SET optimizer_switch='index_merge_sort_intersection=off'; +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; +f pk a b +4 3 4 p +SET optimizer_switch='index_merge_sort_intersection=on'; +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; +f pk a b +4 3 4 p +DROP TABLE t1, t2; diff --git a/mysql-test/main/index_intersect.test b/mysql-test/main/index_intersect.test index d6208f67a9210..60bcb135dcfce 100644 --- a/mysql-test/main/index_intersect.test +++ b/mysql-test/main/index_intersect.test @@ -476,3 +476,52 @@ DROP TABLE t1; SET SESSION optimizer_switch='index_merge_sort_intersection=on'; SET SESSION optimizer_switch='rowid_filter=default'; + +--echo # +--echo # MDEV-38327 wrong result with index_merge_sort_intersection and rowid_filter=on +--echo # +--source include/have_sequence.inc +CREATE TABLE t1 (c int, b int, a int , d int, PRIMARY KEY (c), KEY ib (b), KEY iad (a,d)); +INSERT INTO t1 + SELECT seq + 1000000, FLOOR(seq / 5) % 1350 + 1000000, FLOOR(seq / 5) % 1350 , seq % 10 FROM seq_1_to_7000 ; + +let $query= +select a, b, c from t1 where a=1000 and b=1001000; + +eval explain $query; +eval $query; + +set optimizer_switch='index_merge_sort_intersection=on'; + +eval explain $query; +eval $query; + +analyze table t1; + +eval explain $query; +eval $query; + +drop table t1; + +--echo ## MDEV-28878 case + +CREATE TABLE t1 (f int); +INSERT INTO t1 VALUES (0),(4); + +CREATE TABLE t2 (pk int, a int, b varchar(10), PRIMARY KEY (pk), KEY a (a), KEY b (b)); +INSERT INTO t2 VALUES + (1,2,'v'),(2,3,'p'),(3,4,'p'),(4,2,'y'),(5,7,'q'), + (6,4,'a'),(7,1,'d'),(8,5,'a'),(9,5,'z'),(10,1,'t'), + (11,1,'y'),(12,5,'o'),(13,4,'a'),(14,5,'s'),(15,5,'m'); + +ANALYZE TABLE t1, t2 PERSISTENT FOR ALL; + +SET optimizer_switch='rowid_filter=on'; # Default + +SET optimizer_switch='index_merge_sort_intersection=off'; # Default +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; + +SET optimizer_switch='index_merge_sort_intersection=on'; +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; + +DROP TABLE t1, t2; diff --git a/mysql-test/main/index_intersect_innodb.result b/mysql-test/main/index_intersect_innodb.result index 2aba9dc6fcfb2..727a79ad09a40 100644 --- a/mysql-test/main/index_intersect_innodb.result +++ b/mysql-test/main/index_intersect_innodb.result @@ -979,6 +979,72 @@ f1 f4 f5 DROP TABLE t1; SET SESSION optimizer_switch='index_merge_sort_intersection=on'; SET SESSION optimizer_switch='rowid_filter=default'; +# +# MDEV-38327 wrong result with index_merge_sort_intersection and rowid_filter=on +# +CREATE TABLE t1 (c int, b int, a int , d int, PRIMARY KEY (c), KEY ib (b), KEY iad (a,d)); +INSERT INTO t1 +SELECT seq + 1000000, FLOOR(seq / 5) % 1350 + 1000000, FLOOR(seq / 5) % 1350 , seq % 10 FROM seq_1_to_7000 ; +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +set optimizer_switch='index_merge_sort_intersection=on'; +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +explain select a, b, c from t1 where a=1000 and b=1001000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge ib,iad ib,iad 5,5 NULL 1 Using sort_intersect(ib,iad); Using where +select a, b, c from t1 where a=1000 and b=1001000; +a b c +1000 1001000 1005000 +1000 1001000 1005001 +1000 1001000 1005002 +1000 1001000 1005003 +1000 1001000 1005004 +drop table t1; +## MDEV-28878 case +CREATE TABLE t1 (f int); +INSERT INTO t1 VALUES (0),(4); +CREATE TABLE t2 (pk int, a int, b varchar(10), PRIMARY KEY (pk), KEY a (a), KEY b (b)); +INSERT INTO t2 VALUES +(1,2,'v'),(2,3,'p'),(3,4,'p'),(4,2,'y'),(5,7,'q'), +(6,4,'a'),(7,1,'d'),(8,5,'a'),(9,5,'z'),(10,1,'t'), +(11,1,'y'),(12,5,'o'),(13,4,'a'),(14,5,'s'),(15,5,'m'); +ANALYZE TABLE t1, t2 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +SET optimizer_switch='rowid_filter=on'; +SET optimizer_switch='index_merge_sort_intersection=off'; +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; +f pk a b +4 3 4 p +SET optimizer_switch='index_merge_sort_intersection=on'; +SELECT * FROM t1 JOIN t2 ON t1.f = t2.a WHERE t2.b >= 'j' AND t2.a != 5; +f pk a b +4 3 4 p +DROP TABLE t1, t2; set global innodb_stats_persistent= @innodb_stats_persistent_save; set global innodb_stats_persistent_sample_pages= @innodb_stats_persistent_sample_pages_save; diff --git a/sql/opt_range.h b/sql/opt_range.h index fae6bdc1afc5d..172c4c9208d6f 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -1576,6 +1576,7 @@ class QUICK_INDEX_SORT_SELECT : public QUICK_SELECT_I +/* Index merge sort union */ class QUICK_INDEX_MERGE_SELECT : public QUICK_INDEX_SORT_SELECT { private: @@ -1593,6 +1594,7 @@ class QUICK_INDEX_MERGE_SELECT : public QUICK_INDEX_SORT_SELECT void add_keys_and_lengths(String *key_names, String *used_lengths) override; }; +/* Index merge sort intersection */ class QUICK_INDEX_INTERSECT_SELECT : public QUICK_INDEX_SORT_SELECT { protected: @@ -1611,6 +1613,8 @@ class QUICK_INDEX_INTERSECT_SELECT : public QUICK_INDEX_SORT_SELECT /* + Index merge intersection + Rowid-Ordered Retrieval (ROR) index intersection quick select. This quick select produces intersection of row sequences returned by several QUICK_RANGE_SELECTs it "merges". @@ -1698,6 +1702,8 @@ class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I /* + Index merge union + Rowid-Ordered Retrieval index union select. This quick select produces union of row sequences returned by several quick select it "merges". diff --git a/sql/rowid_filter.h b/sql/rowid_filter.h index 3f18c2edb60c9..0b041e7aeabdb 100644 --- a/sql/rowid_filter.h +++ b/sql/rowid_filter.h @@ -129,7 +129,7 @@ 5. At the execution stage In the function sub_select() just before the first access of a join table s employing a range filter - The method JOIN_TAB::build_range_rowid_filter_if_needed() is called + The method JOIN_TAB::build_range_rowid_filter() is called The method fills the filter using the quick select created by JOIN::make_range_rowid_filters(). diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c6d6916e0a052..45d194cbee713 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -159,11 +159,7 @@ bool Update_plan::save_explain_data_intern(THD *thd, /* Set jtype */ if (select && select->quick) { - int quick_type= select->quick->get_type(); - if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) || - (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) + if (is_index_merge(select->quick->get_type())) explain->jtype= JT_INDEX_MERGE; else explain->jtype= JT_RANGE; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b986e59656e08..ada98c1ea4f68 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -14456,6 +14456,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } tab->type= JT_RANGE; tab->use_quick=1; + if (is_index_merge(tab->quick->get_type())) + tab->clear_range_rowid_filter(); tab->ref.key= -1; tab->ref.key_parts=0; // Don't use ref key. join->best_positions[i].records_read= rows2double(tab->quick->records); @@ -27404,17 +27406,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, } else if (select && select->quick) // Range found by opt_range { - int quick_type= select->quick->get_type(); - /* - assume results are not ordered when index merge is used - TODO: sergeyp: Results of all index merge selects actually are ordered + /* + assume results are not ordered when index merge is used + TODO: sergeyp: Results of all index merge selects actually are ordered by clustered PK values. */ - - if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) + + if (is_index_merge(select->quick->get_type())) { /* we set ref_key=MAX_KEY instead of -1, because test_if_cheaper_ordering() @@ -27654,10 +27652,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, goto skipped_filesort; quick_type= select->quick->get_type(); - if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || + if (is_index_merge(quick_type) || quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { tab->limit= 0; @@ -30850,10 +30845,7 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, { cur_quick= tab_select->quick; quick_type= cur_quick->get_type(); - if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) || - (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) || - (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)) + if (is_index_merge(quick_type)) tab_type= type == JT_HASH ? JT_HASH_INDEX_MERGE : JT_INDEX_MERGE; else tab_type= type == JT_HASH ? JT_HASH_RANGE : JT_RANGE; @@ -31055,10 +31047,7 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, eta->pushed_index_cond= cache_idx_cond; } - if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || - quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || - quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) + if (is_index_merge(quick_type)) { eta->push_extra(ET_USING); } diff --git a/sql/sql_select.h b/sql/sql_select.h index 82e975407edda..518675398ef6b 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2013,6 +2013,13 @@ void copy_fields(TMP_TABLE_PARAM *param); bool copy_funcs(Item **func_ptr, const THD *thd); uint find_shortest_key(TABLE *table, const key_map *usable_keys); bool is_indexed_agg_distinct(JOIN *join, List *out_args); +inline bool is_index_merge(int qtype) +{ + return qtype == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || + qtype == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || + qtype == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || + qtype == QUICK_SELECT_I::QS_TYPE_ROR_UNION; +} /* functions from opt_sum.cc */ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 98ed2b6d7a402..fcbc9e955599d 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4034,7 +4034,7 @@ row_search_idx_cond_check( byte* mysql_rec, /*!< out: record in MySQL format (invalid unless prebuilt->idx_cond!=NULL and - we return ICP_MATCH) */ + we return CHECK_POS) */ row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct for the table handle */ const rec_t* rec, /*!< in: InnoDB record */