summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/gis-rtree.result24
-rw-r--r--mysql-test/t/gis-rtree.test14
-rw-r--r--sql/item_geofunc.cc73
-rw-r--r--sql/item_geofunc.h3
-rw-r--r--sql/opt_range.cc668
-rw-r--r--sql/opt_range.h611
6 files changed, 729 insertions, 664 deletions
diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result
index 345914c9e51..0506a0b2a0a 100644
--- a/mysql-test/r/gis-rtree.result
+++ b/mysql-test/r/gis-rtree.result
@@ -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
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
#
diff --git a/mysql-test/t/gis-rtree.test b/mysql-test/t/gis-rtree.test
index 9c8dce50127..acd91a91c27 100644
--- a/mysql-test/t/gis-rtree.test
+++ b/mysql-test/t/gis-rtree.test
@@ -994,5 +994,19 @@ EXPLAIN SELECT * FROM t1 WHERE ST_INTERSECTS(Point(1,2),a);
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 # End of 10.1 tests
--echo #
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 55e69c68adb..a048462c7aa 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -38,6 +38,7 @@
#include "set_var.h"
#ifdef HAVE_SPATIAL
#include <m_ctype.h>
+#include "opt_range.h"
Field *Item_geometry_func::tmp_table_field(TABLE *t_arg)
@@ -929,6 +930,78 @@ err:
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
{
switch (spatial_rel) {
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 4ae3be73da1..e88fd266af7 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -277,6 +277,9 @@ class Item_func_spatial_rel: public Item_bool_func2
protected:
enum Functype spatial_rel;
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:
Item_func_spatial_rel(Item *a, Item *b, enum Functype sp_rel)
:Item_bool_func2(a, b), spatial_rel(sp_rel)
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 3ff73bccaba..d0b66c6189b 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -132,8 +132,6 @@
*/
#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
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};
-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.
*/
@@ -832,73 +293,6 @@ public:
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
{
@@ -2571,8 +1965,8 @@ SEL_ARG *SEL_ARG::last()
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,
- uint8 b_flag)
+int SEL_ARG::sel_cmp(Field *field, uchar *a, uchar *b, uint8 a_flag,
+ uint8 b_flag)
{
int cmp;
/* 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
+ 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
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 &&
field->match_collation_to_optimize_range() &&
value->result_type() == STRING_RESULT &&
- key_part->image_type == Field::itRAW &&
field->charset() != compare_collation() &&
!((type == EQUAL_FUNC || type == EQ_FUNC) &&
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)
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 &&
!field->optimize_range(param->real_keynr[key_part->key],
key_part->part) &&
@@ -8632,38 +8006,6 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param,
tree->min_flag= NEAR_MIN;
tree->max_flag=NO_MAX_RANGE;
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 EQUAL_FUNC:
break;
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 2f108c1f6b5..d47fe765184 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -52,6 +52,616 @@ struct KEY_PART {
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;
/*
A "MIN_TUPLE < tbl.key_tuple < MAX_TUPLE" interval.
@@ -401,7 +1011,6 @@ public:
struct st_qsel_param;
class PARAM;
-class SEL_ARG;
/*