mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-8610 "WHERE CONTAINS(indexed_geometry_column,1)" causes full table scan
This commit is contained in:
@ -1618,5 +1618,29 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||||||
1 SIMPLE t1 range a a 34 NULL 1 Using where
|
1 SIMPLE t1 range a a 34 NULL 1 Using where
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
#
|
#
|
||||||
|
# MDEV-8610 "WHERE CONTAINS(indexed_geometry_column,1)" causes full table scan
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a GEOMETRY NOT NULL, SPATIAL KEY(a)) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t1 VALUES (Point(1,1)),(Point(2,2)),(Point(3,3));
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1);
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1.0);
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1e0);
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,TIME'00:00:00');
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,DATE'2001-01-01');
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,TIMESTAMP'2001-01-01 00:00:00');
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
# End of 10.1 tests
|
# End of 10.1 tests
|
||||||
#
|
#
|
||||||
|
@ -993,6 +993,20 @@ EXPLAIN SELECT * FROM t1 WHERE MBRINTERSECTS(Point(1,2),a);
|
|||||||
EXPLAIN SELECT * FROM t1 WHERE ST_INTERSECTS(Point(1,2),a);
|
EXPLAIN SELECT * FROM t1 WHERE ST_INTERSECTS(Point(1,2),a);
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-8610 "WHERE CONTAINS(indexed_geometry_column,1)" causes full table scan
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t1 (a GEOMETRY NOT NULL, SPATIAL KEY(a)) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t1 VALUES (Point(1,1)),(Point(2,2)),(Point(3,3));
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1);
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1.0);
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,1e0);
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,TIME'00:00:00');
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,DATE'2001-01-01');
|
||||||
|
EXPLAIN SELECT * FROM t1 WHERE CONTAINS(a,TIMESTAMP'2001-01-01 00:00:00');
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.1 tests
|
--echo # End of 10.1 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "set_var.h"
|
#include "set_var.h"
|
||||||
#ifdef HAVE_SPATIAL
|
#ifdef HAVE_SPATIAL
|
||||||
#include <m_ctype.h>
|
#include <m_ctype.h>
|
||||||
|
#include "opt_range.h"
|
||||||
|
|
||||||
|
|
||||||
Field *Item_geometry_func::tmp_table_field(TABLE *t_arg)
|
Field *Item_geometry_func::tmp_table_field(TABLE *t_arg)
|
||||||
@ -929,6 +930,78 @@ err:
|
|||||||
Functions for spatial relations
|
Functions for spatial relations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static SEL_ARG sel_arg_impossible(SEL_ARG::IMPOSSIBLE);
|
||||||
|
|
||||||
|
SEL_ARG *
|
||||||
|
Item_func_spatial_rel::get_mm_leaf(RANGE_OPT_PARAM *param,
|
||||||
|
Field *field, KEY_PART *key_part,
|
||||||
|
Item_func::Functype type, Item *value)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("Item_func_spatial_rel::get_mm_leaf");
|
||||||
|
if (key_part->image_type != Field::itMBR)
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
if (value->cmp_type() != STRING_RESULT)
|
||||||
|
DBUG_RETURN(&sel_arg_impossible);
|
||||||
|
|
||||||
|
if (param->using_real_indexes &&
|
||||||
|
!field->optimize_range(param->real_keynr[key_part->key],
|
||||||
|
key_part->part))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
|
if (value->save_in_field_no_warnings(field, 1))
|
||||||
|
DBUG_RETURN(&sel_arg_impossible); // Bad GEOMETRY value
|
||||||
|
|
||||||
|
DBUG_ASSERT(!field->real_maybe_null()); // SPATIAL keys do not support NULL
|
||||||
|
|
||||||
|
uchar *str= (uchar*) alloc_root(param->mem_root, key_part->store_length + 1);
|
||||||
|
if (!str)
|
||||||
|
DBUG_RETURN(0); // out of memory
|
||||||
|
field->get_key_image(str, key_part->length, key_part->image_type);
|
||||||
|
SEL_ARG *tree;
|
||||||
|
if (!(tree= new (param->mem_root) SEL_ARG(field, str, str)))
|
||||||
|
DBUG_RETURN(0); // out of memory
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case SP_EQUALS_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_EQUAL;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_DISJOINT_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_DISJOINT;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_INTERSECTS_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_TOUCHES_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_CROSSES_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_WITHIN_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_WITHIN;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_CONTAINS_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_CONTAIN;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
case SP_OVERLAPS_FUNC:
|
||||||
|
tree->min_flag= GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
||||||
|
tree->max_flag= NO_MAX_RANGE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DBUG_RETURN(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const char *Item_func_spatial_mbr_rel::func_name() const
|
const char *Item_func_spatial_mbr_rel::func_name() const
|
||||||
{
|
{
|
||||||
switch (spatial_rel) {
|
switch (spatial_rel) {
|
||||||
|
@ -277,6 +277,9 @@ class Item_func_spatial_rel: public Item_bool_func2
|
|||||||
protected:
|
protected:
|
||||||
enum Functype spatial_rel;
|
enum Functype spatial_rel;
|
||||||
String tmp_value1, tmp_value2;
|
String tmp_value1, tmp_value2;
|
||||||
|
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field,
|
||||||
|
KEY_PART *key_part,
|
||||||
|
Item_func::Functype type, Item *value);
|
||||||
public:
|
public:
|
||||||
Item_func_spatial_rel(Item *a, Item *b, enum Functype sp_rel)
|
Item_func_spatial_rel(Item *a, Item *b, enum Functype sp_rel)
|
||||||
:Item_bool_func2(a, b), spatial_rel(sp_rel)
|
:Item_bool_func2(a, b), spatial_rel(sp_rel)
|
||||||
|
668
sql/opt_range.cc
668
sql/opt_range.cc
@ -132,8 +132,6 @@
|
|||||||
*/
|
*/
|
||||||
#define double2rows(x) ((ha_rows)(x))
|
#define double2rows(x) ((ha_rows)(x))
|
||||||
|
|
||||||
static int sel_cmp(Field *f,uchar *a,uchar *b,uint8 a_flag,uint8 b_flag);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this should be long enough so that any memcmp with a string that
|
this should be long enough so that any memcmp with a string that
|
||||||
starts from '\0' won't cross is_null_string boundaries, even
|
starts from '\0' won't cross is_null_string boundaries, even
|
||||||
@ -141,543 +139,6 @@ static int sel_cmp(Field *f,uchar *a,uchar *b,uint8 a_flag,uint8 b_flag);
|
|||||||
*/
|
*/
|
||||||
static uchar is_null_string[20]= {1,0};
|
static uchar is_null_string[20]= {1,0};
|
||||||
|
|
||||||
class RANGE_OPT_PARAM;
|
|
||||||
/*
|
|
||||||
A construction block of the SEL_ARG-graph.
|
|
||||||
|
|
||||||
The following description only covers graphs of SEL_ARG objects with
|
|
||||||
sel_arg->type==KEY_RANGE:
|
|
||||||
|
|
||||||
One SEL_ARG object represents an "elementary interval" in form
|
|
||||||
|
|
||||||
min_value <=? table.keypartX <=? max_value
|
|
||||||
|
|
||||||
The interval is a non-empty interval of any kind: with[out] minimum/maximum
|
|
||||||
bound, [half]open/closed, single-point interval, etc.
|
|
||||||
|
|
||||||
1. SEL_ARG GRAPH STRUCTURE
|
|
||||||
|
|
||||||
SEL_ARG objects are linked together in a graph. The meaning of the graph
|
|
||||||
is better demostrated by an example:
|
|
||||||
|
|
||||||
tree->keys[i]
|
|
||||||
|
|
|
||||||
| $ $
|
|
||||||
| part=1 $ part=2 $ part=3
|
|
||||||
| $ $
|
|
||||||
| +-------+ $ +-------+ $ +--------+
|
|
||||||
| | kp1<1 |--$-->| kp2=5 |--$-->| kp3=10 |
|
|
||||||
| +-------+ $ +-------+ $ +--------+
|
|
||||||
| | $ $ |
|
|
||||||
| | $ $ +--------+
|
|
||||||
| | $ $ | kp3=12 |
|
|
||||||
| | $ $ +--------+
|
|
||||||
| +-------+ $ $
|
|
||||||
\->| kp1=2 |--$--------------$-+
|
|
||||||
+-------+ $ $ | +--------+
|
|
||||||
| $ $ ==>| kp3=11 |
|
|
||||||
+-------+ $ $ | +--------+
|
|
||||||
| kp1=3 |--$--------------$-+ |
|
|
||||||
+-------+ $ $ +--------+
|
|
||||||
| $ $ | kp3=14 |
|
|
||||||
... $ $ +--------+
|
|
||||||
|
|
||||||
The entire graph is partitioned into "interval lists".
|
|
||||||
|
|
||||||
An interval list is a sequence of ordered disjoint intervals over the same
|
|
||||||
key part. SEL_ARG are linked via "next" and "prev" pointers. Additionally,
|
|
||||||
all intervals in the list form an RB-tree, linked via left/right/parent
|
|
||||||
pointers. The RB-tree root SEL_ARG object will be further called "root of the
|
|
||||||
interval list".
|
|
||||||
|
|
||||||
In the example pic, there are 4 interval lists:
|
|
||||||
"kp<1 OR kp1=2 OR kp1=3", "kp2=5", "kp3=10 OR kp3=12", "kp3=11 OR kp3=13".
|
|
||||||
The vertical lines represent SEL_ARG::next/prev pointers.
|
|
||||||
|
|
||||||
In an interval list, each member X may have SEL_ARG::next_key_part pointer
|
|
||||||
pointing to the root of another interval list Y. The pointed interval list
|
|
||||||
must cover a key part with greater number (i.e. Y->part > X->part).
|
|
||||||
|
|
||||||
In the example pic, the next_key_part pointers are represented by
|
|
||||||
horisontal lines.
|
|
||||||
|
|
||||||
2. SEL_ARG GRAPH SEMANTICS
|
|
||||||
|
|
||||||
It represents a condition in a special form (we don't have a name for it ATM)
|
|
||||||
The SEL_ARG::next/prev is "OR", and next_key_part is "AND".
|
|
||||||
|
|
||||||
For example, the picture represents the condition in form:
|
|
||||||
(kp1 < 1 AND kp2=5 AND (kp3=10 OR kp3=12)) OR
|
|
||||||
(kp1=2 AND (kp3=11 OR kp3=14)) OR
|
|
||||||
(kp1=3 AND (kp3=11 OR kp3=14))
|
|
||||||
|
|
||||||
|
|
||||||
3. SEL_ARG GRAPH USE
|
|
||||||
|
|
||||||
Use get_mm_tree() to construct SEL_ARG graph from WHERE condition.
|
|
||||||
Then walk the SEL_ARG graph and get a list of dijsoint ordered key
|
|
||||||
intervals (i.e. intervals in form
|
|
||||||
|
|
||||||
(constA1, .., const1_K) < (keypart1,.., keypartK) < (constB1, .., constB_K)
|
|
||||||
|
|
||||||
Those intervals can be used to access the index. The uses are in:
|
|
||||||
- check_quick_select() - Walk the SEL_ARG graph and find an estimate of
|
|
||||||
how many table records are contained within all
|
|
||||||
intervals.
|
|
||||||
- get_quick_select() - Walk the SEL_ARG, materialize the key intervals,
|
|
||||||
and create QUICK_RANGE_SELECT object that will
|
|
||||||
read records within these intervals.
|
|
||||||
|
|
||||||
4. SPACE COMPLEXITY NOTES
|
|
||||||
|
|
||||||
SEL_ARG graph is a representation of an ordered disjoint sequence of
|
|
||||||
intervals over the ordered set of index tuple values.
|
|
||||||
|
|
||||||
For multi-part keys, one can construct a WHERE expression such that its
|
|
||||||
list of intervals will be of combinatorial size. Here is an example:
|
|
||||||
|
|
||||||
(keypart1 IN (1,2, ..., n1)) AND
|
|
||||||
(keypart2 IN (1,2, ..., n2)) AND
|
|
||||||
(keypart3 IN (1,2, ..., n3))
|
|
||||||
|
|
||||||
For this WHERE clause the list of intervals will have n1*n2*n3 intervals
|
|
||||||
of form
|
|
||||||
|
|
||||||
(keypart1, keypart2, keypart3) = (k1, k2, k3), where 1 <= k{i} <= n{i}
|
|
||||||
|
|
||||||
SEL_ARG graph structure aims to reduce the amount of required space by
|
|
||||||
"sharing" the elementary intervals when possible (the pic at the
|
|
||||||
beginning of this comment has examples of such sharing). The sharing may
|
|
||||||
prevent combinatorial blowup:
|
|
||||||
|
|
||||||
There are WHERE clauses that have combinatorial-size interval lists but
|
|
||||||
will be represented by a compact SEL_ARG graph.
|
|
||||||
Example:
|
|
||||||
(keypartN IN (1,2, ..., n1)) AND
|
|
||||||
...
|
|
||||||
(keypart2 IN (1,2, ..., n2)) AND
|
|
||||||
(keypart1 IN (1,2, ..., n3))
|
|
||||||
|
|
||||||
but not in all cases:
|
|
||||||
|
|
||||||
- There are WHERE clauses that do have a compact SEL_ARG-graph
|
|
||||||
representation but get_mm_tree() and its callees will construct a
|
|
||||||
graph of combinatorial size.
|
|
||||||
Example:
|
|
||||||
(keypart1 IN (1,2, ..., n1)) AND
|
|
||||||
(keypart2 IN (1,2, ..., n2)) AND
|
|
||||||
...
|
|
||||||
(keypartN IN (1,2, ..., n3))
|
|
||||||
|
|
||||||
- There are WHERE clauses for which the minimal possible SEL_ARG graph
|
|
||||||
representation will have combinatorial size.
|
|
||||||
Example:
|
|
||||||
By induction: Let's take any interval on some keypart in the middle:
|
|
||||||
|
|
||||||
kp15=c0
|
|
||||||
|
|
||||||
Then let's AND it with this interval 'structure' from preceding and
|
|
||||||
following keyparts:
|
|
||||||
|
|
||||||
(kp14=c1 AND kp16=c3) OR keypart14=c2) (*)
|
|
||||||
|
|
||||||
We will obtain this SEL_ARG graph:
|
|
||||||
|
|
||||||
kp14 $ kp15 $ kp16
|
|
||||||
$ $
|
|
||||||
+---------+ $ +---------+ $ +---------+
|
|
||||||
| kp14=c1 |--$-->| kp15=c0 |--$-->| kp16=c3 |
|
|
||||||
+---------+ $ +---------+ $ +---------+
|
|
||||||
| $ $
|
|
||||||
+---------+ $ +---------+ $
|
|
||||||
| kp14=c2 |--$-->| kp15=c0 | $
|
|
||||||
+---------+ $ +---------+ $
|
|
||||||
$ $
|
|
||||||
|
|
||||||
Note that we had to duplicate "kp15=c0" and there was no way to avoid
|
|
||||||
that.
|
|
||||||
The induction step: AND the obtained expression with another "wrapping"
|
|
||||||
expression like (*).
|
|
||||||
When the process ends because of the limit on max. number of keyparts
|
|
||||||
we'll have:
|
|
||||||
|
|
||||||
WHERE clause length is O(3*#max_keyparts)
|
|
||||||
SEL_ARG graph size is O(2^(#max_keyparts/2))
|
|
||||||
|
|
||||||
(it is also possible to construct a case where instead of 2 in 2^n we
|
|
||||||
have a bigger constant, e.g. 4, and get a graph with 4^(31/2)= 2^31
|
|
||||||
nodes)
|
|
||||||
|
|
||||||
We avoid consuming too much memory by setting a limit on the number of
|
|
||||||
SEL_ARG object we can construct during one range analysis invocation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SEL_ARG :public Sql_alloc
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint8 min_flag,max_flag,maybe_flag;
|
|
||||||
uint8 part; // Which key part
|
|
||||||
uint8 maybe_null;
|
|
||||||
/*
|
|
||||||
The ordinal number the least significant component encountered in
|
|
||||||
the ranges of the SEL_ARG tree (the first component has number 1)
|
|
||||||
*/
|
|
||||||
uint16 max_part_no;
|
|
||||||
/*
|
|
||||||
Number of children of this element in the RB-tree, plus 1 for this
|
|
||||||
element itself.
|
|
||||||
*/
|
|
||||||
uint16 elements;
|
|
||||||
/*
|
|
||||||
Valid only for elements which are RB-tree roots: Number of times this
|
|
||||||
RB-tree is referred to (it is referred by SEL_ARG::next_key_part or by
|
|
||||||
SEL_TREE::keys[i] or by a temporary SEL_ARG* variable)
|
|
||||||
*/
|
|
||||||
ulong use_count;
|
|
||||||
|
|
||||||
Field *field;
|
|
||||||
uchar *min_value,*max_value; // Pointer to range
|
|
||||||
|
|
||||||
/*
|
|
||||||
eq_tree() requires that left == right == 0 if the type is MAYBE_KEY.
|
|
||||||
*/
|
|
||||||
SEL_ARG *left,*right; /* R-B tree children */
|
|
||||||
SEL_ARG *next,*prev; /* Links for bi-directional interval list */
|
|
||||||
SEL_ARG *parent; /* R-B tree parent */
|
|
||||||
SEL_ARG *next_key_part;
|
|
||||||
enum leaf_color { BLACK,RED } color;
|
|
||||||
enum Type { IMPOSSIBLE, MAYBE, MAYBE_KEY, KEY_RANGE } type;
|
|
||||||
|
|
||||||
enum { MAX_SEL_ARGS = 16000 };
|
|
||||||
|
|
||||||
SEL_ARG() {}
|
|
||||||
SEL_ARG(SEL_ARG &);
|
|
||||||
SEL_ARG(Field *,const uchar *, const uchar *);
|
|
||||||
SEL_ARG(Field *field, uint8 part, uchar *min_value, uchar *max_value,
|
|
||||||
uint8 min_flag, uint8 max_flag, uint8 maybe_flag);
|
|
||||||
SEL_ARG(enum Type type_arg)
|
|
||||||
:min_flag(0), max_part_no(0) /* first key part means 1. 0 mean 'no parts'*/,
|
|
||||||
elements(1),use_count(1),left(0),right(0),
|
|
||||||
next_key_part(0), color(BLACK), type(type_arg)
|
|
||||||
{}
|
|
||||||
/**
|
|
||||||
returns true if a range predicate is equal. Use all_same()
|
|
||||||
to check for equality of all the predicates on this keypart.
|
|
||||||
*/
|
|
||||||
inline bool is_same(const SEL_ARG *arg) const
|
|
||||||
{
|
|
||||||
if (type != arg->type || part != arg->part)
|
|
||||||
return false;
|
|
||||||
if (type != KEY_RANGE)
|
|
||||||
return true;
|
|
||||||
return cmp_min_to_min(arg) == 0 && cmp_max_to_max(arg) == 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
returns true if all the predicates in the keypart tree are equal
|
|
||||||
*/
|
|
||||||
bool all_same(const SEL_ARG *arg) const
|
|
||||||
{
|
|
||||||
if (type != arg->type || part != arg->part)
|
|
||||||
return false;
|
|
||||||
if (type != KEY_RANGE)
|
|
||||||
return true;
|
|
||||||
if (arg == this)
|
|
||||||
return true;
|
|
||||||
const SEL_ARG *cmp_arg= arg->first();
|
|
||||||
const SEL_ARG *cur_arg= first();
|
|
||||||
for (; cur_arg && cmp_arg && cur_arg->is_same(cmp_arg);
|
|
||||||
cur_arg= cur_arg->next, cmp_arg= cmp_arg->next) ;
|
|
||||||
if (cur_arg || cmp_arg)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; }
|
|
||||||
inline void maybe_smaller() { maybe_flag=1; }
|
|
||||||
/* Return true iff it's a single-point null interval */
|
|
||||||
inline bool is_null_interval() { return maybe_null && max_value[0] == 1; }
|
|
||||||
inline int cmp_min_to_min(const SEL_ARG* arg) const
|
|
||||||
{
|
|
||||||
return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag);
|
|
||||||
}
|
|
||||||
inline int cmp_min_to_max(const SEL_ARG* arg) const
|
|
||||||
{
|
|
||||||
return sel_cmp(field,min_value, arg->max_value, min_flag, arg->max_flag);
|
|
||||||
}
|
|
||||||
inline int cmp_max_to_max(const SEL_ARG* arg) const
|
|
||||||
{
|
|
||||||
return sel_cmp(field,max_value, arg->max_value, max_flag, arg->max_flag);
|
|
||||||
}
|
|
||||||
inline int cmp_max_to_min(const SEL_ARG* arg) const
|
|
||||||
{
|
|
||||||
return sel_cmp(field,max_value, arg->min_value, max_flag, arg->min_flag);
|
|
||||||
}
|
|
||||||
SEL_ARG *clone_and(THD *thd, SEL_ARG* arg)
|
|
||||||
{ // Get overlapping range
|
|
||||||
uchar *new_min,*new_max;
|
|
||||||
uint8 flag_min,flag_max;
|
|
||||||
if (cmp_min_to_min(arg) >= 0)
|
|
||||||
{
|
|
||||||
new_min=min_value; flag_min=min_flag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_min=arg->min_value; flag_min=arg->min_flag; /* purecov: deadcode */
|
|
||||||
}
|
|
||||||
if (cmp_max_to_max(arg) <= 0)
|
|
||||||
{
|
|
||||||
new_max=max_value; flag_max=max_flag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
flag_max,
|
|
||||||
MY_TEST(maybe_flag && arg->maybe_flag));
|
|
||||||
}
|
|
||||||
SEL_ARG *clone_first(SEL_ARG *arg)
|
|
||||||
{ // min <= X < arg->min
|
|
||||||
return new SEL_ARG(field,part, min_value, arg->min_value,
|
|
||||||
min_flag, arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX,
|
|
||||||
maybe_flag | arg->maybe_flag);
|
|
||||||
}
|
|
||||||
SEL_ARG *clone_last(SEL_ARG *arg)
|
|
||||||
{ // min <= X <= key_max
|
|
||||||
return new SEL_ARG(field, part, min_value, arg->max_value,
|
|
||||||
min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
|
|
||||||
}
|
|
||||||
SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
|
|
||||||
|
|
||||||
bool copy_min(SEL_ARG* arg)
|
|
||||||
{ // Get overlapping range
|
|
||||||
if (cmp_min_to_min(arg) > 0)
|
|
||||||
{
|
|
||||||
min_value=arg->min_value; min_flag=arg->min_flag;
|
|
||||||
if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
|
|
||||||
(NO_MAX_RANGE | NO_MIN_RANGE))
|
|
||||||
return 1; // Full range
|
|
||||||
}
|
|
||||||
maybe_flag|=arg->maybe_flag;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
bool copy_max(SEL_ARG* arg)
|
|
||||||
{ // Get overlapping range
|
|
||||||
if (cmp_max_to_max(arg) <= 0)
|
|
||||||
{
|
|
||||||
max_value=arg->max_value; max_flag=arg->max_flag;
|
|
||||||
if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
|
|
||||||
(NO_MAX_RANGE | NO_MIN_RANGE))
|
|
||||||
return 1; // Full range
|
|
||||||
}
|
|
||||||
maybe_flag|=arg->maybe_flag;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy_min_to_min(SEL_ARG *arg)
|
|
||||||
{
|
|
||||||
min_value=arg->min_value; min_flag=arg->min_flag;
|
|
||||||
}
|
|
||||||
void copy_min_to_max(SEL_ARG *arg)
|
|
||||||
{
|
|
||||||
max_value=arg->min_value;
|
|
||||||
max_flag=arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX;
|
|
||||||
}
|
|
||||||
void copy_max_to_min(SEL_ARG *arg)
|
|
||||||
{
|
|
||||||
min_value=arg->max_value;
|
|
||||||
min_flag=arg->max_flag & NEAR_MAX ? 0 : NEAR_MIN;
|
|
||||||
}
|
|
||||||
/* returns a number of keypart values (0 or 1) appended to the key buffer */
|
|
||||||
int store_min(uint length, uchar **min_key,uint min_key_flag)
|
|
||||||
{
|
|
||||||
/* "(kp1 > c1) AND (kp2 OP c2) AND ..." -> (kp1 > c1) */
|
|
||||||
if ((min_flag & GEOM_FLAG) ||
|
|
||||||
(!(min_flag & NO_MIN_RANGE) &&
|
|
||||||
!(min_key_flag & (NO_MIN_RANGE | NEAR_MIN))))
|
|
||||||
{
|
|
||||||
if (maybe_null && *min_value)
|
|
||||||
{
|
|
||||||
**min_key=1;
|
|
||||||
bzero(*min_key+1,length-1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memcpy(*min_key,min_value,length);
|
|
||||||
(*min_key)+= length;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* returns a number of keypart values (0 or 1) appended to the key buffer */
|
|
||||||
int store_max(uint length, uchar **max_key, uint max_key_flag)
|
|
||||||
{
|
|
||||||
if (!(max_flag & NO_MAX_RANGE) &&
|
|
||||||
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
|
|
||||||
{
|
|
||||||
if (maybe_null && *max_value)
|
|
||||||
{
|
|
||||||
**max_key=1;
|
|
||||||
bzero(*max_key+1,length-1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memcpy(*max_key,max_value,length);
|
|
||||||
(*max_key)+= length;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Returns a number of keypart values appended to the key buffer
|
|
||||||
for min key and max key. This function is used by both Range
|
|
||||||
Analysis and Partition pruning. For partition pruning we have
|
|
||||||
to ensure that we don't store also subpartition fields. Thus
|
|
||||||
we have to stop at the last partition part and not step into
|
|
||||||
the subpartition fields. For Range Analysis we set last_part
|
|
||||||
to MAX_KEY which we should never reach.
|
|
||||||
*/
|
|
||||||
int store_min_key(KEY_PART *key,
|
|
||||||
uchar **range_key,
|
|
||||||
uint *range_key_flag,
|
|
||||||
uint last_part)
|
|
||||||
{
|
|
||||||
SEL_ARG *key_tree= first();
|
|
||||||
uint res= key_tree->store_min(key[key_tree->part].store_length,
|
|
||||||
range_key, *range_key_flag);
|
|
||||||
*range_key_flag|= key_tree->min_flag;
|
|
||||||
if (key_tree->next_key_part &&
|
|
||||||
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
|
|
||||||
key_tree->part != last_part &&
|
|
||||||
key_tree->next_key_part->part == key_tree->part+1 &&
|
|
||||||
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)))
|
|
||||||
res+= key_tree->next_key_part->store_min_key(key,
|
|
||||||
range_key,
|
|
||||||
range_key_flag,
|
|
||||||
last_part);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns a number of keypart values appended to the key buffer */
|
|
||||||
int store_max_key(KEY_PART *key,
|
|
||||||
uchar **range_key,
|
|
||||||
uint *range_key_flag,
|
|
||||||
uint last_part)
|
|
||||||
{
|
|
||||||
SEL_ARG *key_tree= last();
|
|
||||||
uint res=key_tree->store_max(key[key_tree->part].store_length,
|
|
||||||
range_key, *range_key_flag);
|
|
||||||
(*range_key_flag)|= key_tree->max_flag;
|
|
||||||
if (key_tree->next_key_part &&
|
|
||||||
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
|
|
||||||
key_tree->part != last_part &&
|
|
||||||
key_tree->next_key_part->part == key_tree->part+1 &&
|
|
||||||
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
|
|
||||||
res+= key_tree->next_key_part->store_max_key(key,
|
|
||||||
range_key,
|
|
||||||
range_key_flag,
|
|
||||||
last_part);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEL_ARG *insert(SEL_ARG *key);
|
|
||||||
SEL_ARG *tree_delete(SEL_ARG *key);
|
|
||||||
SEL_ARG *find_range(SEL_ARG *key);
|
|
||||||
SEL_ARG *rb_insert(SEL_ARG *leaf);
|
|
||||||
friend SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key, SEL_ARG *par);
|
|
||||||
#ifdef EXTRA_DEBUG
|
|
||||||
friend int test_rb_tree(SEL_ARG *element,SEL_ARG *parent);
|
|
||||||
void test_use_count(SEL_ARG *root);
|
|
||||||
#endif
|
|
||||||
SEL_ARG *first();
|
|
||||||
const SEL_ARG *first() const;
|
|
||||||
SEL_ARG *last();
|
|
||||||
void make_root();
|
|
||||||
inline bool simple_key()
|
|
||||||
{
|
|
||||||
return !next_key_part && elements == 1;
|
|
||||||
}
|
|
||||||
void increment_use_count(long count)
|
|
||||||
{
|
|
||||||
if (next_key_part)
|
|
||||||
{
|
|
||||||
next_key_part->use_count+=count;
|
|
||||||
count*= (next_key_part->use_count-count);
|
|
||||||
for (SEL_ARG *pos=next_key_part->first(); pos ; pos=pos->next)
|
|
||||||
if (pos->next_key_part)
|
|
||||||
pos->increment_use_count(count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void incr_refs()
|
|
||||||
{
|
|
||||||
increment_use_count(1);
|
|
||||||
use_count++;
|
|
||||||
}
|
|
||||||
void incr_refs_all()
|
|
||||||
{
|
|
||||||
for (SEL_ARG *pos=first(); pos ; pos=pos->next)
|
|
||||||
{
|
|
||||||
pos->increment_use_count(1);
|
|
||||||
}
|
|
||||||
use_count++;
|
|
||||||
}
|
|
||||||
void free_tree()
|
|
||||||
{
|
|
||||||
for (SEL_ARG *pos=first(); pos ; pos=pos->next)
|
|
||||||
if (pos->next_key_part)
|
|
||||||
{
|
|
||||||
pos->next_key_part->use_count--;
|
|
||||||
pos->next_key_part->free_tree();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline SEL_ARG **parent_ptr()
|
|
||||||
{
|
|
||||||
return parent->left == this ? &parent->left : &parent->right;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check if this SEL_ARG object represents a single-point interval
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
is_singlepoint()
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
Check if this SEL_ARG object (not tree) represents a single-point
|
|
||||||
interval, i.e. if it represents a "keypart = const" or
|
|
||||||
"keypart IS NULL".
|
|
||||||
|
|
||||||
RETURN
|
|
||||||
TRUE This SEL_ARG object represents a singlepoint interval
|
|
||||||
FALSE Otherwise
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool is_singlepoint()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Check for NEAR_MIN ("strictly less") and NO_MIN_RANGE (-inf < field)
|
|
||||||
flags, and the same for right edge.
|
|
||||||
*/
|
|
||||||
if (min_flag || max_flag)
|
|
||||||
return FALSE;
|
|
||||||
uchar *min_val= min_value;
|
|
||||||
uchar *max_val= max_value;
|
|
||||||
|
|
||||||
if (maybe_null)
|
|
||||||
{
|
|
||||||
/* First byte is a NULL value indicator */
|
|
||||||
if (*min_val != *max_val)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (*min_val)
|
|
||||||
return TRUE; /* This "x IS NULL" */
|
|
||||||
min_val++;
|
|
||||||
max_val++;
|
|
||||||
}
|
|
||||||
return !field->key_cmp(min_val, max_val);
|
|
||||||
}
|
|
||||||
SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Helper function to compare two SEL_ARG's.
|
Helper function to compare two SEL_ARG's.
|
||||||
*/
|
*/
|
||||||
@ -832,73 +293,6 @@ public:
|
|||||||
bool without_imerges() { return merges.is_empty(); }
|
bool without_imerges() { return merges.is_empty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class RANGE_OPT_PARAM
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
THD *thd; /* Current thread handle */
|
|
||||||
TABLE *table; /* Table being analyzed */
|
|
||||||
table_map prev_tables;
|
|
||||||
table_map read_tables;
|
|
||||||
table_map current_table; /* Bit of the table being analyzed */
|
|
||||||
|
|
||||||
/* Array of parts of all keys for which range analysis is performed */
|
|
||||||
KEY_PART *key_parts;
|
|
||||||
KEY_PART *key_parts_end;
|
|
||||||
MEM_ROOT *mem_root; /* Memory that will be freed when range analysis completes */
|
|
||||||
MEM_ROOT *old_root; /* Memory that will last until the query end */
|
|
||||||
/*
|
|
||||||
Number of indexes used in range analysis (In SEL_TREE::keys only first
|
|
||||||
#keys elements are not empty)
|
|
||||||
*/
|
|
||||||
uint keys;
|
|
||||||
|
|
||||||
/*
|
|
||||||
If true, the index descriptions describe real indexes (and it is ok to
|
|
||||||
call field->optimize_range(real_keynr[...], ...).
|
|
||||||
Otherwise index description describes fake indexes.
|
|
||||||
*/
|
|
||||||
bool using_real_indexes;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Aggressively remove "scans" that do not have conditions on first
|
|
||||||
keyparts. Such scans are usable when doing partition pruning but not
|
|
||||||
regular range optimization.
|
|
||||||
*/
|
|
||||||
bool remove_jump_scans;
|
|
||||||
|
|
||||||
/*
|
|
||||||
TRUE <=> Range analyzer should remove parts of condition that are found
|
|
||||||
to be always FALSE.
|
|
||||||
*/
|
|
||||||
bool remove_false_where_parts;
|
|
||||||
|
|
||||||
/*
|
|
||||||
used_key_no -> table_key_no translation table. Only makes sense if
|
|
||||||
using_real_indexes==TRUE
|
|
||||||
*/
|
|
||||||
uint real_keynr[MAX_KEY];
|
|
||||||
|
|
||||||
/*
|
|
||||||
Used to store 'current key tuples', in both range analysis and
|
|
||||||
partitioning (list) analysis
|
|
||||||
*/
|
|
||||||
uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
|
|
||||||
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
|
|
||||||
|
|
||||||
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
|
|
||||||
uint alloced_sel_args;
|
|
||||||
|
|
||||||
bool force_default_mrr;
|
|
||||||
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
|
|
||||||
|
|
||||||
bool statement_should_be_aborted() const
|
|
||||||
{
|
|
||||||
return
|
|
||||||
thd->is_fatal_error ||
|
|
||||||
thd->is_error() ||
|
|
||||||
alloced_sel_args > SEL_ARG::MAX_SEL_ARGS;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PARAM : public RANGE_OPT_PARAM
|
class PARAM : public RANGE_OPT_PARAM
|
||||||
{
|
{
|
||||||
@ -2571,8 +1965,8 @@ SEL_ARG *SEL_ARG::last()
|
|||||||
Returns -2 or 2 if the ranges where 'joined' like < 2 and >= 2
|
Returns -2 or 2 if the ranges where 'joined' like < 2 and >= 2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int sel_cmp(Field *field, uchar *a, uchar *b, uint8 a_flag,
|
int SEL_ARG::sel_cmp(Field *field, uchar *a, uchar *b, uint8 a_flag,
|
||||||
uint8 b_flag)
|
uint8 b_flag)
|
||||||
{
|
{
|
||||||
int cmp;
|
int cmp;
|
||||||
/* First check if there was a compare to a min or max element */
|
/* First check if there was a compare to a min or max element */
|
||||||
@ -8410,6 +7804,9 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
|
|||||||
|
|
||||||
DBUG_ASSERT(value); // IS NULL and IS NOT NULL are handled separately
|
DBUG_ASSERT(value); // IS NULL and IS NOT NULL are handled separately
|
||||||
|
|
||||||
|
if (key_part->image_type != Field::itRAW)
|
||||||
|
DBUG_RETURN(0); // e.g. SPATIAL index
|
||||||
|
|
||||||
/*
|
/*
|
||||||
1. Usually we can't use an index if the column collation
|
1. Usually we can't use an index if the column collation
|
||||||
differ from the operation collation.
|
differ from the operation collation.
|
||||||
@ -8425,7 +7822,6 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
|
|||||||
if (field->result_type() == STRING_RESULT &&
|
if (field->result_type() == STRING_RESULT &&
|
||||||
field->match_collation_to_optimize_range() &&
|
field->match_collation_to_optimize_range() &&
|
||||||
value->result_type() == STRING_RESULT &&
|
value->result_type() == STRING_RESULT &&
|
||||||
key_part->image_type == Field::itRAW &&
|
|
||||||
field->charset() != compare_collation() &&
|
field->charset() != compare_collation() &&
|
||||||
!((type == EQUAL_FUNC || type == EQ_FUNC) &&
|
!((type == EQUAL_FUNC || type == EQ_FUNC) &&
|
||||||
compare_collation()->state & MY_CS_BINSORT))
|
compare_collation()->state & MY_CS_BINSORT))
|
||||||
@ -8433,28 +7829,6 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
|
|||||||
if (value->cmp_type() == TIME_RESULT && field->cmp_type() != TIME_RESULT)
|
if (value->cmp_type() == TIME_RESULT && field->cmp_type() != TIME_RESULT)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (key_part->image_type == Field::itMBR)
|
|
||||||
{
|
|
||||||
// @todo: use is_spatial_operator() instead?
|
|
||||||
switch (type) {
|
|
||||||
case SP_EQUALS_FUNC:
|
|
||||||
case SP_DISJOINT_FUNC:
|
|
||||||
case SP_INTERSECTS_FUNC:
|
|
||||||
case SP_TOUCHES_FUNC:
|
|
||||||
case SP_CROSSES_FUNC:
|
|
||||||
case SP_WITHIN_FUNC:
|
|
||||||
case SP_CONTAINS_FUNC:
|
|
||||||
case SP_OVERLAPS_FUNC:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*
|
|
||||||
We cannot involve spatial indexes for queries that
|
|
||||||
don't use MBREQUALS(), MBRDISJOINT(), etc. functions.
|
|
||||||
*/
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param->using_real_indexes &&
|
if (param->using_real_indexes &&
|
||||||
!field->optimize_range(param->real_keynr[key_part->key],
|
!field->optimize_range(param->real_keynr[key_part->key],
|
||||||
key_part->part) &&
|
key_part->part) &&
|
||||||
@ -8632,38 +8006,6 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
|
|||||||
tree->min_flag= NEAR_MIN;
|
tree->min_flag= NEAR_MIN;
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
tree->max_flag=NO_MAX_RANGE;
|
||||||
break;
|
break;
|
||||||
case SP_EQUALS_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_EQUAL;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_DISJOINT_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_DISJOINT;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_INTERSECTS_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_TOUCHES_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_CROSSES_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_WITHIN_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_WITHIN;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_CONTAINS_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_CONTAIN;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case SP_OVERLAPS_FUNC:
|
|
||||||
tree->min_flag=GEOM_FLAG | HA_READ_MBR_INTERSECT;// NEAR_MIN;//512;
|
|
||||||
tree->max_flag=NO_MAX_RANGE;
|
|
||||||
break;
|
|
||||||
case EQ_FUNC:
|
case EQ_FUNC:
|
||||||
case EQUAL_FUNC:
|
case EQUAL_FUNC:
|
||||||
break;
|
break;
|
||||||
|
611
sql/opt_range.h
611
sql/opt_range.h
@ -52,6 +52,616 @@ struct KEY_PART {
|
|||||||
Field::imagetype image_type;
|
Field::imagetype image_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RANGE_OPT_PARAM;
|
||||||
|
/*
|
||||||
|
A construction block of the SEL_ARG-graph.
|
||||||
|
|
||||||
|
The following description only covers graphs of SEL_ARG objects with
|
||||||
|
sel_arg->type==KEY_RANGE:
|
||||||
|
|
||||||
|
One SEL_ARG object represents an "elementary interval" in form
|
||||||
|
|
||||||
|
min_value <=? table.keypartX <=? max_value
|
||||||
|
|
||||||
|
The interval is a non-empty interval of any kind: with[out] minimum/maximum
|
||||||
|
bound, [half]open/closed, single-point interval, etc.
|
||||||
|
|
||||||
|
1. SEL_ARG GRAPH STRUCTURE
|
||||||
|
|
||||||
|
SEL_ARG objects are linked together in a graph. The meaning of the graph
|
||||||
|
is better demostrated by an example:
|
||||||
|
|
||||||
|
tree->keys[i]
|
||||||
|
|
|
||||||
|
| $ $
|
||||||
|
| part=1 $ part=2 $ part=3
|
||||||
|
| $ $
|
||||||
|
| +-------+ $ +-------+ $ +--------+
|
||||||
|
| | kp1<1 |--$-->| kp2=5 |--$-->| kp3=10 |
|
||||||
|
| +-------+ $ +-------+ $ +--------+
|
||||||
|
| | $ $ |
|
||||||
|
| | $ $ +--------+
|
||||||
|
| | $ $ | kp3=12 |
|
||||||
|
| | $ $ +--------+
|
||||||
|
| +-------+ $ $
|
||||||
|
\->| kp1=2 |--$--------------$-+
|
||||||
|
+-------+ $ $ | +--------+
|
||||||
|
| $ $ ==>| kp3=11 |
|
||||||
|
+-------+ $ $ | +--------+
|
||||||
|
| kp1=3 |--$--------------$-+ |
|
||||||
|
+-------+ $ $ +--------+
|
||||||
|
| $ $ | kp3=14 |
|
||||||
|
... $ $ +--------+
|
||||||
|
|
||||||
|
The entire graph is partitioned into "interval lists".
|
||||||
|
|
||||||
|
An interval list is a sequence of ordered disjoint intervals over the same
|
||||||
|
key part. SEL_ARG are linked via "next" and "prev" pointers. Additionally,
|
||||||
|
all intervals in the list form an RB-tree, linked via left/right/parent
|
||||||
|
pointers. The RB-tree root SEL_ARG object will be further called "root of the
|
||||||
|
interval list".
|
||||||
|
|
||||||
|
In the example pic, there are 4 interval lists:
|
||||||
|
"kp<1 OR kp1=2 OR kp1=3", "kp2=5", "kp3=10 OR kp3=12", "kp3=11 OR kp3=13".
|
||||||
|
The vertical lines represent SEL_ARG::next/prev pointers.
|
||||||
|
|
||||||
|
In an interval list, each member X may have SEL_ARG::next_key_part pointer
|
||||||
|
pointing to the root of another interval list Y. The pointed interval list
|
||||||
|
must cover a key part with greater number (i.e. Y->part > X->part).
|
||||||
|
|
||||||
|
In the example pic, the next_key_part pointers are represented by
|
||||||
|
horisontal lines.
|
||||||
|
|
||||||
|
2. SEL_ARG GRAPH SEMANTICS
|
||||||
|
|
||||||
|
It represents a condition in a special form (we don't have a name for it ATM)
|
||||||
|
The SEL_ARG::next/prev is "OR", and next_key_part is "AND".
|
||||||
|
|
||||||
|
For example, the picture represents the condition in form:
|
||||||
|
(kp1 < 1 AND kp2=5 AND (kp3=10 OR kp3=12)) OR
|
||||||
|
(kp1=2 AND (kp3=11 OR kp3=14)) OR
|
||||||
|
(kp1=3 AND (kp3=11 OR kp3=14))
|
||||||
|
|
||||||
|
|
||||||
|
3. SEL_ARG GRAPH USE
|
||||||
|
|
||||||
|
Use get_mm_tree() to construct SEL_ARG graph from WHERE condition.
|
||||||
|
Then walk the SEL_ARG graph and get a list of dijsoint ordered key
|
||||||
|
intervals (i.e. intervals in form
|
||||||
|
|
||||||
|
(constA1, .., const1_K) < (keypart1,.., keypartK) < (constB1, .., constB_K)
|
||||||
|
|
||||||
|
Those intervals can be used to access the index. The uses are in:
|
||||||
|
- check_quick_select() - Walk the SEL_ARG graph and find an estimate of
|
||||||
|
how many table records are contained within all
|
||||||
|
intervals.
|
||||||
|
- get_quick_select() - Walk the SEL_ARG, materialize the key intervals,
|
||||||
|
and create QUICK_RANGE_SELECT object that will
|
||||||
|
read records within these intervals.
|
||||||
|
|
||||||
|
4. SPACE COMPLEXITY NOTES
|
||||||
|
|
||||||
|
SEL_ARG graph is a representation of an ordered disjoint sequence of
|
||||||
|
intervals over the ordered set of index tuple values.
|
||||||
|
|
||||||
|
For multi-part keys, one can construct a WHERE expression such that its
|
||||||
|
list of intervals will be of combinatorial size. Here is an example:
|
||||||
|
|
||||||
|
(keypart1 IN (1,2, ..., n1)) AND
|
||||||
|
(keypart2 IN (1,2, ..., n2)) AND
|
||||||
|
(keypart3 IN (1,2, ..., n3))
|
||||||
|
|
||||||
|
For this WHERE clause the list of intervals will have n1*n2*n3 intervals
|
||||||
|
of form
|
||||||
|
|
||||||
|
(keypart1, keypart2, keypart3) = (k1, k2, k3), where 1 <= k{i} <= n{i}
|
||||||
|
|
||||||
|
SEL_ARG graph structure aims to reduce the amount of required space by
|
||||||
|
"sharing" the elementary intervals when possible (the pic at the
|
||||||
|
beginning of this comment has examples of such sharing). The sharing may
|
||||||
|
prevent combinatorial blowup:
|
||||||
|
|
||||||
|
There are WHERE clauses that have combinatorial-size interval lists but
|
||||||
|
will be represented by a compact SEL_ARG graph.
|
||||||
|
Example:
|
||||||
|
(keypartN IN (1,2, ..., n1)) AND
|
||||||
|
...
|
||||||
|
(keypart2 IN (1,2, ..., n2)) AND
|
||||||
|
(keypart1 IN (1,2, ..., n3))
|
||||||
|
|
||||||
|
but not in all cases:
|
||||||
|
|
||||||
|
- There are WHERE clauses that do have a compact SEL_ARG-graph
|
||||||
|
representation but get_mm_tree() and its callees will construct a
|
||||||
|
graph of combinatorial size.
|
||||||
|
Example:
|
||||||
|
(keypart1 IN (1,2, ..., n1)) AND
|
||||||
|
(keypart2 IN (1,2, ..., n2)) AND
|
||||||
|
...
|
||||||
|
(keypartN IN (1,2, ..., n3))
|
||||||
|
|
||||||
|
- There are WHERE clauses for which the minimal possible SEL_ARG graph
|
||||||
|
representation will have combinatorial size.
|
||||||
|
Example:
|
||||||
|
By induction: Let's take any interval on some keypart in the middle:
|
||||||
|
|
||||||
|
kp15=c0
|
||||||
|
|
||||||
|
Then let's AND it with this interval 'structure' from preceding and
|
||||||
|
following keyparts:
|
||||||
|
|
||||||
|
(kp14=c1 AND kp16=c3) OR keypart14=c2) (*)
|
||||||
|
|
||||||
|
We will obtain this SEL_ARG graph:
|
||||||
|
|
||||||
|
kp14 $ kp15 $ kp16
|
||||||
|
$ $
|
||||||
|
+---------+ $ +---------+ $ +---------+
|
||||||
|
| kp14=c1 |--$-->| kp15=c0 |--$-->| kp16=c3 |
|
||||||
|
+---------+ $ +---------+ $ +---------+
|
||||||
|
| $ $
|
||||||
|
+---------+ $ +---------+ $
|
||||||
|
| kp14=c2 |--$-->| kp15=c0 | $
|
||||||
|
+---------+ $ +---------+ $
|
||||||
|
$ $
|
||||||
|
|
||||||
|
Note that we had to duplicate "kp15=c0" and there was no way to avoid
|
||||||
|
that.
|
||||||
|
The induction step: AND the obtained expression with another "wrapping"
|
||||||
|
expression like (*).
|
||||||
|
When the process ends because of the limit on max. number of keyparts
|
||||||
|
we'll have:
|
||||||
|
|
||||||
|
WHERE clause length is O(3*#max_keyparts)
|
||||||
|
SEL_ARG graph size is O(2^(#max_keyparts/2))
|
||||||
|
|
||||||
|
(it is also possible to construct a case where instead of 2 in 2^n we
|
||||||
|
have a bigger constant, e.g. 4, and get a graph with 4^(31/2)= 2^31
|
||||||
|
nodes)
|
||||||
|
|
||||||
|
We avoid consuming too much memory by setting a limit on the number of
|
||||||
|
SEL_ARG object we can construct during one range analysis invocation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SEL_ARG :public Sql_alloc
|
||||||
|
{
|
||||||
|
static int sel_cmp(Field *field, uchar *a, uchar *b, uint8 a_flag,
|
||||||
|
uint8 b_flag);
|
||||||
|
public:
|
||||||
|
uint8 min_flag,max_flag,maybe_flag;
|
||||||
|
uint8 part; // Which key part
|
||||||
|
uint8 maybe_null;
|
||||||
|
/*
|
||||||
|
The ordinal number the least significant component encountered in
|
||||||
|
the ranges of the SEL_ARG tree (the first component has number 1)
|
||||||
|
*/
|
||||||
|
uint16 max_part_no;
|
||||||
|
/*
|
||||||
|
Number of children of this element in the RB-tree, plus 1 for this
|
||||||
|
element itself.
|
||||||
|
*/
|
||||||
|
uint16 elements;
|
||||||
|
/*
|
||||||
|
Valid only for elements which are RB-tree roots: Number of times this
|
||||||
|
RB-tree is referred to (it is referred by SEL_ARG::next_key_part or by
|
||||||
|
SEL_TREE::keys[i] or by a temporary SEL_ARG* variable)
|
||||||
|
*/
|
||||||
|
ulong use_count;
|
||||||
|
|
||||||
|
Field *field;
|
||||||
|
uchar *min_value,*max_value; // Pointer to range
|
||||||
|
|
||||||
|
/*
|
||||||
|
eq_tree() requires that left == right == 0 if the type is MAYBE_KEY.
|
||||||
|
*/
|
||||||
|
SEL_ARG *left,*right; /* R-B tree children */
|
||||||
|
SEL_ARG *next,*prev; /* Links for bi-directional interval list */
|
||||||
|
SEL_ARG *parent; /* R-B tree parent */
|
||||||
|
SEL_ARG *next_key_part;
|
||||||
|
enum leaf_color { BLACK,RED } color;
|
||||||
|
enum Type { IMPOSSIBLE, MAYBE, MAYBE_KEY, KEY_RANGE } type;
|
||||||
|
|
||||||
|
enum { MAX_SEL_ARGS = 16000 };
|
||||||
|
|
||||||
|
SEL_ARG() {}
|
||||||
|
SEL_ARG(SEL_ARG &);
|
||||||
|
SEL_ARG(Field *,const uchar *, const uchar *);
|
||||||
|
SEL_ARG(Field *field, uint8 part, uchar *min_value, uchar *max_value,
|
||||||
|
uint8 min_flag, uint8 max_flag, uint8 maybe_flag);
|
||||||
|
SEL_ARG(enum Type type_arg)
|
||||||
|
:min_flag(0), max_part_no(0) /* first key part means 1. 0 mean 'no parts'*/,
|
||||||
|
elements(1),use_count(1),left(0),right(0),
|
||||||
|
next_key_part(0), color(BLACK), type(type_arg)
|
||||||
|
{}
|
||||||
|
/**
|
||||||
|
returns true if a range predicate is equal. Use all_same()
|
||||||
|
to check for equality of all the predicates on this keypart.
|
||||||
|
*/
|
||||||
|
inline bool is_same(const SEL_ARG *arg) const
|
||||||
|
{
|
||||||
|
if (type != arg->type || part != arg->part)
|
||||||
|
return false;
|
||||||
|
if (type != KEY_RANGE)
|
||||||
|
return true;
|
||||||
|
return cmp_min_to_min(arg) == 0 && cmp_max_to_max(arg) == 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
returns true if all the predicates in the keypart tree are equal
|
||||||
|
*/
|
||||||
|
bool all_same(const SEL_ARG *arg) const
|
||||||
|
{
|
||||||
|
if (type != arg->type || part != arg->part)
|
||||||
|
return false;
|
||||||
|
if (type != KEY_RANGE)
|
||||||
|
return true;
|
||||||
|
if (arg == this)
|
||||||
|
return true;
|
||||||
|
const SEL_ARG *cmp_arg= arg->first();
|
||||||
|
const SEL_ARG *cur_arg= first();
|
||||||
|
for (; cur_arg && cmp_arg && cur_arg->is_same(cmp_arg);
|
||||||
|
cur_arg= cur_arg->next, cmp_arg= cmp_arg->next) ;
|
||||||
|
if (cur_arg || cmp_arg)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; }
|
||||||
|
inline void maybe_smaller() { maybe_flag=1; }
|
||||||
|
/* Return true iff it's a single-point null interval */
|
||||||
|
inline bool is_null_interval() { return maybe_null && max_value[0] == 1; }
|
||||||
|
inline int cmp_min_to_min(const SEL_ARG* arg) const
|
||||||
|
{
|
||||||
|
return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag);
|
||||||
|
}
|
||||||
|
inline int cmp_min_to_max(const SEL_ARG* arg) const
|
||||||
|
{
|
||||||
|
return sel_cmp(field,min_value, arg->max_value, min_flag, arg->max_flag);
|
||||||
|
}
|
||||||
|
inline int cmp_max_to_max(const SEL_ARG* arg) const
|
||||||
|
{
|
||||||
|
return sel_cmp(field,max_value, arg->max_value, max_flag, arg->max_flag);
|
||||||
|
}
|
||||||
|
inline int cmp_max_to_min(const SEL_ARG* arg) const
|
||||||
|
{
|
||||||
|
return sel_cmp(field,max_value, arg->min_value, max_flag, arg->min_flag);
|
||||||
|
}
|
||||||
|
SEL_ARG *clone_and(THD *thd, SEL_ARG* arg)
|
||||||
|
{ // Get overlapping range
|
||||||
|
uchar *new_min,*new_max;
|
||||||
|
uint8 flag_min,flag_max;
|
||||||
|
if (cmp_min_to_min(arg) >= 0)
|
||||||
|
{
|
||||||
|
new_min=min_value; flag_min=min_flag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_min=arg->min_value; flag_min=arg->min_flag; /* purecov: deadcode */
|
||||||
|
}
|
||||||
|
if (cmp_max_to_max(arg) <= 0)
|
||||||
|
{
|
||||||
|
new_max=max_value; flag_max=max_flag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
flag_max,
|
||||||
|
MY_TEST(maybe_flag && arg->maybe_flag));
|
||||||
|
}
|
||||||
|
SEL_ARG *clone_first(SEL_ARG *arg)
|
||||||
|
{ // min <= X < arg->min
|
||||||
|
return new SEL_ARG(field,part, min_value, arg->min_value,
|
||||||
|
min_flag, arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX,
|
||||||
|
maybe_flag | arg->maybe_flag);
|
||||||
|
}
|
||||||
|
SEL_ARG *clone_last(SEL_ARG *arg)
|
||||||
|
{ // min <= X <= key_max
|
||||||
|
return new SEL_ARG(field, part, min_value, arg->max_value,
|
||||||
|
min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
|
||||||
|
}
|
||||||
|
SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next);
|
||||||
|
|
||||||
|
bool copy_min(SEL_ARG* arg)
|
||||||
|
{ // Get overlapping range
|
||||||
|
if (cmp_min_to_min(arg) > 0)
|
||||||
|
{
|
||||||
|
min_value=arg->min_value; min_flag=arg->min_flag;
|
||||||
|
if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
|
||||||
|
(NO_MAX_RANGE | NO_MIN_RANGE))
|
||||||
|
return 1; // Full range
|
||||||
|
}
|
||||||
|
maybe_flag|=arg->maybe_flag;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
bool copy_max(SEL_ARG* arg)
|
||||||
|
{ // Get overlapping range
|
||||||
|
if (cmp_max_to_max(arg) <= 0)
|
||||||
|
{
|
||||||
|
max_value=arg->max_value; max_flag=arg->max_flag;
|
||||||
|
if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
|
||||||
|
(NO_MAX_RANGE | NO_MIN_RANGE))
|
||||||
|
return 1; // Full range
|
||||||
|
}
|
||||||
|
maybe_flag|=arg->maybe_flag;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_min_to_min(SEL_ARG *arg)
|
||||||
|
{
|
||||||
|
min_value=arg->min_value; min_flag=arg->min_flag;
|
||||||
|
}
|
||||||
|
void copy_min_to_max(SEL_ARG *arg)
|
||||||
|
{
|
||||||
|
max_value=arg->min_value;
|
||||||
|
max_flag=arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX;
|
||||||
|
}
|
||||||
|
void copy_max_to_min(SEL_ARG *arg)
|
||||||
|
{
|
||||||
|
min_value=arg->max_value;
|
||||||
|
min_flag=arg->max_flag & NEAR_MAX ? 0 : NEAR_MIN;
|
||||||
|
}
|
||||||
|
/* returns a number of keypart values (0 or 1) appended to the key buffer */
|
||||||
|
int store_min(uint length, uchar **min_key,uint min_key_flag)
|
||||||
|
{
|
||||||
|
/* "(kp1 > c1) AND (kp2 OP c2) AND ..." -> (kp1 > c1) */
|
||||||
|
if ((min_flag & GEOM_FLAG) ||
|
||||||
|
(!(min_flag & NO_MIN_RANGE) &&
|
||||||
|
!(min_key_flag & (NO_MIN_RANGE | NEAR_MIN))))
|
||||||
|
{
|
||||||
|
if (maybe_null && *min_value)
|
||||||
|
{
|
||||||
|
**min_key=1;
|
||||||
|
bzero(*min_key+1,length-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memcpy(*min_key,min_value,length);
|
||||||
|
(*min_key)+= length;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* returns a number of keypart values (0 or 1) appended to the key buffer */
|
||||||
|
int store_max(uint length, uchar **max_key, uint max_key_flag)
|
||||||
|
{
|
||||||
|
if (!(max_flag & NO_MAX_RANGE) &&
|
||||||
|
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
|
||||||
|
{
|
||||||
|
if (maybe_null && *max_value)
|
||||||
|
{
|
||||||
|
**max_key=1;
|
||||||
|
bzero(*max_key+1,length-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memcpy(*max_key,max_value,length);
|
||||||
|
(*max_key)+= length;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns a number of keypart values appended to the key buffer
|
||||||
|
for min key and max key. This function is used by both Range
|
||||||
|
Analysis and Partition pruning. For partition pruning we have
|
||||||
|
to ensure that we don't store also subpartition fields. Thus
|
||||||
|
we have to stop at the last partition part and not step into
|
||||||
|
the subpartition fields. For Range Analysis we set last_part
|
||||||
|
to MAX_KEY which we should never reach.
|
||||||
|
*/
|
||||||
|
int store_min_key(KEY_PART *key,
|
||||||
|
uchar **range_key,
|
||||||
|
uint *range_key_flag,
|
||||||
|
uint last_part)
|
||||||
|
{
|
||||||
|
SEL_ARG *key_tree= first();
|
||||||
|
uint res= key_tree->store_min(key[key_tree->part].store_length,
|
||||||
|
range_key, *range_key_flag);
|
||||||
|
*range_key_flag|= key_tree->min_flag;
|
||||||
|
if (key_tree->next_key_part &&
|
||||||
|
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
|
||||||
|
key_tree->part != last_part &&
|
||||||
|
key_tree->next_key_part->part == key_tree->part+1 &&
|
||||||
|
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)))
|
||||||
|
res+= key_tree->next_key_part->store_min_key(key,
|
||||||
|
range_key,
|
||||||
|
range_key_flag,
|
||||||
|
last_part);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns a number of keypart values appended to the key buffer */
|
||||||
|
int store_max_key(KEY_PART *key,
|
||||||
|
uchar **range_key,
|
||||||
|
uint *range_key_flag,
|
||||||
|
uint last_part)
|
||||||
|
{
|
||||||
|
SEL_ARG *key_tree= last();
|
||||||
|
uint res=key_tree->store_max(key[key_tree->part].store_length,
|
||||||
|
range_key, *range_key_flag);
|
||||||
|
(*range_key_flag)|= key_tree->max_flag;
|
||||||
|
if (key_tree->next_key_part &&
|
||||||
|
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
|
||||||
|
key_tree->part != last_part &&
|
||||||
|
key_tree->next_key_part->part == key_tree->part+1 &&
|
||||||
|
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
|
||||||
|
res+= key_tree->next_key_part->store_max_key(key,
|
||||||
|
range_key,
|
||||||
|
range_key_flag,
|
||||||
|
last_part);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEL_ARG *insert(SEL_ARG *key);
|
||||||
|
SEL_ARG *tree_delete(SEL_ARG *key);
|
||||||
|
SEL_ARG *find_range(SEL_ARG *key);
|
||||||
|
SEL_ARG *rb_insert(SEL_ARG *leaf);
|
||||||
|
friend SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key, SEL_ARG *par);
|
||||||
|
#ifdef EXTRA_DEBUG
|
||||||
|
friend int test_rb_tree(SEL_ARG *element,SEL_ARG *parent);
|
||||||
|
void test_use_count(SEL_ARG *root);
|
||||||
|
#endif
|
||||||
|
SEL_ARG *first();
|
||||||
|
const SEL_ARG *first() const;
|
||||||
|
SEL_ARG *last();
|
||||||
|
void make_root();
|
||||||
|
inline bool simple_key()
|
||||||
|
{
|
||||||
|
return !next_key_part && elements == 1;
|
||||||
|
}
|
||||||
|
void increment_use_count(long count)
|
||||||
|
{
|
||||||
|
if (next_key_part)
|
||||||
|
{
|
||||||
|
next_key_part->use_count+=count;
|
||||||
|
count*= (next_key_part->use_count-count);
|
||||||
|
for (SEL_ARG *pos=next_key_part->first(); pos ; pos=pos->next)
|
||||||
|
if (pos->next_key_part)
|
||||||
|
pos->increment_use_count(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void incr_refs()
|
||||||
|
{
|
||||||
|
increment_use_count(1);
|
||||||
|
use_count++;
|
||||||
|
}
|
||||||
|
void incr_refs_all()
|
||||||
|
{
|
||||||
|
for (SEL_ARG *pos=first(); pos ; pos=pos->next)
|
||||||
|
{
|
||||||
|
pos->increment_use_count(1);
|
||||||
|
}
|
||||||
|
use_count++;
|
||||||
|
}
|
||||||
|
void free_tree()
|
||||||
|
{
|
||||||
|
for (SEL_ARG *pos=first(); pos ; pos=pos->next)
|
||||||
|
if (pos->next_key_part)
|
||||||
|
{
|
||||||
|
pos->next_key_part->use_count--;
|
||||||
|
pos->next_key_part->free_tree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline SEL_ARG **parent_ptr()
|
||||||
|
{
|
||||||
|
return parent->left == this ? &parent->left : &parent->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check if this SEL_ARG object represents a single-point interval
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
is_singlepoint()
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Check if this SEL_ARG object (not tree) represents a single-point
|
||||||
|
interval, i.e. if it represents a "keypart = const" or
|
||||||
|
"keypart IS NULL".
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
TRUE This SEL_ARG object represents a singlepoint interval
|
||||||
|
FALSE Otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool is_singlepoint()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Check for NEAR_MIN ("strictly less") and NO_MIN_RANGE (-inf < field)
|
||||||
|
flags, and the same for right edge.
|
||||||
|
*/
|
||||||
|
if (min_flag || max_flag)
|
||||||
|
return FALSE;
|
||||||
|
uchar *min_val= min_value;
|
||||||
|
uchar *max_val= max_value;
|
||||||
|
|
||||||
|
if (maybe_null)
|
||||||
|
{
|
||||||
|
/* First byte is a NULL value indicator */
|
||||||
|
if (*min_val != *max_val)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (*min_val)
|
||||||
|
return TRUE; /* This "x IS NULL" */
|
||||||
|
min_val++;
|
||||||
|
max_val++;
|
||||||
|
}
|
||||||
|
return !field->key_cmp(min_val, max_val);
|
||||||
|
}
|
||||||
|
SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RANGE_OPT_PARAM
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
THD *thd; /* Current thread handle */
|
||||||
|
TABLE *table; /* Table being analyzed */
|
||||||
|
table_map prev_tables;
|
||||||
|
table_map read_tables;
|
||||||
|
table_map current_table; /* Bit of the table being analyzed */
|
||||||
|
|
||||||
|
/* Array of parts of all keys for which range analysis is performed */
|
||||||
|
KEY_PART *key_parts;
|
||||||
|
KEY_PART *key_parts_end;
|
||||||
|
MEM_ROOT *mem_root; /* Memory that will be freed when range analysis completes */
|
||||||
|
MEM_ROOT *old_root; /* Memory that will last until the query end */
|
||||||
|
/*
|
||||||
|
Number of indexes used in range analysis (In SEL_TREE::keys only first
|
||||||
|
#keys elements are not empty)
|
||||||
|
*/
|
||||||
|
uint keys;
|
||||||
|
|
||||||
|
/*
|
||||||
|
If true, the index descriptions describe real indexes (and it is ok to
|
||||||
|
call field->optimize_range(real_keynr[...], ...).
|
||||||
|
Otherwise index description describes fake indexes.
|
||||||
|
*/
|
||||||
|
bool using_real_indexes;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Aggressively remove "scans" that do not have conditions on first
|
||||||
|
keyparts. Such scans are usable when doing partition pruning but not
|
||||||
|
regular range optimization.
|
||||||
|
*/
|
||||||
|
bool remove_jump_scans;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TRUE <=> Range analyzer should remove parts of condition that are found
|
||||||
|
to be always FALSE.
|
||||||
|
*/
|
||||||
|
bool remove_false_where_parts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
used_key_no -> table_key_no translation table. Only makes sense if
|
||||||
|
using_real_indexes==TRUE
|
||||||
|
*/
|
||||||
|
uint real_keynr[MAX_KEY];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used to store 'current key tuples', in both range analysis and
|
||||||
|
partitioning (list) analysis
|
||||||
|
*/
|
||||||
|
uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
|
||||||
|
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
|
||||||
|
|
||||||
|
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
|
||||||
|
uint alloced_sel_args;
|
||||||
|
|
||||||
|
bool force_default_mrr;
|
||||||
|
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
|
||||||
|
|
||||||
|
bool statement_should_be_aborted() const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
thd->is_fatal_error ||
|
||||||
|
thd->is_error() ||
|
||||||
|
alloced_sel_args > SEL_ARG::MAX_SEL_ARGS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Explain_quick_select;
|
class Explain_quick_select;
|
||||||
/*
|
/*
|
||||||
A "MIN_TUPLE < tbl.key_tuple < MAX_TUPLE" interval.
|
A "MIN_TUPLE < tbl.key_tuple < MAX_TUPLE" interval.
|
||||||
@ -401,7 +1011,6 @@ public:
|
|||||||
|
|
||||||
struct st_qsel_param;
|
struct st_qsel_param;
|
||||||
class PARAM;
|
class PARAM;
|
||||||
class SEL_ARG;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user