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
} }
......
...@@ -1879,6 +1879,7 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() ...@@ -1879,6 +1879,7 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc()
max_flag=arg.max_flag; max_flag=arg.max_flag;
maybe_flag=arg.maybe_flag; maybe_flag=arg.maybe_flag;
maybe_null=arg.maybe_null; maybe_null=arg.maybe_null;
is_ascending= arg.is_ascending;
part=arg.part; part=arg.part;
field=arg.field; field=arg.field;
min_value=arg.min_value; min_value=arg.min_value;
...@@ -1904,9 +1905,10 @@ inline void SEL_ARG::make_root() ...@@ -1904,9 +1905,10 @@ inline void SEL_ARG::make_root()
use_count=0; elements=1; use_count=0; elements=1;
} }
SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg, SEL_ARG::SEL_ARG(Field *f, bool is_asc, const uchar *min_value_arg,
const uchar *max_value_arg) const uchar *max_value_arg)
:min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()), :min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()),
is_ascending(is_asc),
elements(1), use_count(1), field(f), min_value((uchar*) min_value_arg), elements(1), use_count(1), field(f), min_value((uchar*) min_value_arg),
max_value((uchar*) max_value_arg), next(0),prev(0), max_value((uchar*) max_value_arg), next(0),prev(0),
next_key_part(0), color(BLACK), type(KEY_RANGE), weight(1) next_key_part(0), color(BLACK), type(KEY_RANGE), weight(1)
...@@ -1915,11 +1917,12 @@ SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg, ...@@ -1915,11 +1917,12 @@ SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg,
max_part_no= 1; max_part_no= 1;
} }
SEL_ARG::SEL_ARG(Field *field_,uint8 part_, SEL_ARG::SEL_ARG(Field *field_,uint8 part_, bool is_asc_,
uchar *min_value_, uchar *max_value_, uchar *min_value_, uchar *max_value_,
uint8 min_flag_,uint8 max_flag_,uint8 maybe_flag_) uint8 min_flag_,uint8 max_flag_,uint8 maybe_flag_)
:min_flag(min_flag_),max_flag(max_flag_),maybe_flag(maybe_flag_), :min_flag(min_flag_),max_flag(max_flag_),maybe_flag(maybe_flag_),
part(part_),maybe_null(field_->real_maybe_null()), elements(1),use_count(1), part(part_),maybe_null(field_->real_maybe_null()), is_ascending(is_asc_),
elements(1),use_count(1),
field(field_), min_value(min_value_), max_value(max_value_), field(field_), min_value(min_value_), max_value(max_value_),
next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE), weight(1) next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE), weight(1)
{ {
...@@ -1938,8 +1941,8 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_, ...@@ -1938,8 +1941,8 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,
class SEL_ARG_LE: public SEL_ARG class SEL_ARG_LE: public SEL_ARG
{ {
public: public:
SEL_ARG_LE(const uchar *key, Field *field) SEL_ARG_LE(const uchar *key, Field *field, bool is_asc)
:SEL_ARG(field, key, key) :SEL_ARG(field, is_asc, key, key)
{ {
if (!field->real_maybe_null()) if (!field->real_maybe_null())
min_flag= NO_MIN_RANGE; // From start min_flag= NO_MIN_RANGE; // From start
...@@ -1959,16 +1962,17 @@ class SEL_ARG_LT: public SEL_ARG_LE ...@@ -1959,16 +1962,17 @@ class SEL_ARG_LT: public SEL_ARG_LE
Use this constructor if value->save_in_field() went precisely, Use this constructor if value->save_in_field() went precisely,
without any data rounding or truncation. without any data rounding or truncation.
*/ */
SEL_ARG_LT(const uchar *key, Field *field) SEL_ARG_LT(const uchar *key, Field *field, bool is_asc)
:SEL_ARG_LE(key, field) :SEL_ARG_LE(key, field, is_asc)
{ max_flag= NEAR_MAX; } { max_flag= NEAR_MAX; }
/* /*
Use this constructor if value->save_in_field() returned success, Use this constructor if value->save_in_field() returned success,
but we don't know if rounding or truncation happened but we don't know if rounding or truncation happened
(as some Field::store() do not report minor data changes). (as some Field::store() do not report minor data changes).
*/ */
SEL_ARG_LT(THD *thd, const uchar *key, Field *field, Item *value) SEL_ARG_LT(THD *thd, const uchar *key, Field *field, bool is_asc,
:SEL_ARG_LE(key, field) Item *value)
:SEL_ARG_LE(key, field, is_asc)
{ {
if (stored_field_cmp_to_item(thd, field, value) == 0) if (stored_field_cmp_to_item(thd, field, value) == 0)
max_flag= NEAR_MAX; max_flag= NEAR_MAX;
...@@ -1984,7 +1988,7 @@ class SEL_ARG_GT: public SEL_ARG ...@@ -1984,7 +1988,7 @@ class SEL_ARG_GT: public SEL_ARG
without any data rounding or truncation. without any data rounding or truncation.
*/ */
SEL_ARG_GT(const uchar *key, const KEY_PART *key_part, Field *field) SEL_ARG_GT(const uchar *key, const KEY_PART *key_part, Field *field)
:SEL_ARG(field, key, key) :SEL_ARG(field, !(key_part->flag & HA_REVERSE_SORT), key, key)
{ {
// Don't use open ranges for partial key_segments // Don't use open ranges for partial key_segments
if (!(key_part->flag & HA_PART_KEY_SEG)) if (!(key_part->flag & HA_PART_KEY_SEG))
...@@ -1998,7 +2002,7 @@ class SEL_ARG_GT: public SEL_ARG ...@@ -1998,7 +2002,7 @@ class SEL_ARG_GT: public SEL_ARG
*/ */
SEL_ARG_GT(THD *thd, const uchar *key, SEL_ARG_GT(THD *thd, const uchar *key,
const KEY_PART *key_part, Field *field, Item *value) const KEY_PART *key_part, Field *field, Item *value)
:SEL_ARG(field, key, key) :SEL_ARG(field, !(key_part->flag & HA_REVERSE_SORT), key, key)
{ {
// Don't use open ranges for partial key_segments // Don't use open ranges for partial key_segments
if ((!(key_part->flag & HA_PART_KEY_SEG)) && if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
...@@ -2016,8 +2020,8 @@ class SEL_ARG_GE: public SEL_ARG ...@@ -2016,8 +2020,8 @@ class SEL_ARG_GE: public SEL_ARG
Use this constructor if value->save_in_field() went precisely, Use this constructor if value->save_in_field() went precisely,
without any data rounding or truncation. without any data rounding or truncation.
*/ */
SEL_ARG_GE(const uchar *key, Field *field) SEL_ARG_GE(const uchar *key, Field *field, bool is_asc)
:SEL_ARG(field, key, key) :SEL_ARG(field, is_asc, key, key)
{ {
max_flag= NO_MAX_RANGE; max_flag= NO_MAX_RANGE;
} }
...@@ -2028,7 +2032,7 @@ class SEL_ARG_GE: public SEL_ARG ...@@ -2028,7 +2032,7 @@ class SEL_ARG_GE: public SEL_ARG
*/ */
SEL_ARG_GE(THD *thd, const uchar *key, SEL_ARG_GE(THD *thd, const uchar *key,
const KEY_PART *key_part, Field *field, Item *value) const KEY_PART *key_part, Field *field, Item *value)
:SEL_ARG(field, key, key) :SEL_ARG(field, !(key_part->flag & HA_REVERSE_SORT), key, key)
{ {
// Don't use open ranges for partial key_segments // Don't use open ranges for partial key_segments
if ((!(key_part->flag & HA_PART_KEY_SEG)) && if ((!(key_part->flag & HA_PART_KEY_SEG)) &&
...@@ -2059,7 +2063,8 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, ...@@ -2059,7 +2063,8 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
} }
else else
{ {
if (!(tmp= new (param->mem_root) SEL_ARG(field,part, min_value,max_value, if (!(tmp= new (param->mem_root) SEL_ARG(field, part, is_ascending,
min_value, max_value,
min_flag, max_flag, maybe_flag))) min_flag, max_flag, maybe_flag)))
return 0; // OOM return 0; // OOM
tmp->parent=new_parent; tmp->parent=new_parent;
...@@ -2830,6 +2835,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -2830,6 +2835,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
} }
trace_keypart.end(); trace_keypart.end();
trace_idx_details.add("usable", !unusable_has_desc_keyparts); trace_idx_details.add("usable", !unusable_has_desc_keyparts);
unusable_has_desc_keyparts= false;
if (unusable_has_desc_keyparts) // TODO MDEV-13756 if (unusable_has_desc_keyparts) // TODO MDEV-13756
{ {
key_parts= param.key[param.keys]; key_parts= param.key[param.keys];
...@@ -4423,12 +4429,14 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) ...@@ -4423,12 +4429,14 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
key_tree->next_key_part->store_min_key(ppar->key, key_tree->next_key_part->store_min_key(ppar->key,
&tmp_min_key, &tmp_min_key,
&tmp_min_flag, &tmp_min_flag,
ppar->last_part_partno); ppar->last_part_partno,
true);
if (!tmp_max_flag) if (!tmp_max_flag)
key_tree->next_key_part->store_max_key(ppar->key, key_tree->next_key_part->store_max_key(ppar->key,
&tmp_max_key, &tmp_max_key,
&tmp_max_flag, &tmp_max_flag,
ppar->last_part_partno); ppar->last_part_partno,
false);
flag= tmp_min_flag | tmp_max_flag; flag= tmp_min_flag | tmp_max_flag;
} }
else else
...@@ -8674,7 +8682,8 @@ Item_func_null_predicate::get_mm_leaf(RANGE_OPT_PARAM *param, ...@@ -8674,7 +8682,8 @@ Item_func_null_predicate::get_mm_leaf(RANGE_OPT_PARAM *param,
if (!field->real_maybe_null()) if (!field->real_maybe_null())
DBUG_RETURN(type == ISNULL_FUNC ? &null_element : NULL); DBUG_RETURN(type == ISNULL_FUNC ? &null_element : NULL);
SEL_ARG *tree; SEL_ARG *tree;
if (!(tree= new (alloc) SEL_ARG(field, is_null_string, is_null_string))) bool is_asc= !(key_part->flag & HA_REVERSE_SORT);
if (!(tree= new (alloc) SEL_ARG(field, is_asc, is_null_string, is_null_string)))
DBUG_RETURN(0); DBUG_RETURN(0);
if (type == Item_func::ISNOTNULL_FUNC) if (type == Item_func::ISNOTNULL_FUNC)
{ {
...@@ -8774,7 +8783,8 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, ...@@ -8774,7 +8783,8 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
int2store(min_str + maybe_null, min_length); int2store(min_str + maybe_null, min_length);
int2store(max_str + maybe_null, max_length); int2store(max_str + maybe_null, max_length);
} }
SEL_ARG *tree= new (param->mem_root) SEL_ARG(field, min_str, max_str); bool is_asc= !(key_part->flag & HA_REVERSE_SORT);
SEL_ARG *tree= new (param->mem_root) SEL_ARG(field, is_asc, min_str, max_str);
DBUG_RETURN(tree); DBUG_RETURN(tree);
} }
...@@ -9022,18 +9032,19 @@ SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param, ...@@ -9022,18 +9032,19 @@ SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
if (!(str= make_key_image(param->mem_root, key_part))) if (!(str= make_key_image(param->mem_root, key_part)))
DBUG_RETURN(0); DBUG_RETURN(0);
bool is_asc= !(key_part->flag & HA_REVERSE_SORT);
switch (op) { switch (op) {
case SCALAR_CMP_LE: case SCALAR_CMP_LE:
DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this)); DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this, is_asc));
case SCALAR_CMP_LT: case SCALAR_CMP_LT:
DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, this, value)); DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, this, is_asc, value));
case SCALAR_CMP_GT: case SCALAR_CMP_GT:
DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value)); DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value));
case SCALAR_CMP_GE: case SCALAR_CMP_GE:
DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value)); DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value));
case SCALAR_CMP_EQ: case SCALAR_CMP_EQ:
case SCALAR_CMP_EQUAL: case SCALAR_CMP_EQUAL:
DBUG_RETURN(new (mem_root) SEL_ARG(this, str, str)); DBUG_RETURN(new (mem_root) SEL_ARG(this, is_asc, str, str));
break; break;
} }
DBUG_ASSERT(0); DBUG_ASSERT(0);
...@@ -9051,18 +9062,19 @@ SEL_ARG *Field::stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param, ...@@ -9051,18 +9062,19 @@ SEL_ARG *Field::stored_field_make_mm_leaf_exact(RANGE_OPT_PARAM *param,
if (!(str= make_key_image(param->mem_root, key_part))) if (!(str= make_key_image(param->mem_root, key_part)))
DBUG_RETURN(0); DBUG_RETURN(0);
bool is_asc= !(key_part->flag & HA_REVERSE_SORT);
switch (op) { switch (op) {
case SCALAR_CMP_LE: case SCALAR_CMP_LE:
DBUG_RETURN(new (param->mem_root) SEL_ARG_LE(str, this)); DBUG_RETURN(new (param->mem_root) SEL_ARG_LE(str, this, is_asc));
case SCALAR_CMP_LT: case SCALAR_CMP_LT:
DBUG_RETURN(new (param->mem_root) SEL_ARG_LT(str, this)); DBUG_RETURN(new (param->mem_root) SEL_ARG_LT(str, this, is_asc));
case SCALAR_CMP_GT: case SCALAR_CMP_GT:
DBUG_RETURN(new (param->mem_root) SEL_ARG_GT(str, key_part, this)); DBUG_RETURN(new (param->mem_root) SEL_ARG_GT(str, key_part, this));
case SCALAR_CMP_GE: case SCALAR_CMP_GE:
DBUG_RETURN(new (param->mem_root) SEL_ARG_GE(str, this)); DBUG_RETURN(new (param->mem_root) SEL_ARG_GE(str, this, is_asc));
case SCALAR_CMP_EQ: case SCALAR_CMP_EQ:
case SCALAR_CMP_EQUAL: case SCALAR_CMP_EQUAL:
DBUG_RETURN(new (param->mem_root) SEL_ARG(this, str, str)); DBUG_RETURN(new (param->mem_root) SEL_ARG(this, is_asc, str, str));
break; break;
} }
DBUG_ASSERT(0); DBUG_ASSERT(0);
...@@ -11780,6 +11792,46 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags, ...@@ -11780,6 +11792,46 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags,
} }
void SEL_ARG::store_next_min_max_keys(KEY_PART *key,
uchar **cur_min_key, uint *cur_min_flag,
uchar **cur_max_key, uint *cur_max_flag,
int *min_part, int *max_part)
{
DBUG_ASSERT(next_key_part);
bool asc = next_key_part->is_ascending;
if (!get_min_flag())
{
if (asc)
{
*min_part += next_key_part->store_min_key(key, cur_min_key,
cur_min_flag, MAX_KEY, true);
}
else
{
uint tmp_flag = invert_min_flag(*cur_min_flag);
*min_part += next_key_part->store_max_key(key, cur_min_key, &tmp_flag,
MAX_KEY, true);
*cur_min_flag = invert_max_flag(tmp_flag);
}
}
if (!get_max_flag())
{
if (asc)
{
*max_part += next_key_part->store_max_key(key, cur_max_key,
cur_max_flag, MAX_KEY, false);
}
else
{
uint tmp_flag = invert_max_flag(*cur_max_flag);
*max_part += next_key_part->store_min_key(key, cur_max_key, &tmp_flag,
MAX_KEY, false);
*cur_max_flag = invert_min_flag(tmp_flag);
}
}
}
/* /*
** Fix this to get all possible sub_ranges ** Fix this to get all possible sub_ranges
*/ */
...@@ -11793,17 +11845,19 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, ...@@ -11793,17 +11845,19 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
int min_part= key_tree->part-1, // # of keypart values in min_key buffer int min_part= key_tree->part-1, // # of keypart values in min_key buffer
max_part= key_tree->part-1; // # of keypart values in max_key buffer max_part= key_tree->part-1; // # of keypart values in max_key buffer
if (key_tree->left != &null_element) SEL_ARG *next_tree = key_tree->is_ascending ? key_tree->left : key_tree->right;
if (next_tree != &null_element)
{ {
if (get_quick_keys(param,quick,key,key_tree->left, if (get_quick_keys(param,quick,key,next_tree,
min_key,min_key_flag, max_key, max_key_flag)) min_key,min_key_flag, max_key, max_key_flag))
return 1; return 1;
} }
uchar *tmp_min_key=min_key,*tmp_max_key=max_key; uchar *tmp_min_key=min_key,*tmp_max_key=max_key;
min_part+= key_tree->store_min(key[key_tree->part].store_length,
&tmp_min_key,min_key_flag); key_tree->store_min_max(key[key_tree->part].store_length,
max_part+= key_tree->store_max(key[key_tree->part].store_length, &tmp_min_key, min_key_flag,
&tmp_max_key,max_key_flag); &tmp_max_key, max_key_flag,
&min_part, &max_part);
if (key_tree->next_key_part && if (key_tree->next_key_part &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE && key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
...@@ -11813,31 +11867,40 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, ...@@ -11813,31 +11867,40 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
memcmp(min_key, max_key, (uint)(tmp_max_key - max_key))==0 && memcmp(min_key, max_key, (uint)(tmp_max_key - max_key))==0 &&
key_tree->min_flag==0 && key_tree->max_flag==0) key_tree->min_flag==0 && key_tree->max_flag==0)
{ {
// psergey-note: simplified the parameters below as follows:
// min_key_flag | key_tree->min_flag -> min_key_flag
// max_key_flag | key_tree->max_flag -> max_key_flag
if (get_quick_keys(param,quick,key,key_tree->next_key_part, if (get_quick_keys(param,quick,key,key_tree->next_key_part,
tmp_min_key, min_key_flag | key_tree->min_flag, tmp_min_key, min_key_flag,
tmp_max_key, max_key_flag | key_tree->max_flag)) tmp_max_key, max_key_flag))
return 1; return 1;
goto end; // Ugly, but efficient goto end; // Ugly, but efficient
} }
{ {
uint tmp_min_flag=key_tree->min_flag,tmp_max_flag=key_tree->max_flag; uint tmp_min_flag= key_tree->get_min_flag();
if (!tmp_min_flag) uint tmp_max_flag= key_tree->get_max_flag();
min_part+= key_tree->next_key_part->store_min_key(key,
&tmp_min_key, key_tree->store_next_min_max_keys(key,
&tmp_min_flag, &tmp_min_key, &tmp_min_flag,
MAX_KEY); &tmp_max_key, &tmp_max_flag,
if (!tmp_max_flag) &min_part, &max_part);
max_part+= key_tree->next_key_part->store_max_key(key,
&tmp_max_key,
&tmp_max_flag,
MAX_KEY);
flag=tmp_min_flag | tmp_max_flag; flag=tmp_min_flag | tmp_max_flag;
} }
} }
else else
{ {
flag = (key_tree->min_flag & GEOM_FLAG) ? if (key_tree->is_ascending)
key_tree->min_flag : key_tree->min_flag | key_tree->max_flag; {
flag= (key_tree->min_flag & GEOM_FLAG) ? key_tree->min_flag:
(key_tree->min_flag |
key_tree->max_flag);
}
else
{
// Invert flags for DESC keypart
flag= invert_min_flag(key_tree->min_flag) |
invert_max_flag(key_tree->max_flag);
}
} }
/* /*
...@@ -11898,8 +11961,9 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, ...@@ -11898,8 +11961,9 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
return 1; return 1;
end: end:
if (key_tree->right != &null_element) next_tree = key_tree->is_ascending ? key_tree->right : key_tree->left;
return get_quick_keys(param,quick,key,key_tree->right, if (next_tree != &null_element)
return get_quick_keys(param,quick,key,next_tree,
min_key,min_key_flag, min_key,min_key_flag,
max_key,max_key_flag); max_key,max_key_flag);
return 0; return 0;
......
...@@ -54,6 +54,33 @@ struct KEY_PART { ...@@ -54,6 +54,33 @@ struct KEY_PART {
}; };
/**
A helper function to invert min flags to max flags for DESC key parts.
It changes NEAR_MIN, NO_MIN_RANGE to NEAR_MAX, NO_MAX_RANGE appropriately
*/
inline uint invert_min_flag(uint min_flag)
{
uint max_flag_out = min_flag & ~(NEAR_MIN | NO_MIN_RANGE);
if (min_flag & NEAR_MIN) max_flag_out |= NEAR_MAX;
if (min_flag & NO_MIN_RANGE) max_flag_out |= NO_MAX_RANGE;
return max_flag_out;
}
/**
A helper function to invert max flags to min flags for DESC key parts.
It changes NEAR_MAX, NO_MAX_RANGE to NEAR_MIN, NO_MIN_RANGE appropriately
*/
inline uint invert_max_flag(uint max_flag)
{
uint min_flag_out = max_flag & ~(NEAR_MAX | NO_MAX_RANGE);
if (max_flag & NEAR_MAX) min_flag_out |= NEAR_MIN;
if (max_flag & NO_MAX_RANGE) min_flag_out |= NO_MIN_RANGE;
return min_flag_out;
}
class RANGE_OPT_PARAM; class RANGE_OPT_PARAM;
/* /*
A construction block of the SEL_ARG-graph. A construction block of the SEL_ARG-graph.
...@@ -267,6 +294,8 @@ class RANGE_OPT_PARAM; ...@@ -267,6 +294,8 @@ class RANGE_OPT_PARAM;
- it is a lot easier to compute than computing the number of ranges, - it is a lot easier to compute than computing the number of ranges,
- it can be updated incrementally when performing AND/OR operations on - it can be updated incrementally when performing AND/OR operations on
parts of the graph. parts of the graph.
6. For handling DESC keyparts, See HowRangeOptimizerHandlesDescKeyparts
*/ */
class SEL_ARG :public Sql_alloc class SEL_ARG :public Sql_alloc
...@@ -277,6 +306,11 @@ class SEL_ARG :public Sql_alloc ...@@ -277,6 +306,11 @@ class SEL_ARG :public Sql_alloc
uint8 min_flag,max_flag,maybe_flag; uint8 min_flag,max_flag,maybe_flag;
uint8 part; // Which key part uint8 part; // Which key part
uint8 maybe_null; uint8 maybe_null;
/*
Whether the keypart is ascending or descending.
See HowRangeOptimizerHandlesDescKeyparts for details.
*/
uint8 is_ascending;
/* /*
The ordinal number the least significant component encountered in The ordinal number the least significant component encountered in
the ranges of the SEL_ARG tree (the first component has number 1) the ranges of the SEL_ARG tree (the first component has number 1)
...@@ -327,11 +361,15 @@ class SEL_ARG :public Sql_alloc ...@@ -327,11 +361,15 @@ class SEL_ARG :public Sql_alloc
SEL_ARG() {} SEL_ARG() {}
SEL_ARG(SEL_ARG &); SEL_ARG(SEL_ARG &);
SEL_ARG(Field *,const uchar *, const uchar *); SEL_ARG(Field *, bool is_asc, const uchar *, const uchar *);
SEL_ARG(Field *field, uint8 part, uchar *min_value, uchar *max_value, SEL_ARG(Field *field, uint8 part, bool is_asc,
uchar *min_value, uchar *max_value,
uint8 min_flag, uint8 max_flag, uint8 maybe_flag); uint8 min_flag, uint8 max_flag, uint8 maybe_flag);
/* This is used to construct degenerate SEL_ARGS like ALWAYS, IMPOSSIBLE, etc */
SEL_ARG(enum Type type_arg) SEL_ARG(enum Type type_arg)
:min_flag(0), max_part_no(0) /* first key part means 1. 0 mean 'no parts'*/, :min_flag(0), is_ascending(false),
max_part_no(0) /* first key part means 1. 0 mean 'no parts'*/,
elements(1),use_count(1),left(0),right(0), elements(1),use_count(1),left(0),right(0),
next_key_part(0), color(BLACK), type(type_arg), weight(1) next_key_part(0), color(BLACK), type(type_arg), weight(1)
{} {}
...@@ -409,19 +447,20 @@ class SEL_ARG :public Sql_alloc ...@@ -409,19 +447,20 @@ class SEL_ARG :public Sql_alloc
{ {
new_max=arg->max_value; flag_max=arg->max_flag; new_max=arg->max_value; flag_max=arg->max_flag;
} }
return new (thd->mem_root) SEL_ARG(field, part, new_min, new_max, flag_min, return new (thd->mem_root) SEL_ARG(field, part, is_ascending,
new_min, new_max, flag_min,
flag_max, flag_max,
MY_TEST(maybe_flag && arg->maybe_flag)); MY_TEST(maybe_flag && arg->maybe_flag));
} }
SEL_ARG *clone_first(SEL_ARG *arg) SEL_ARG *clone_first(SEL_ARG *arg)
{ // min <= X < arg->min { // min <= X < arg->min
return new SEL_ARG(field,part, min_value, arg->min_value, return new SEL_ARG(field, part, is_ascending, min_value, arg->min_value,
min_flag, arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX, min_flag, arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX,
maybe_flag | arg->maybe_flag); maybe_flag | arg->maybe_flag);
} }
SEL_ARG *clone_last(SEL_ARG *arg) SEL_ARG *clone_last(SEL_ARG *arg)
{ // min <= X <= key_max { // min <= X <= key_max
return new SEL_ARG(field, part, min_value, arg->max_value, return new SEL_ARG(field, part, is_ascending, min_value, arg->max_value,
min_flag, arg->max_flag, maybe_flag | arg->maybe_flag); min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
} }
SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next); SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
...@@ -504,6 +543,56 @@ class SEL_ARG :public Sql_alloc ...@@ -504,6 +543,56 @@ class SEL_ARG :public Sql_alloc
return 0; return 0;
} }
/* Save minimum and maximum, taking index order into account */
void store_min_max(uint length,
uchar **min_key, uint min_flag,
uchar **max_key, uint max_flag,
int *min_part, int *max_part)
{
if (is_ascending) {
*min_part += store_min(length, min_key, min_flag);
*max_part += store_max(length, max_key, max_flag);
} else {
*max_part += store_min(length, max_key, min_flag);
*min_part += store_max(length, min_key, max_flag);
}
}
/*
Get the flag for range's starting endpoint, taking index order into
account.
*/
uint get_min_flag()
{
return (is_ascending ? min_flag : invert_max_flag(max_flag));
}
/*
Get the flag for range's starting endpoint, taking index order into
account.
*/
uint get_max_flag()
{
return (is_ascending ? max_flag : invert_min_flag(min_flag));
}
/* Get the previous interval, taking index order into account */
inline SEL_ARG* index_order_prev()
{
return is_ascending? prev: next;
}
/* Get the next interval, taking index order into account */
inline SEL_ARG* index_order_next()
{
return is_ascending? next: prev;
}
/*
Produce a single multi-part interval, taking key part ordering into
account.
*/
void store_next_min_max_keys(KEY_PART *key, uchar **cur_min_key,
uint *cur_min_flag, uchar **cur_max_key,
uint *cur_max_flag, int *min_part,
int *max_part);
/* /*
Returns a number of keypart values appended to the key buffer Returns a number of keypart values appended to the key buffer
for min key and max key. This function is used by both Range for min key and max key. This function is used by both Range
...@@ -516,7 +605,8 @@ class SEL_ARG :public Sql_alloc ...@@ -516,7 +605,8 @@ class SEL_ARG :public Sql_alloc
int store_min_key(KEY_PART *key, int store_min_key(KEY_PART *key,
uchar **range_key, uchar **range_key,
uint *range_key_flag, uint *range_key_flag,
uint last_part) uint last_part,
bool start_key)
{ {
SEL_ARG *key_tree= first(); SEL_ARG *key_tree= first();
uint res= key_tree->store_min(key[key_tree->part].store_length, uint res= key_tree->store_min(key[key_tree->part].store_length,
...@@ -525,15 +615,26 @@ class SEL_ARG :public Sql_alloc ...@@ -525,15 +615,26 @@ class SEL_ARG :public Sql_alloc
if (!res) if (!res)
return 0; return 0;
*range_key_flag|= key_tree->min_flag; *range_key_flag|= key_tree->min_flag;
if (key_tree->next_key_part && SEL_ARG *nkp= key_tree->next_key_part;
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE && if (nkp && nkp->type == SEL_ARG::KEY_RANGE &&
key_tree->part != last_part && key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 && nkp->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN))) !(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)))
res+= key_tree->next_key_part->store_min_key(key, {
range_key, const bool asc = nkp->is_ascending;
range_key_flag, if (start_key == asc)
last_part); {
res+= nkp->store_min_key(key, range_key, range_key_flag, last_part,
start_key);
}
else
{
uint tmp_flag = invert_min_flag(*range_key_flag);
res += nkp->store_max_key(key, range_key, &tmp_flag, last_part,
start_key);
*range_key_flag = invert_max_flag(tmp_flag);
}
}
return res; return res;
} }
...@@ -541,7 +642,8 @@ class SEL_ARG :public Sql_alloc ...@@ -541,7 +642,8 @@ class SEL_ARG :public Sql_alloc
int store_max_key(KEY_PART *key, int store_max_key(KEY_PART *key,
uchar **range_key, uchar **range_key,
uint *range_key_flag, uint *range_key_flag,
uint last_part) uint last_part,
bool start_key)
{ {
SEL_ARG *key_tree= last(); SEL_ARG *key_tree= last();
uint res=key_tree->store_max(key[key_tree->part].store_length, uint res=key_tree->store_max(key[key_tree->part].store_length,
...@@ -549,15 +651,26 @@ class SEL_ARG :public Sql_alloc ...@@ -549,15 +651,26 @@ class SEL_ARG :public Sql_alloc
if (!res) if (!res)
return 0; return 0;
*range_key_flag|= key_tree->max_flag; *range_key_flag|= key_tree->max_flag;
if (key_tree->next_key_part && SEL_ARG *nkp= key_tree->next_key_part;
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE && if (nkp && nkp->type == SEL_ARG::KEY_RANGE &&
key_tree->part != last_part && key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 && nkp->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX))) !(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
res+= key_tree->next_key_part->store_max_key(key, {
range_key, const bool asc = nkp->is_ascending;
range_key_flag, if ((!start_key && asc) || (start_key && !asc))
last_part); {
res += nkp->store_max_key(key, range_key, range_key_flag, last_part,
start_key);
}
else
{
uint tmp_flag = invert_max_flag(*range_key_flag);
res += nkp->store_min_key(key, range_key, &tmp_flag, last_part,
start_key);
*range_key_flag = invert_min_flag(tmp_flag);
}
}
return res; return res;
} }
...@@ -661,13 +774,83 @@ class SEL_ARG :public Sql_alloc ...@@ -661,13 +774,83 @@ class SEL_ARG :public Sql_alloc
SEL_ARG *clone_tree(RANGE_OPT_PARAM *param); SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
}; };
/*
HowRangeOptimizerHandlesDescKeyparts
====================================
Starting with MySQL-8.0 and MariaDB 10.8, index key parts may be descending,
for example:
INDEX idx1(col1, col2 DESC, col3, col4 DESC)
Range Optimizer handles this as follows:
The SEL_ARG object has SEL_ARG::is_ascending which specifies whether the
keypart is ascending.
Other than that, the SEL_ARG graph is built without any regard to DESC
keyparts.
For example, for an index
INDEX idx2(kp1 DESC, kp2)
and range
kp1 BETWEEN 10 and 20 (RANGE-1)
the SEL_ARG will have min_value=10, max_value=20, is_ascending=false.
The ordering of key parts is taken into account when SEL_ARG graph is
linearized to ranges, in sel_arg_range_seq_next() and get_quick_keys().
The storage engine expects the first bound to be the first in the index and
the last bound to be the last, that is, for (RANGE-1) we will flip min and
max and generate these key_range structures:
start.key='20' , end.key='10'
See SEL_ARG::store_min_max(). The flag values are flipped as well, see
SEL_ARG::get_min_flag(), get_max_flag().
== Handling multiple key parts ==
For multi-part keys, the order of key parts has an effect on which ranges are
generated. Consider
kp1 >= 10 AND kp2 >'foo'
for INDEX(kp1 ASC, kp2 ASC) the range will be
(kp1, kp2) > (10, 'foo')
while for INDEX(kp1 ASC, kp2 DESC) it will be just
kp1 >= 10
Another example:
(kp1 BETWEEN 10 AND 20) AND (kp2 BETWEEN 'foo' AND 'quux')
with INDEX (kp1 ASC, kp2 ASC) will generate
(10, 'foo') <= (kp1, kp2) < (20, 'quux')
while with index INDEX (kp1 ASC, kp2 DESC) it will generate
(10, 'quux') <= (kp1, kp2) < (20, 'foo')
This is again achieved by sel_arg_range_seq_next() and get_quick_keys()
flipping SEL_ARG's min,max, their flags and next/prev as needed.
*/
extern MYSQL_PLUGIN_IMPORT SEL_ARG null_element; extern MYSQL_PLUGIN_IMPORT SEL_ARG null_element;
class SEL_ARG_IMPOSSIBLE: public SEL_ARG class SEL_ARG_IMPOSSIBLE: public SEL_ARG
{ {
public: public:
SEL_ARG_IMPOSSIBLE(Field *field) SEL_ARG_IMPOSSIBLE(Field *field)
:SEL_ARG(field, 0, 0) :SEL_ARG(field, false, 0, 0)
{ {
type= SEL_ARG::IMPOSSIBLE; type= SEL_ARG::IMPOSSIBLE;
} }
......
...@@ -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