Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
60985e53
Commit
60985e53
authored
Aug 13, 2015
by
Alexander Barkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MDEV-8610 "WHERE CONTAINS(indexed_geometry_column,1)" causes full table scan
parent
9d884fd3
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
729 additions
and
664 deletions
+729
-664
mysql-test/r/gis-rtree.result
mysql-test/r/gis-rtree.result
+24
-0
mysql-test/t/gis-rtree.test
mysql-test/t/gis-rtree.test
+14
-0
sql/item_geofunc.cc
sql/item_geofunc.cc
+73
-0
sql/item_geofunc.h
sql/item_geofunc.h
+3
-0
sql/opt_range.cc
sql/opt_range.cc
+5
-663
sql/opt_range.h
sql/opt_range.h
+610
-1
No files found.
mysql-test/r/gis-rtree.result
View file @
60985e53
...
@@ -1618,5 +1618,29 @@ id select_type table type possible_keys key key_len ref rows Extra
...
@@ -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
#
#
mysql-test/t/gis-rtree.test
View file @
60985e53
...
@@ -993,6 +993,20 @@ EXPLAIN SELECT * FROM t1 WHERE MBRINTERSECTS(Point(1,2),a);
...
@@ -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
,
1
e0
);
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
#
sql/item_geofunc.cc
View file @
60985e53
...
@@ -38,6 +38,7 @@
...
@@ -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:
...
@@ -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
)
{
...
...
sql/item_geofunc.h
View file @
60985e53
...
@@ -277,6 +277,9 @@ class Item_func_spatial_rel: public Item_bool_func2
...
@@ -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
)
...
...
sql/opt_range.cc
View file @
60985e53
...
@@ -132,8 +132,6 @@
...
@@ -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);
...
@@ -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:
...
@@ -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()
...
@@ -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,
...
@@ -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,
...
@@ -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,
...
@@ -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,
...
@@ -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
;
...
...
sql/opt_range.h
View file @
60985e53
...
@@ -52,6 +52,616 @@ struct KEY_PART {
...
@@ -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:
...
@@ -401,7 +1011,6 @@ public:
struct
st_qsel_param
;
struct
st_qsel_param
;
class
PARAM
;
class
PARAM
;
class
SEL_ARG
;
/*
/*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment