diff --git a/mysql-test/main/mdev-34413-icp-reverse-order.result b/mysql-test/main/mdev-34413-icp-reverse-order.result index 0b1d8e5c941..6b88bc14060 100644 --- a/mysql-test/main/mdev-34413-icp-reverse-order.result +++ b/mysql-test/main/mdev-34413-icp-reverse-order.result @@ -19,7 +19,7 @@ a b c 10 10 10 explain select * from t10 force index(a) where a between 10 and 20 and b+1 <3333 order by a desc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10 range a a 5 NULL 11 Using where +1 SIMPLE t10 range a a 5 NULL 11 Using index condition flush status; select * from t10 force index(a) where a between 10 and 20 and b+1 <3333 order by a desc, b desc; a b c @@ -36,8 +36,8 @@ a b c 10 10 10 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 11 +HANDLER_ICP_MATCH 11 select * from t10 force index(a) where a between 10 and 20 and b+1 <3333 order by a asc, b asc; a b c 10 10 10 @@ -77,15 +77,15 @@ a b c 10 10 10 explain select * from t10 force index(a) where a=10 and b+1 <3333 order by a desc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10 ref a a 5 const 1 Using where +1 SIMPLE t10 ref a a 5 const 1 Using index condition; Using where flush status; select * from t10 force index(a) where a=10 and b+1 <3333 order by a desc, b desc; a b c 10 10 10 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 1 +HANDLER_ICP_MATCH 1 select * from t10 force index(a) where a=10 and b+1 <3333 order by a asc, b asc; a b c 10 10 10 @@ -105,15 +105,15 @@ a b c 10 10 10 explain select * from t10 force index(a) where a=10 and b+1 <3333 order by a asc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t10 ref a a 5 const 1 Using where +1 SIMPLE t10 ref a a 5 const 1 Using index condition; Using where flush status; select * from t10 force index(a) where a=10 and b+1 <3333 order by a asc, b desc; a b c 10 10 10 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 1 +HANDLER_ICP_MATCH 1 select * from t10 force index(a) where a=10 and b+1 <3333 order by a desc, b asc; a b c 10 10 10 @@ -135,15 +135,15 @@ a b c 3 30 300 explain select * from t1 where a >= 3 and a <= 3 order by a desc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 5 NULL 1 Using where +1 SIMPLE t1 range a a 5 NULL 1 Using index condition flush status; select * from t1 where a >= 3 and a <= 3 order by a desc, b desc; a b c 3 30 300 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 1 +HANDLER_ICP_MATCH 1 select * from t1 where a >= 3 and a <= 3 order by a asc, b asc; a b c 3 30 300 @@ -166,15 +166,15 @@ a b c 2 20 200 explain select * from t1 where a >= 2 and a <= 2 order by a desc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 5 NULL 1 Using where +1 SIMPLE t1 range a a 5 NULL 1 Using index condition flush status; select * from t1 where a >= 2 and a <= 2 order by a desc, b desc; a b c 2 20 200 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 1 +HANDLER_ICP_MATCH 1 select * from t1 where a >= 2 and a <= 2 order by a asc, b asc; a b c 2 20 200 @@ -214,7 +214,7 @@ a b c 3 3 3 explain select * from t1 where a >= 3 and a <= 7 order by a desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 4 NULL 5 Using where +1 SIMPLE t1 range a a 4 NULL 5 Using index condition flush status; select * from t1 where a >= 3 and a <= 7 order by a desc; a b c @@ -225,8 +225,8 @@ a b c 3 3 3 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 6 +HANDLER_ICP_MATCH 6 select * from t1 where a >= 3 and a <= 7 order by a desc, b desc; a b c 7 7 7 @@ -236,7 +236,7 @@ a b c 3 3 3 explain select * from t1 where a >= 3 and a <= 7 order by a desc, b desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 4 NULL 5 Using where +1 SIMPLE t1 range a a 4 NULL 5 Using index condition flush status; select * from t1 where a >= 3 and a <= 7 order by a desc, b desc; a b c @@ -247,8 +247,8 @@ a b c 3 3 3 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 -HANDLER_ICP_MATCH 0 +HANDLER_ICP_ATTEMPTS 6 +HANDLER_ICP_MATCH 6 drop table t1; create table t1 ( pk int primary key, @@ -273,6 +273,6 @@ select * from t1 where kp1 between 950 and 960 and kp2+1 >33333 order by kp1 des pk kp1 kp2 col1 SELECT * FROM information_schema.SESSION_STATUS WHERE VARIABLE_NAME LIKE '%icp%'; VARIABLE_NAME VARIABLE_VALUE -HANDLER_ICP_ATTEMPTS 0 +HANDLER_ICP_ATTEMPTS 11 HANDLER_ICP_MATCH 0 drop table t1; diff --git a/mysql-test/main/mdev-34413-icp-reverse-order.test b/mysql-test/main/mdev-34413-icp-reverse-order.test index f72c9bdbac1..ad2c2bde879 100644 --- a/mysql-test/main/mdev-34413-icp-reverse-order.test +++ b/mysql-test/main/mdev-34413-icp-reverse-order.test @@ -1,4 +1,3 @@ ---source include/have_innodb.inc --source include/have_partition.inc --source include/have_sequence.inc diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index 537d30fb12e..0f1fed4c5ab 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -1779,7 +1779,7 @@ FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i DESC LIMIT 5; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 8 100.00 Using where +1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 8 100.00 Using index condition Warnings: Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`i` AS `i` from `test`.`t2` where `test`.`t2`.`i` > 10 and `test`.`t2`.`i` <= 18 order by `test`.`t2`.`i` desc limit 5 # Status of EXPLAIN EXTENDED "equivalent" SELECT query execution @@ -2291,7 +2291,7 @@ FLUSH STATUS; FLUSH TABLES; EXPLAIN EXTENDED SELECT * FROM t2 WHERE i > 10 AND i <= 18 ORDER BY i DESC LIMIT 5; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 8 100.00 Using where +1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 8 100.00 Using index condition Warnings: Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`i` AS `i` from `test`.`t2` where `test`.`t2`.`i` > 10 and `test`.`t2`.`i` <= 18 order by `test`.`t2`.`i` desc limit 5 # Status of EXPLAIN EXTENDED "equivalent" SELECT query execution diff --git a/mysql-test/main/myisam_icp.result b/mysql-test/main/myisam_icp.result index 97acd6d4a13..70f969cfdb5 100644 --- a/mysql-test/main/myisam_icp.result +++ b/mysql-test/main/myisam_icp.result @@ -165,7 +165,7 @@ WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00' ORDER BY ts DESC LIMIT 2; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using index condition DROP TABLE t1; # @@ -1016,12 +1016,12 @@ set optimizer_switch='mrr=off'; # Must not use ICP: explain select * from t1 where a between 5 and 8 order by a desc, col desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 5 NULL 40 Using where +1 SIMPLE t1 range a a 5 NULL 40 Using index condition set optimizer_switch= @tmp_10000051; # Must not use ICP: explain select * from t1 where a=3 and col > 500 order by a desc, col desc; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range a a 10 NULL 10 Using where +1 SIMPLE t1 range a a 10 NULL 10 Using index condition drop table t0, t1; # # MDEV-13628: ORed condition in pushed index condition is not removed from the WHERE diff --git a/mysql-test/main/order_by_optimizer_innodb.result b/mysql-test/main/order_by_optimizer_innodb.result index ad0bebdc7ef..5ceee39602e 100644 --- a/mysql-test/main/order_by_optimizer_innodb.result +++ b/mysql-test/main/order_by_optimizer_innodb.result @@ -42,11 +42,11 @@ pk1 count(*) # The following should use range(ux_pk1_fd5), two key parts (key_len=5+8=13) EXPLAIN SELECT * FROM t2 USE INDEX(ux_pk1_fd5) WHERE pk1=9 AND fd5 < 500 ORDER BY fd5 DESC LIMIT 10; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range ux_pk1_fd5 ux_pk1_fd5 13 NULL 138 Using where +1 SIMPLE t2 range ux_pk1_fd5 ux_pk1_fd5 13 NULL 138 Using index condition # This also must use range, not ref. key_len must be 13 EXPLAIN SELECT * FROM t2 WHERE pk1=9 AND fd5 < 500 ORDER BY fd5 DESC LIMIT 10; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range PRIMARY,ux_pk1_fd5 ux_pk1_fd5 13 NULL 138 Using where +1 SIMPLE t2 range PRIMARY,ux_pk1_fd5 ux_pk1_fd5 13 NULL 138 Using index condition drop table t2; # # MDEV-6814: Server crashes in calculate_key_len on query with ORDER BY diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result index 9202af1e686..ae148bf4662 100644 --- a/mysql-test/main/rowid_filter_innodb.result +++ b/mysql-test/main/rowid_filter_innodb.result @@ -3826,15 +3826,18 @@ ANALYZE "loops": 1, "r_loops": 1, "rows": 250, - "r_rows": 250, + "r_index_rows": 250, + "r_rows": 2, "cost": "REPLACED", "r_table_time_ms": "REPLACED", "r_other_time_ms": "REPLACED", "r_engine_stats": REPLACED, "filtered": 4.799086094, "r_total_filtered": 0.8, - "attached_condition": "t1.a <=> 30100 and t1.b in (30100,30101,30102) and 30100 + t1.pk > 38539", - "r_filtered": 0.8 + "index_condition": "30100 + t1.pk > 38539", + "r_icp_filtered": 0.8, + "attached_condition": "t1.a <=> 30100 and t1.b in (30100,30101,30102)", + "r_filtered": 100 } } ] @@ -3870,15 +3873,18 @@ ANALYZE "loops": 1, "r_loops": 1, "rows": 250, - "r_rows": 250, + "r_index_rows": 250, + "r_rows": 2, "cost": "REPLACED", "r_table_time_ms": "REPLACED", "r_other_time_ms": "REPLACED", "r_engine_stats": REPLACED, "filtered": 4.799086094, "r_total_filtered": 0.8, - "attached_condition": "t1.a <=> 30100 and t1.b in (30100,30101,30102) and 30100 + t1.pk > 38539", - "r_filtered": 0.8 + "index_condition": "30100 + t1.pk > 38539", + "r_icp_filtered": 0.8, + "attached_condition": "t1.a <=> 30100 and t1.b in (30100,30101,30102)", + "r_filtered": 100 } } ] diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result index 88c8a9c41c9..6c51b2cfb77 100644 --- a/mysql-test/main/subselect.result +++ b/mysql-test/main/subselect.result @@ -3197,7 +3197,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result index 38df3b6e308..3c6118264b9 100644 --- a/mysql-test/main/subselect_no_exists_to_in.result +++ b/mysql-test/main/subselect_no_exists_to_in.result @@ -3200,7 +3200,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result index fc3d2bfc70a..1a41aae4cf6 100644 --- a/mysql-test/main/subselect_no_mat.result +++ b/mysql-test/main/subselect_no_mat.result @@ -3202,7 +3202,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result index 7c35f4bade3..70184962780 100644 --- a/mysql-test/main/subselect_no_opts.result +++ b/mysql-test/main/subselect_no_opts.result @@ -3198,7 +3198,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result index 70e5a9b45f9..bf0f5373106 100644 --- a/mysql-test/main/subselect_no_scache.result +++ b/mysql-test/main/subselect_no_scache.result @@ -3203,7 +3203,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result index cbdcf7c82e9..70d4f1969dc 100644 --- a/mysql-test/main/subselect_no_semijoin.result +++ b/mysql-test/main/subselect_no_semijoin.result @@ -3198,7 +3198,7 @@ ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 system PRIMARY NULL NULL NULL 1 1 PRIMARY r const PRIMARY PRIMARY 4 const 1 -2 SUBQUERY t2 range cb cb 40 NULL 3 Using where +2 SUBQUERY t2 range cb cb 40 NULL 3 Using index condition SELECT sql_no_cache t1.a, r.a, r.b FROM t1 LEFT JOIN t2 r ON r.a = (SELECT t2.a FROM t2 WHERE t2.c = t1.a AND t2.b <= '359899' ORDER BY t2.c DESC, t2.b DESC LIMIT 1) WHERE t1.a = 10; diff --git a/mysql-test/suite/maria/icp.result b/mysql-test/suite/maria/icp.result index db2c95e0942..4909ac2a400 100644 --- a/mysql-test/suite/maria/icp.result +++ b/mysql-test/suite/maria/icp.result @@ -167,7 +167,7 @@ WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00' ORDER BY ts DESC LIMIT 2; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using index condition DROP TABLE t1; # diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index e4732c6f17f..eee15d513b9 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6367,7 +6367,7 @@ int ha_partition::read_range_first(const key_range *start_key, m_ordered= sorted; eq_range= eq_range_arg; - set_end_range(end_key); + set_end_range(end_key, RANGE_SCAN_ASC); range_key_part= m_curr_key_info[0]->key_part; if (start_key) @@ -6403,6 +6403,17 @@ int ha_partition::read_range_next() DBUG_RETURN(handle_unordered_next(table->record[0], eq_range)); } + +void ha_partition::set_end_range(const key_range *end_key, + enum_range_scan_direction direction) +{ + for (uint i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + m_file[i]->set_end_range(end_key, direction); +} + + /** Create a copy of all keys used by multi_range_read() diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 00fab4cdab9..625a2eb03cb 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -859,7 +859,8 @@ public: const key_range * end_key, bool eq_range, bool sorted) override; int read_range_next() override; - + void set_end_range(const key_range *end_key, + enum_range_scan_direction direction) override; HANDLER_BUFFER *m_mrr_buffer; uint *m_mrr_buffer_size; diff --git a/sql/handler.cc b/sql/handler.cc index 81d5ec6da58..59e2214c457 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -7132,7 +7132,7 @@ int handler::read_range_first(const key_range *start_key, DBUG_ENTER("handler::read_range_first"); eq_range= eq_range_arg; - set_end_range(end_key); + set_end_range(end_key, RANGE_SCAN_ASC); range_key_part= table->key_info[active_index].key_part; if (!start_key) // Read first record @@ -7208,9 +7208,17 @@ int handler::read_range_next() } -void handler::set_end_range(const key_range *end_key) +/* + @brief + Inform the Storage Engine about the end of range to be scanned. + See opt_index_cond_pushdown.cc, "End-of-range checks". +*/ + +void handler::set_end_range(const key_range *end_key, + enum_range_scan_direction direction) { end_range= 0; + range_scan_direction= direction; if (end_key) { end_range= &save_end_range; @@ -7218,6 +7226,7 @@ void handler::set_end_range(const key_range *end_key) key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 : (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0); + range_key_part= table->key_info[active_index].key_part; } } @@ -7250,8 +7259,11 @@ int handler::compare_key(key_range *range) /* - Same as compare_key() but doesn't check have in_range_check_pushed_down. - This is used by index condition pushdown implementation. + Same as compare_key() but + - doesn't check in_range_check_pushed_down, + - supports reverse index scans. + + This is used by Index Condition Pushdown implementation. */ int handler::compare_key2(key_range *range) const @@ -7262,6 +7274,8 @@ int handler::compare_key2(key_range *range) const cmp= key_cmp(range_key_part, range->key, range->length); if (!cmp) cmp= key_compare_result_on_equal; + if (range_scan_direction == RANGE_SCAN_DESC) + cmp= -cmp; return cmp; } @@ -7286,6 +7300,10 @@ extern "C" check_result_t handler_index_cond_check(void* h_arg) if (killed > abort_at) return CHECK_ABORTED_BY_USER; } + /* + Before checking the Pushed Index Condition, check if we went out of range. + See opt_index_cond_pushdown.cc, "End-of-range checks". + */ if (unlikely(h->end_range) && h->compare_key2(h->end_range) > 0) return CHECK_OUT_OF_RANGE; h->increment_statistics(&SSV::ha_icp_attempts); diff --git a/sql/handler.h b/sql/handler.h index cb256c97428..bbd49b35f19 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -3244,6 +3244,17 @@ class handler :public Sql_alloc { public: typedef ulonglong Table_flags; + + /* + The direction of the current range or index scan. This is used by + the ICP implementation to determine if it has reached the end + of the current range. + */ + enum enum_range_scan_direction { + RANGE_SCAN_ASC, + RANGE_SCAN_DESC + }; + protected: TABLE_SHARE *table_share; /* The table definition */ TABLE *table; /* The current open table */ @@ -3259,6 +3270,7 @@ protected: */ ha_handler_stats active_handler_stats; void set_handler_stats(); + public: handlerton *ht; /* storage engine of this handler */ OPTIMIZER_COSTS *costs; /* Points to table->share->costs */ @@ -3285,7 +3297,11 @@ public: KEY_MULTI_RANGE mrr_cur_range; - /** The following are for read_range() */ +private: + /* Used by Index Condition Pushdown, handler_index_cond_check()/compare_key2() */ + enum_range_scan_direction range_scan_direction{RANGE_SCAN_ASC}; +public: + /** The following are for read_range_first/next() and ICP */ key_range save_end_range, *end_range; KEY_PART_INFO *range_key_part; int key_compare_result_on_equal; @@ -3535,7 +3551,8 @@ public: DBUG_ASSERT(inited==INDEX); inited= NONE; active_index= MAX_KEY; - end_range= NULL; + end_range= NULL; + range_scan_direction= RANGE_SCAN_ASC; DBUG_RETURN(index_end()); } /* This is called after index_init() if we need to do a index scan */ @@ -4335,7 +4352,8 @@ public: const key_range *end_key, bool eq_range, bool sorted); virtual int read_range_next(); - void set_end_range(const key_range *end_key); + virtual void set_end_range(const key_range *end_key, + enum_range_scan_direction direction = RANGE_SCAN_ASC); int compare_key(key_range *range); int compare_key2(key_range *range) const; virtual int ft_init() { return HA_ERR_WRONG_COMMAND; } diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index f98adee4f7c..a42868917a4 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -19,9 +19,89 @@ #include "sql_test.h" #include "opt_trace.h" -/**************************************************************************** - * Index Condition Pushdown code starts - ***************************************************************************/ +/* + Index Condition Pushdown Module + =============================== + + Storage Engine API + ================== + SQL layer can push a condition to be checked for index tuple by calling + + handler::idx_cond_push(uint keyno, Item *cond) + + After that, the SQL layer is expected to start an index scan on the specified + index. The scan should be non-index-only (that is, do not use HA_EXTRA_KEYREAD + option). + + Then, any call that reads rows from the index: + + handler->some_index_read_function() + + will check the index condition (see handler_index_cond_check()) and ignore + index tuples that do not match it. + + Pushing index condition requires pushing end-of-range check, too + ================================================================ + + Suppose we're computing + + select * + from t1 + where key1 between 10 and 20 and extra_index_cond + + by using a range scan on (10 <= key1 <= 20) and pushing extra_index_cond as + pushed index condition. + SQL could use these calls to read rows: + + h->idx_cond_push(key1, extra_index_cond); + h->index_read_map(key1=10, HA_READ_KEY_OR_NEXT); // (read-1) + while (h->index_next() != HA_ERR_END_OF_FILE) { // (read-2) + if (cmp_key(h->record, "key1=20" ) < 0) + break; // end of range + //process row. + } + + Suppose an index read function above (either (read-1) or (read-2)) encounters + key1=21. Suppose extra_index_cond evaluates to false for this row. Then, it + will proceed to read next row, e.g. key1=22. If extra_index_cond again + evaluates to false it will continue further. This way, the index scan can + continue till the end of the index, ignoring the fact that we are not + interested in rows with key1>20. + + The solution is: whenever ICP is used, the storage engine must be aware of the + end of the range being scanned so it can stop the scan as soon as it is reached. + + End-of-range checks + =================== + There are four call patterns: + + 1. Index Navigation commands. End of range check is setup with set_end_range + call: + + handler->set_end_range(endpoint, direction); + handler->index_read_XXXX(); + while (handler->index_next() == 0) // or index_prev() + { ... } + + 2. Range Read API. set_end_range is called from read_range_first: + + handler->read_range_first(start_range, end_range); + while (handler->read_range_next() == 0) { ... } + + 3. Equality lookups + + handler->index_read_map(lookup_tuple, HA_READ_KEY_EXACT); + while (handler->index_next_same() == 0) { ... } + + Here, set_end_range is not necessary, because index scanning code + will not read index tuples that do not match the lookup tuple. + + 4. multi_range_read calls. + These either fall-back to Range Read API or use their own ICP + implementation with its own ICP checks. +*/ + + /* Check if given expression uses only table fields covered by the given index diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1ee4cdeb5d3..cf4585b52a3 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -13766,16 +13766,24 @@ int QUICK_SELECT_DESC::get_next() continue; } - if (last_range->flag & EQ_RANGE && - used_key_parts <= head->key_info[index].user_defined_key_parts) + // Case where we can avoid descending scan, see comment above + const bool eqrange_all_keyparts= (last_range->flag & EQ_RANGE) && + (used_key_parts <= head->key_info[index].user_defined_key_parts); + if (eqrange_all_keyparts) { + file->set_end_range(NULL, handler::RANGE_SCAN_ASC); result= file->ha_index_read_map(record, last_range->max_key, last_range->max_keypart_map, HA_READ_KEY_EXACT); } else { + key_range min_range; + last_range->make_min_endpoint(&min_range); + if (min_range.length > 0) + file->set_end_range(&min_range, handler::RANGE_SCAN_DESC); + DBUG_ASSERT(last_range->flag & NEAR_MAX || (last_range->flag & EQ_RANGE && used_key_parts > head->key_info[index].user_defined_key_parts) || diff --git a/sql/sql_select.cc b/sql/sql_select.cc index be3efae83e7..0ffc0a5f9ce 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -25016,6 +25016,20 @@ join_read_last_key(JOIN_TAB *tab) report_error(table,error); return -1; } + + /* + Tell the storage engine what the range endpoint is so that it can stop + scanning once it has hit that point. + */ + key_range range_endpoint + { + tab->ref.key_buff, + tab->ref.key_length, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_PREFIX_LAST + }; + table->file->set_end_range(&range_endpoint, handler::RANGE_SCAN_DESC); + if (unlikely((error= table->file->ha_index_read_map(table->record[0], tab->ref.key_buff, @@ -27042,9 +27056,9 @@ void compute_part_of_sort_key_for_equals(JOIN *join, TABLE *table, @detail - Disable "Range checked for each record" (Is this strictly necessary here?) - - Disable Index Condition Pushdown and Rowid Filtering. + - Disable Rowid Filtering. - IndexConditionPushdownAndReverseScans, RowidFilteringAndReverseScans: + RowidFilteringAndReverseScans: Suppose we're computing select * from t1 @@ -27076,10 +27090,10 @@ void compute_part_of_sort_key_for_equals(JOIN *join, TABLE *table, } Note that the check whether we've walked beyond the key=10 endpoint is - made at the SQL layer. The storage engine has no information about the left + made at the SQL layer. The storage engine has no information about the left endpoint of the interval we're scanning. If all rows before that endpoint - do not satisfy ICP condition or do not pass the Rowid Filter, the storage - engine will enumerate the records until the table start. + do not pass the Rowid Filter, the storage engine will enumerate the records + until the table start. In MySQL, the API is extended with set_end_range() call so that the storage engine "knows" when to stop scanning. @@ -27094,16 +27108,7 @@ static void prepare_for_reverse_ordered_access(JOIN_TAB *tab) tab->read_first_record= join_init_read_record; } /* - Cancel Pushed Index Condition, as it doesn't work for reverse scans. - */ - if (tab->select && tab->select->pre_idx_push_select_cond) - { - tab->set_cond(tab->select->pre_idx_push_select_cond); - tab->table->file->cancel_pushed_idx_cond(); - } - /* - The same with Rowid Filter: it doesn't work with reverse scans so cancel - it, too. + Rowid filtering does not work with reverse scans so cancel it. */ { /*