summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/bool.result3
-rw-r--r--mysql-test/r/negation_elimination.result382
-rw-r--r--mysql-test/t/bool.test1
-rw-r--r--mysql-test/t/negation_elimination.test68
-rw-r--r--sql/item.h3
-rw-r--r--sql/item_cmpfunc.cc103
-rw-r--r--sql/item_cmpfunc.h23
-rw-r--r--sql/item_func.h3
-rw-r--r--sql/sql_select.cc59
-rw-r--r--sql/sql_select.h1
10 files changed, 638 insertions, 8 deletions
diff --git a/mysql-test/r/bool.result b/mysql-test/r/bool.result
index 890c85dc946..a054eceec0c 100644
--- a/mysql-test/r/bool.result
+++ b/mysql-test/r/bool.result
@@ -42,9 +42,6 @@ SELECT * FROM t1 WHERE a=2 OR (NULL AND (@a:=@a+1));
a
SELECT * FROM t1 WHERE NOT(a=2 OR (NULL AND (@b:=@b+1)));
a
-SELECT @a, @b;
-@a @b
-0 6
DROP TABLE t1;
create table t1 (a int, b int);
insert into t1 values(null, null), (0, null), (1, null), (null, 0), (null, 1), (0, 0), (0, 1), (1, 0), (1, 1);
diff --git a/mysql-test/r/negation_elimination.result b/mysql-test/r/negation_elimination.result
new file mode 100644
index 00000000000..9a9764c8f18
--- /dev/null
+++ b/mysql-test/r/negation_elimination.result
@@ -0,0 +1,382 @@
+drop table if exists t1;
+create table t1 (a int, key (a));
+insert into t1 values (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+explain select * from t1 where not(not(a));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using where; Using index
+select * from t1 where not(not(a));
+a
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not(not(not(a > 10)));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 10 Using where; Using index
+select * from t1 where not(not(not(a > 10)));
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+explain select * from t1 where not(not(not(a < 5) and not(a > 10)));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 5 Using where; Using index
+select * from t1 where not(not(not(a < 5) and not(a > 10)));
+a
+5
+6
+7
+8
+9
+10
+explain select * from t1 where not(a = 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using where; Using index
+select * from t1 where not(a = 10);
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not(a != 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref a a 5 const 1 Using where; Using index
+select * from t1 where not(a != 1);
+a
+1
+explain select * from t1 where not(a < 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 11 Using where; Using index
+select * from t1 where not(a < 10);
+a
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not(a >= 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 9 Using where; Using index
+select * from t1 where not(a >= 10);
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+explain select * from t1 where not(a > 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 10 Using where; Using index
+select * from t1 where not(a > 10);
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+explain select * from t1 where not(a <= 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 10 Using where; Using index
+select * from t1 where not(a <= 10);
+a
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not(a is null);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 20 Using where; Using index
+select * from t1 where not(a is null);
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not(a is not null);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref a a 5 const 1 Using where; Using index
+select * from t1 where not(a is not null);
+a
+NULL
+explain select * from t1 where not(a < 5 or a > 15);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 10 Using where; Using index
+select * from t1 where not(a < 5 or a > 15);
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+explain select * from t1 where not(a < 15 and a > 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 12 Using where; Using index
+select * from t1 where not(a < 15 and a > 5);
+a
+0
+1
+2
+3
+4
+5
+15
+16
+17
+18
+19
+explain select * from t1 where a = 2 or not(a < 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 12 Using where; Using index
+select * from t1 where a = 2 or not(a < 10);
+a
+2
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where a > 5 and not(a > 10);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 4 Using where; Using index
+select * from t1 where a > 5 and not(a > 10);
+a
+6
+7
+8
+9
+10
+explain select * from t1 where a > 5 xor a < 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using where; Using index
+select * from t1 where a > 5 xor a < 10;
+a
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where a = 2 or not(a < 5 or a > 15);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 11 Using where; Using index
+select * from t1 where a = 2 or not(a < 5 or a > 15);
+a
+2
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+explain select * from t1 where a = 7 or not(a < 15 and a > 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 13 Using where; Using index
+select * from t1 where a = 7 or not(a < 15 and a > 5);
+a
+0
+1
+2
+3
+4
+5
+7
+15
+16
+17
+18
+19
+explain select * from t1 where NULL or not(a < 15 and a > 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 12 Using where; Using index
+select * from t1 where NULL or not(a < 15 and a > 5);
+a
+0
+1
+2
+3
+4
+5
+15
+16
+17
+18
+19
+explain select * from t1 where not(NULL and a > 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 6 Using where; Using index
+select * from t1 where not(NULL and a > 5);
+a
+0
+1
+2
+3
+4
+5
+explain select * from t1 where not(NULL or a);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
+select * from t1 where not(NULL or a);
+a
+explain select * from t1 where not(NULL and a);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using where; Using index
+select * from t1 where not(NULL and a);
+a
+0
+explain select * from t1 where not((a < 5 or a < 10) and (not(a > 16) or a > 17));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 11 Using where; Using index
+select * from t1 where not((a < 5 or a < 10) and (not(a > 16) or a > 17));
+a
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where not((a < 5 and a < 10) and (not(a > 16) or a > 17));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 15 Using where; Using index
+select * from t1 where not((a < 5 and a < 10) and (not(a > 16) or a > 17));
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+explain select * from t1 where ((a between 5 and 15) and (not(a like 10)));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 10 Using where; Using index
+select * from t1 where ((a between 5 and 15) and (not(a like 10)));
+a
+5
+6
+7
+8
+9
+11
+12
+13
+14
+15
+drop table t1;
diff --git a/mysql-test/t/bool.test b/mysql-test/t/bool.test
index c001973baeb..b263ecfded2 100644
--- a/mysql-test/t/bool.test
+++ b/mysql-test/t/bool.test
@@ -26,7 +26,6 @@ SELECT * FROM t1 WHERE NULL AND (@a:=@a+1);
SELECT * FROM t1 WHERE NOT(a>=0 AND NULL AND (@b:=@b+1));
SELECT * FROM t1 WHERE a=2 OR (NULL AND (@a:=@a+1));
SELECT * FROM t1 WHERE NOT(a=2 OR (NULL AND (@b:=@b+1)));
-SELECT @a, @b;
DROP TABLE t1;
diff --git a/mysql-test/t/negation_elimination.test b/mysql-test/t/negation_elimination.test
new file mode 100644
index 00000000000..49428cc238b
--- /dev/null
+++ b/mysql-test/t/negation_elimination.test
@@ -0,0 +1,68 @@
+#
+# Test negation elimination
+#
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (a int, key (a));
+insert into t1 values (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+
+explain select * from t1 where not(not(a));
+select * from t1 where not(not(a));
+explain select * from t1 where not(not(not(a > 10)));
+select * from t1 where not(not(not(a > 10)));
+explain select * from t1 where not(not(not(a < 5) and not(a > 10)));
+select * from t1 where not(not(not(a < 5) and not(a > 10)));
+explain select * from t1 where not(a = 10);
+select * from t1 where not(a = 10);
+explain select * from t1 where not(a != 10);
+select * from t1 where not(a != 1);
+explain select * from t1 where not(a < 10);
+select * from t1 where not(a < 10);
+explain select * from t1 where not(a >= 10);
+select * from t1 where not(a >= 10);
+explain select * from t1 where not(a > 10);
+select * from t1 where not(a > 10);
+explain select * from t1 where not(a <= 10);
+select * from t1 where not(a <= 10);
+explain select * from t1 where not(a is null);
+select * from t1 where not(a is null);
+explain select * from t1 where not(a is not null);
+select * from t1 where not(a is not null);
+explain select * from t1 where not(a < 5 or a > 15);
+select * from t1 where not(a < 5 or a > 15);
+explain select * from t1 where not(a < 15 and a > 5);
+select * from t1 where not(a < 15 and a > 5);
+
+explain select * from t1 where a = 2 or not(a < 10);
+select * from t1 where a = 2 or not(a < 10);
+explain select * from t1 where a > 5 and not(a > 10);
+select * from t1 where a > 5 and not(a > 10);
+explain select * from t1 where a > 5 xor a < 10;
+select * from t1 where a > 5 xor a < 10;
+
+explain select * from t1 where a = 2 or not(a < 5 or a > 15);
+select * from t1 where a = 2 or not(a < 5 or a > 15);
+explain select * from t1 where a = 7 or not(a < 15 and a > 5);
+select * from t1 where a = 7 or not(a < 15 and a > 5);
+
+explain select * from t1 where NULL or not(a < 15 and a > 5);
+select * from t1 where NULL or not(a < 15 and a > 5);
+explain select * from t1 where not(NULL and a > 5);
+select * from t1 where not(NULL and a > 5);
+explain select * from t1 where not(NULL or a);
+select * from t1 where not(NULL or a);
+explain select * from t1 where not(NULL and a);
+select * from t1 where not(NULL and a);
+
+explain select * from t1 where not((a < 5 or a < 10) and (not(a > 16) or a > 17));
+select * from t1 where not((a < 5 or a < 10) and (not(a > 16) or a > 17));
+explain select * from t1 where not((a < 5 and a < 10) and (not(a > 16) or a > 17));
+select * from t1 where not((a < 5 and a < 10) and (not(a > 16) or a > 17));
+explain select * from t1 where ((a between 5 and 15) and (not(a like 10)));
+select * from t1 where ((a between 5 and 15) and (not(a like 10)));
+
+drop table t1;
diff --git a/sql/item.h b/sql/item.h
index a126a61e32e..e630d0229d7 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -212,6 +212,9 @@ public:
virtual void bring_value() {}
Field *tmp_table_field_from_field_type(TABLE *table);
+
+ /* Used in sql_select.cc:eliminate_not_funcs() */
+ virtual Item *neg_transformer() { return NULL; }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 4e6301d2626..7e2d0961c14 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -23,6 +23,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
+#include "sql_select.h"
static Item_result item_store_type(Item_result a,Item_result b)
{
@@ -476,6 +477,7 @@ longlong Item_func_eq::val_int()
return value == 0 ? 1 : 0;
}
+
/* Same as Item_func_eq, but NULL = NULL */
void Item_func_equal::fix_length_and_dec()
@@ -1721,6 +1723,19 @@ void Item_cond::print(String *str)
str->append(')');
}
+
+void Item_cond::neg_arguments()
+{
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item= li++)) /* Apply not transformation to the arguments */
+ {
+ Item *new_item= item->neg_transformer();
+ VOID(li.replace(new_item ? new_item : new Item_func_not(item)));
+ }
+}
+
+
/*
Evalution of AND(expr, expr, expr ...)
@@ -2334,3 +2349,91 @@ longlong Item_cond_xor::val_int()
}
return (longlong) result;
}
+
+/*
+ Apply NOT transformation to the item and return a new one.
+
+ SYNPOSIS
+ neg_transformer()
+
+ DESCRIPTION
+ Transform the item using next rules:
+ a AND b AND ... -> NOT(a) OR NOT(b) OR ...
+ a OR b OR ... -> NOT(a) AND NOT(b) AND ...
+ NOT(a) -> a
+ a = b -> a != b
+ a != b -> a = b
+ a < b -> a >= b
+ a >= b -> a < b
+ a > b -> a <= b
+ a <= b -> a > b
+ IS NULL(a) -> IS NOT NULL(a)
+ IS NOT NULL(a) -> IS NULL(a)
+
+ NOTE
+ This method is used in the eliminate_not_funcs() function.
+
+ RETURN
+ New item or
+ NULL if we cannot apply NOT transformation (see Item::neg_transformer()).
+*/
+
+Item *Item_func_not::neg_transformer() /* NOT(x) -> x */
+{
+ /* We should apply negation elimination to the argument of the NOT function */
+ return eliminate_not_funcs(args[0]);
+}
+
+Item *Item_func_eq::neg_transformer() /* a = b -> a != b */
+{
+ return new Item_func_ne(args[0], args[1]);
+}
+
+Item *Item_func_ne::neg_transformer() /* a != b -> a = b */
+{
+ return new Item_func_eq(args[0], args[1]);
+}
+
+Item *Item_func_lt::neg_transformer() /* a < b -> a >= b */
+{
+ return new Item_func_ge(args[0], args[1]);
+}
+
+Item *Item_func_ge::neg_transformer() /* a >= b -> a < b */
+{
+ return new Item_func_lt(args[0], args[1]);
+}
+
+Item *Item_func_gt::neg_transformer() /* a > b -> a <= b */
+{
+ return new Item_func_le(args[0], args[1]);
+}
+
+Item *Item_func_le::neg_transformer() /* a <= b -> a > b */
+{
+ return new Item_func_gt(args[0], args[1]);
+}
+
+Item *Item_func_isnull::neg_transformer() /* a IS NULL -> a IS NOT NULL */
+{
+ return new Item_func_isnotnull(args[0]);
+}
+
+Item *Item_func_isnotnull::neg_transformer() /* a IS NOT NULL -> a IS NULL */
+{
+ return new Item_func_isnull(args[0]);
+}
+
+Item *Item_cond_and::neg_transformer() /* NOT(a AND b AND ...) -> */
+ /* NOT a OR NOT b OR ... */
+{
+ neg_arguments();
+ return new Item_cond_or(list);
+}
+
+Item *Item_cond_or::neg_transformer() /* NOT(a OR b OR ...) -> */
+ /* NOT a AND NOT b AND ... */
+{
+ neg_arguments();
+ return new Item_cond_and(list);
+}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 42b73c48606..856d32cadd6 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -153,7 +153,9 @@ class Item_func_not :public Item_bool_func
public:
Item_func_not(Item *a) :Item_bool_func(a) {}
longlong val_int();
+ enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
+ Item *neg_transformer();
};
class Item_func_not_all :public Item_func_not
@@ -164,6 +166,8 @@ public:
virtual void top_level_item() { abort_on_null= 1; }
bool top_level() { return abort_on_null; }
longlong val_int();
+ enum Functype functype() const { return NOT_ALL_FUNC; }
+ const char *func_name() const { return "not_all"; }
};
class Item_func_eq :public Item_bool_rowready_func2
@@ -175,6 +179,7 @@ public:
enum Functype rev_functype() const { return EQ_FUNC; }
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return "="; }
+ Item *neg_transformer();
};
class Item_func_equal :public Item_bool_rowready_func2
@@ -199,6 +204,7 @@ public:
enum Functype rev_functype() const { return LE_FUNC; }
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return ">="; }
+ Item *neg_transformer();
};
@@ -211,6 +217,7 @@ public:
enum Functype rev_functype() const { return LT_FUNC; }
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return ">"; }
+ Item *neg_transformer();
};
@@ -223,6 +230,7 @@ public:
enum Functype rev_functype() const { return GE_FUNC; }
cond_result eq_cmp_result() const { return COND_TRUE; }
const char *func_name() const { return "<="; }
+ Item *neg_transformer();
};
@@ -235,6 +243,7 @@ public:
enum Functype rev_functype() const { return GT_FUNC; }
cond_result eq_cmp_result() const { return COND_FALSE; }
const char *func_name() const { return "<"; }
+ Item *neg_transformer();
};
@@ -247,6 +256,7 @@ public:
cond_result eq_cmp_result() const { return COND_FALSE; }
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "<>"; }
+ Item *neg_transformer();
};
@@ -690,6 +700,7 @@ public:
}
table_map not_null_tables() const { return 0; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
+ Item *neg_transformer();
};
/* Functions used by HAVING for rewriting IN subquery */
@@ -722,6 +733,7 @@ public:
const char *func_name() const { return "isnotnull"; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
table_map not_null_tables() const { return 0; }
+ Item *neg_transformer();
};
@@ -801,7 +813,8 @@ protected:
public:
/* Item_cond() is only used to create top level items */
- Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; }
+ Item_cond(): Item_bool_func(), abort_on_null(1)
+ { const_item_cache=0; }
Item_cond(Item *i1,Item *i2)
:Item_bool_func(), abort_on_null(0)
{
@@ -809,6 +822,8 @@ public:
list.push_back(i2);
}
Item_cond(THD *thd, Item_cond &item);
+ Item_cond(List<Item> &nlist)
+ :Item_bool_func(), list(nlist), abort_on_null(0) {}
~Item_cond() { list.delete_elements(); }
bool add(Item *item) { return list.push_back(item); }
bool fix_fields(THD *, struct st_table_list *, Item **ref);
@@ -822,8 +837,8 @@ public:
friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
void top_level_item() { abort_on_null=1; }
void copy_andor_arguments(THD *thd, Item_cond *item);
-
bool walk(Item_processor processor, byte *arg);
+ void neg_arguments();
};
@@ -833,6 +848,7 @@ public:
Item_cond_and() :Item_cond() {}
Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {}
Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {}
+ Item_cond_and(List<Item> &list): Item_cond(list) {}
enum Functype functype() const { return COND_AND_FUNC; }
longlong val_int();
const char *func_name() const { return "and"; }
@@ -843,6 +859,7 @@ public:
item->copy_andor_arguments(thd, this);
return item;
}
+ Item *neg_transformer();
};
class Item_cond_or :public Item_cond
@@ -851,6 +868,7 @@ public:
Item_cond_or() :Item_cond() {}
Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {}
Item_cond_or(THD *thd, Item_cond_or &item) :Item_cond(thd, item) {}
+ Item_cond_or(List<Item> &list): Item_cond(list) {}
enum Functype functype() const { return COND_OR_FUNC; }
longlong val_int();
const char *func_name() const { return "or"; }
@@ -862,6 +880,7 @@ public:
item->copy_andor_arguments(thd, this);
return item;
}
+ Item *neg_transformer();
};
diff --git a/sql/item_func.h b/sql/item_func.h
index 8086e65786d..7c3d499f52e 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -46,7 +46,8 @@ public:
SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC,
SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
- SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN};
+ SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
+ NOT_FUNC, NOT_ALL_FUNC};
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 4bab97d4a46..0fc78421824 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -4161,6 +4161,60 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father,
}
+/*
+ Eliminate NOT functions from the condition tree.
+
+ SYNPOSIS
+ eliminate_not_funcs()
+ cond condition tree
+
+ DESCRIPTION
+ Eliminate NOT functions from the condition tree where it's possible.
+ Recursively traverse condition tree to find all NOT functions.
+ Call neg_transformer() method for negated arguments.
+
+ NOTE
+ If neg_transformer() returned a new condition we call fix_fields().
+ We don't delete any items as it's not needed. They will be deleted
+ later at once.
+
+ RETURN
+ New condition tree
+*/
+
+COND *eliminate_not_funcs(COND *cond)
+{
+ if (!cond)
+ return cond;
+ if (cond->type() == Item::COND_ITEM) /* OR or AND */
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item= li++))
+ {
+ Item *new_item= eliminate_not_funcs(item);
+ if (item != new_item)
+ VOID(li.replace(new_item)); /* replace item with a new condition */
+ }
+ }
+ else if (cond->type() == Item::FUNC_ITEM && /* 'NOT' operation? */
+ ((Item_func*) cond)->functype() == Item_func::NOT_FUNC)
+ {
+ COND *new_cond= ((Item_func*) cond)->arguments()[0]->neg_transformer();
+ if (new_cond)
+ {
+ /*
+ Here we can delete the NOT function. Something like: delete cond;
+ But we don't need to do it. All items will be deleted later at once.
+ */
+ new_cond->fix_fields(current_thd, 0, &new_cond);
+ cond= new_cond;
+ }
+ }
+ return cond;
+}
+
+
static COND *
optimize_cond(COND *conds,Item::cond_result *cond_value)
{
@@ -4169,8 +4223,11 @@ optimize_cond(COND *conds,Item::cond_result *cond_value)
*cond_value= Item::COND_TRUE;
return conds;
}
- /* change field = field to field = const for each found field = const */
DBUG_EXECUTE("where",print_where(conds,"original"););
+ /* eliminate NOT operators */
+ conds= eliminate_not_funcs(conds);
+ DBUG_EXECUTE("where", print_where(conds, "after negation elimination"););
+ /* change field = field to field = const for each found field = const */
propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds);
/*
Remove all instances of item == item
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 6c17a646ee6..642d53a6de3 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -414,3 +414,4 @@ bool error_if_full_join(JOIN *join);
void relink_tables(SELECT_LEX *select_lex);
int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab);
+COND *eliminate_not_funcs(COND *cond);