mirror of
https://github.com/MariaDB/server.git
synced 2025-04-18 21:44:20 +03:00
MDEV-34413 Index Condition Pushdown for reverse ordered scans
Allows index condition pushdown for reverse ordered scans, a previously disabled feature due to poor performance. This patch adds a new API to the handler class called set_end_range which allows callers to tell the handler what the end of the index range will be when scanning. Combined with a pushed index condition, the handler can scan the index efficiently and not read beyond the end of the given range. When checking if the pushed index condition matches, the handler will also check if scanning has reached the end of the provided range and stop if so. If we instead only enabled ICP for reverse ordered scans without also calling this new API, then the handler would perform unnecessary index condition checks. In fact this would continue until the end of the index is reached. These changes are agnostic of storage engine. That is, any storage engine that supports index condition pushdown will inhereit this new behavior as it is implemented in the SQL and storage engine API layers. The partitioned tables storage meta-engine (ha_partition) adds an override of set_end_range which recursively calls set_end_range on its child storage engine (handler) implementations. This commit updates the test made in an earlier commit to show that ICP matches happen for the reverse ordered case. This patch is based on changes written by Olav Sandstaa in MySQL commit da1d92fd46071cd86de61058b6ea39fd9affcd87
This commit is contained in:
parent
261d5520a2
commit
7e4233746e
@ -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;
|
||||
|
@ -1,4 +1,3 @@
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_partition.inc
|
||||
--source include/have_sequence.inc
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
#
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) ||
|
||||
|
@ -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.
|
||||
*/
|
||||
{
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user