From 2c780b461dc62924b3f35bd9e17ec7237dfd31d8 Mon Sep 17 00:00:00 2001 From: Jorgen Loland Date: Fri, 12 Apr 2013 09:39:56 +0200 Subject: [PATCH] Bug#16540042: WRONG QUERY RESULT WHEN USING RANGE OVER PARTIAL INDEX Consider the following table definition: CREATE TABLE t ( my_col CHAR(10), ... INDEX my_idx (my_col(1)) ) The my_idx index is not able to distinguish between rows with equal first-character my_col-values (e.g. "f", "foo", "fee"). Prior to this CS, the range optimizer would translate "WHERE my_col NOT IN ('f', 'h')" into (optimizer trace syntax) "ranges": [ "NULL < my_col < f", "f < my_col" ] But this was not correct because the rows with values "foo" and "fee" would not belong to any of those ranges. However, the predicate "my_col != 'f' AND my_col != 'h'" would translate to "ranges": [ "NULL < my_col" ] because get_mm_leaf() changes from "<" to "<=" for partial keyparts. This CS changes the range optimizer implementation for NOT IN to behave like a conjunction of NOT EQUAL: it replaces "<" with "<=" for all but the first range when the keypart is partial. --- sql/opt_range.cc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 9604ed1e3b3..b8f583a66ed 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -5345,6 +5345,34 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, { new_interval->min_value= last_val->max_value; new_interval->min_flag= NEAR_MIN; + + /* + If the interval is over a partial keypart, the + interval must be "c_{i-1} <= X < c_i" instead of + "c_{i-1} < X < c_i". Reason: + + Consider a table with a column "my_col VARCHAR(3)", + and an index with definition + "INDEX my_idx my_col(1)". If the table contains rows + with my_col values "f" and "foo", the index will not + distinguish the two rows. + + Note that tree_or() below will effectively merge + this range with the range created for c_{i-1} and + we'll eventually end up with only one range: + "NULL < X". + + Partitioning indexes are never partial. + */ + if (param->using_real_indexes) + { + const KEY key= + param->table->key_info[param->real_keynr[idx]]; + const KEY_PART_INFO *kpi= key.key_part + new_interval->part; + + if (kpi->key_part_flag & HA_PART_KEY_SEG) + new_interval->min_flag= 0; + } } } /* @@ -5827,6 +5855,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field, if (key_part->image_type == Field::itMBR) { + // @todo: use is_spatial_operator() instead? switch (type) { case Item_func::SP_EQUALS_FUNC: case Item_func::SP_DISJOINT_FUNC: