Commit 791146b9 authored by Sergei Petrunia's avatar Sergei Petrunia Committed by Sergei Golubchik

MDEV-26996 Support descending indexes in the range optimizer

Make the Range Optimizer support descending index key parts.

We follow the approach taken in MySQL-8.

See HowRangeOptimizerHandlesDescKeyparts for the description.
parent a4cac0e0
create table t1 (
a int,
key (a desc)
);
insert into t1 select seq from seq_1_to_1000;
set optimizer_trace=1;
explain select * from t1 force index(a) where a in (2, 4, 6);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 3 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(6) <= (a) <= (6)",
"(4) <= (a) <= (4)",
"(2) <= (a) <= (2)"
]
]
set optimizer_trace=default;
# These should go in reverse order:
select * from t1 force index(a) where a in (2, 4, 6);
a
6
4
2
drop table t1;
#
# Multi-part key tests
#
create table t1 (
a int not null,
b int not null,
key ab(a, b desc)
);
insert into t1 select A.seq, B.seq*10 from seq_1_to_10 A, seq_1_to_10 B;
set optimizer_trace=1;
explain select * from t1 force index(ab) where a>=8 and b>=50;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range ab ab 4 NULL 51 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(8) <= (a)"
]
]
explain select * from t1 force index(ab) where a>=8 and b<=50;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range ab ab 8 NULL 46 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(8,50) <= (a,b)"
]
]
select * from t1 force index(ab) where a>=8 and b<=50;
a b
8 50
8 40
8 30
8 20
8 10
9 50
9 40
9 30
9 20
9 10
10 50
10 40
10 30
10 20
10 10
select * from t1 ignore index(ab) where a>=8 and b<=50 order by a, b desc;
a b
8 50
8 40
8 30
8 20
8 10
9 50
9 40
9 30
9 20
9 10
10 50
10 40
10 30
10 20
10 10
explain
select * from t1 where a between 2 and 4 and b between 50 and 80;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range ab ab 8 NULL 17 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(2,80) <= (a,b) <= (4,50)"
]
]
select * from t1 where a between 2 and 4 and b between 50 and 80;
a b
2 80
2 70
2 60
2 50
3 80
3 70
3 60
3 50
4 80
4 70
4 60
4 50
drop table t1;
create table t2 (
a int not null,
b int not null,
key ab(a desc, b desc)
);
insert into t2 select A.seq, B.seq*10 from seq_1_to_10 A, seq_1_to_10 B;
explain
select * from t2 where a between 2 and 4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 range ab ab 4 NULL 40 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(4) <= (a) <= (2)"
]
]
explain
select * from t2 where a between 2 and 4 and b between 50 and 80;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 range ab ab 8 NULL 31 Using where; Using index
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
[
[
"(4,80) <= (a,b) <= (2,50)"
]
]
set optimizer_trace=default;
drop table t2;
#
# Tests for range access and descending indexes
#
--source include/have_sequence.inc
--source include/have_innodb.inc
# The test uses optimizer trace:
--source include/not_embedded.inc
create table t1 (
a int,
key (a desc)
);
insert into t1 select seq from seq_1_to_1000;
set optimizer_trace=1;
explain select * from t1 force index(a) where a in (2, 4, 6);
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
set optimizer_trace=default;
--echo # These should go in reverse order:
select * from t1 force index(a) where a in (2, 4, 6);
drop table t1;
--echo #
--echo # Multi-part key tests
--echo #
create table t1 (
a int not null,
b int not null,
key ab(a, b desc)
);
insert into t1 select A.seq, B.seq*10 from seq_1_to_10 A, seq_1_to_10 B;
set optimizer_trace=1;
explain select * from t1 force index(ab) where a>=8 and b>=50;
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
explain select * from t1 force index(ab) where a>=8 and b<=50;
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
select * from t1 force index(ab) where a>=8 and b<=50;
select * from t1 ignore index(ab) where a>=8 and b<=50 order by a, b desc;
explain
select * from t1 where a between 2 and 4 and b between 50 and 80;
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
select * from t1 where a between 2 and 4 and b between 50 and 80;
drop table t1;
create table t2 (
a int not null,
b int not null,
key ab(a desc, b desc)
);
insert into t2 select A.seq, B.seq*10 from seq_1_to_10 A, seq_1_to_10 B;
explain
select * from t2 where a between 2 and 4;
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
explain
select * from t2 where a between 2 and 4 and b between 50 and 80;
select json_detailed(json_extract(trace, '$**.range_access_plan.ranges'))
from information_schema.optimizer_trace;
set optimizer_trace=default;
drop table t2;
...@@ -1083,7 +1083,8 @@ Item_func_spatial_rel::get_mm_leaf(RANGE_OPT_PARAM *param, ...@@ -1083,7 +1083,8 @@ Item_func_spatial_rel::get_mm_leaf(RANGE_OPT_PARAM *param,
DBUG_RETURN(0); // out of memory DBUG_RETURN(0); // out of memory
field->get_key_image(str, key_part->length, key_part->image_type); field->get_key_image(str, key_part->length, key_part->image_type);
SEL_ARG *tree; SEL_ARG *tree;
if (!(tree= new (param->mem_root) SEL_ARG(field, str, str)))
if (!(tree= new (param->mem_root) SEL_ARG(field, true, str, str)))
DBUG_RETURN(0); // out of memory DBUG_RETURN(0); // out of memory
switch (type) { switch (type) {
......
...@@ -495,6 +495,7 @@ int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length) ...@@ -495,6 +495,7 @@ int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length)
{ {
int cmp; int cmp;
store_length= key_part->store_length; store_length= key_part->store_length;
int sort_order = (key_part->key_part_flag & HA_REVERSE_SORT) ? -1 : 1;
if (key_part->null_bit) if (key_part->null_bit)
{ {
/* This key part allows null values; NULL is lower than everything */ /* This key part allows null values; NULL is lower than everything */
...@@ -503,19 +504,19 @@ int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length) ...@@ -503,19 +504,19 @@ int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length)
{ {
/* the range is expecting a null value */ /* the range is expecting a null value */
if (!field_is_null) if (!field_is_null)
return 1; // Found key is > range return sort_order; // Found key is > range
/* null -- exact match, go to next key part */ /* null -- exact match, go to next key part */
continue; continue;
} }
else if (field_is_null) else if (field_is_null)
return -1; // NULL is less than any value return -sort_order; // NULL is less than any value
key++; // Skip null byte key++; // Skip null byte
store_length--; store_length--;
} }
if ((cmp=key_part->field->key_cmp(key, key_part->length)) < 0) if ((cmp=key_part->field->key_cmp(key, key_part->length)) < 0)
return -1; return -sort_order;
if (cmp > 0) if (cmp > 0)
return 1; return sort_order;
} }
return 0; // Keys are equal return 0; // Keys are equal
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -34,7 +34,7 @@ typedef struct st_range_seq_entry ...@@ -34,7 +34,7 @@ typedef struct st_range_seq_entry
uint min_key_flag, max_key_flag; uint min_key_flag, max_key_flag;
/* Number of key parts */ /* Number of key parts */
uint min_key_parts, max_key_parts; int min_key_parts, max_key_parts;
SEL_ARG *key_tree; SEL_ARG *key_tree;
} RANGE_SEQ_ENTRY; } RANGE_SEQ_ENTRY;
...@@ -105,13 +105,14 @@ static void step_down_to(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree) ...@@ -105,13 +105,14 @@ static void step_down_to(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree)
cur->max_key_parts= prev->max_key_parts; cur->max_key_parts= prev->max_key_parts;
uint16 stor_length= arg->param->key[arg->keyno][key_tree->part].store_length; uint16 stor_length= arg->param->key[arg->keyno][key_tree->part].store_length;
cur->min_key_parts += key_tree->store_min(stor_length, &cur->min_key,
prev->min_key_flag);
cur->max_key_parts += key_tree->store_max(stor_length, &cur->max_key,
prev->max_key_flag);
cur->min_key_flag= prev->min_key_flag | key_tree->min_flag; key_tree->store_min_max(stor_length,
cur->max_key_flag= prev->max_key_flag | key_tree->max_flag; &cur->min_key, prev->min_key_flag,
&cur->max_key, prev->max_key_flag,
&cur->min_key_parts, &cur->max_key_parts);
cur->min_key_flag= prev->min_key_flag | key_tree->get_min_flag();
cur->max_key_flag= prev->max_key_flag | key_tree->get_max_flag();
if (key_tree->is_null_interval()) if (key_tree->is_null_interval())
cur->min_key_flag |= NULL_RANGE; cur->min_key_flag |= NULL_RANGE;
...@@ -165,12 +166,12 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) ...@@ -165,12 +166,12 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
/* Ok, we're at some "full tuple" position in the tree */ /* Ok, we're at some "full tuple" position in the tree */
/* Step down if we can */ /* Step down if we can */
if (key_tree->next && key_tree->next != &null_element) if (key_tree->index_order_next() && key_tree->index_order_next() != &null_element)
{ {
//step down; (update the tuple, we'll step right and stay there) //step down; (update the tuple, we'll step right and stay there)
seq->i--; seq->i--;
step_down_to(seq, key_tree->next); step_down_to(seq, key_tree->index_order_next());
key_tree= key_tree->next; key_tree= key_tree->index_order_next();
seq->is_ror_scan= FALSE; seq->is_ror_scan= FALSE;
goto walk_right_n_up; goto walk_right_n_up;
} }
...@@ -185,12 +186,12 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) ...@@ -185,12 +186,12 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
key_tree= seq->stack[seq->i].key_tree; key_tree= seq->stack[seq->i].key_tree;
/* Step down if we can */ /* Step down if we can */
if (key_tree->next && key_tree->next != &null_element) if (key_tree->index_order_next() && key_tree->index_order_next() != &null_element)
{ {
// Step down; update the tuple // Step down; update the tuple
seq->i--; seq->i--;
step_down_to(seq, key_tree->next); step_down_to(seq, key_tree->index_order_next());
key_tree= key_tree->next; key_tree= key_tree->index_order_next();
break; break;
} }
} }
...@@ -214,16 +215,10 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) ...@@ -214,16 +215,10 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
!key_tree->min_flag && !key_tree->max_flag)) !key_tree->min_flag && !key_tree->max_flag))
{ {
seq->is_ror_scan= FALSE; seq->is_ror_scan= FALSE;
if (!key_tree->min_flag) key_tree->store_next_min_max_keys(seq->param->key[seq->keyno],
cur->min_key_parts += &cur->min_key, &cur->min_key_flag,
key_tree->next_key_part->store_min_key(seq->param->key[seq->keyno], &cur->max_key, &cur->max_key_flag,
&cur->min_key, &cur->min_key_parts, &cur->max_key_parts);
&cur->min_key_flag, MAX_KEY);
if (!key_tree->max_flag)
cur->max_key_parts +=
key_tree->next_key_part->store_max_key(seq->param->key[seq->keyno],
&cur->max_key,
&cur->max_key_flag, MAX_KEY);
break; break;
} }
} }
...@@ -235,10 +230,11 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) ...@@ -235,10 +230,11 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
key_tree= key_tree->next_key_part; key_tree= key_tree->next_key_part;
walk_up_n_right: walk_up_n_right:
while (key_tree->prev && key_tree->prev != &null_element) while (key_tree->index_order_prev() &&
key_tree->index_order_prev() != &null_element)
{ {
/* Step up */ /* Step up */
key_tree= key_tree->prev; key_tree= key_tree->index_order_prev();
} }
step_down_to(seq, key_tree); step_down_to(seq, key_tree);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment