From 18366c930c54452b20245501d55babcca963ec5b Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sun, 29 Aug 2004 23:14:46 +0300 Subject: [PATCH 1/2] NOT elimination moved in parsing (suggested by Monty) --- sql/item_cmpfunc.cc | 33 --------------------------------- sql/item_cmpfunc.h | 2 +- sql/sql_select.cc | 13 ------------- sql/sql_yacc.yy | 12 ++++++++++-- 4 files changed, 11 insertions(+), 49 deletions(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bf7813eb9ba..de37e858bac 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2030,15 +2030,6 @@ void Item_cond::neg_arguments(THD *thd) { if (!(new_item= new Item_func_not(item))) return; // Fatal OEM error - /* - We can use 0 as tables list because Item_func_not do not use it - on fix_fields and its arguments are already fixed. - - We do not check results of fix_fields, because there are not way - to return error in this functions interface, thd->net.report_error - will be checked on upper level call. - */ - new_item->fix_fields(thd, 0, &new_item); } VOID(li.replace(new_item)); } @@ -2734,18 +2725,6 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ Item *Item_bool_rowready_func2::neg_transformer(THD *thd) { Item *item= negated_item(); - if (item) - { - /* - We can use 0 as tables list because Item_func* family do not use it - on fix_fields and its arguments are already fixed. - - We do not check results of fix_fields, because there are not way - to return error in this functions interface, thd->net.report_error - will be checked on upper level call. - */ - item->fix_fields(thd, 0, &item); - } return item; } @@ -2754,9 +2733,6 @@ Item *Item_bool_rowready_func2::neg_transformer(THD *thd) Item *Item_func_isnull::neg_transformer(THD *thd) { Item *item= new Item_func_isnotnull(args[0]); - // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer - if (item) - item->fix_fields(thd, 0, &item); return item; } @@ -2765,9 +2741,6 @@ Item *Item_func_isnull::neg_transformer(THD *thd) Item *Item_func_isnotnull::neg_transformer(THD *thd) { Item *item= new Item_func_isnull(args[0]); - // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer - if (item) - item->fix_fields(thd, 0, &item); return item; } @@ -2777,9 +2750,6 @@ Item *Item_cond_and::neg_transformer(THD *thd) /* NOT(a AND b AND ...) -> */ { neg_arguments(thd); Item *item= new Item_cond_or(list); - // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer - if (item) - item->fix_fields(thd, 0, &item); return item; } @@ -2789,9 +2759,6 @@ Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */ { neg_arguments(thd); Item *item= new Item_cond_and(list); - // see comment before fix_fields in Item_bool_rowready_func2::neg_transformer - if (item) - item->fix_fields(thd, 0, &item); return item; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 4f2dcb6a412..c3551b35d63 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -274,7 +274,7 @@ public: enum Functype rev_functype() const { return EQUAL_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<=>"; } - Item* neg_transformer(THD *thd) { return 0; } + Item *neg_transformer(THD *thd) { return 0; } }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4ca8008c518..701d2597d3d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4401,19 +4401,6 @@ optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) if (conds) { DBUG_EXECUTE("where", print_where(conds, "original");); - /* Eliminate NOT operators; in case of PS/SP do it once */ - if (thd->current_arena->is_first_stmt_execute()) - { - Item_arena *arena= thd->current_arena, backup; - thd->set_n_backup_item_arena(arena, &backup); - conds= eliminate_not_funcs(thd, conds); - select->prep_where= conds->copy_andor_structure(thd); - thd->restore_backup_item_arena(arena, &backup); - } - else - conds= eliminate_not_funcs(thd, 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 *) 0, conds, conds); /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1c057e03a11..afb55463ad1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2755,8 +2755,16 @@ simple_expr: | '+' expr %prec NEG { $$= $2; } | '-' expr %prec NEG { $$= new Item_func_neg($2); } | '~' expr %prec NEG { $$= new Item_func_bit_neg($2); } - | NOT expr %prec NEG { $$= new Item_func_not($2); } - | '!' expr %prec NEG { $$= new Item_func_not($2); } + | NOT expr %prec NEG + { + if (($$= $2->neg_transformer(YYTHD)) == 0) + $$= new Item_func_not($2); + } + | '!' expr %prec NEG + { + if (($$= $2->neg_transformer(YYTHD)) == 0) + $$= new Item_func_not($2); + } | '(' expr ')' { $$= $2; } | '(' expr ',' expr_list ')' { From 2cf1234ba2d38f2087549377d5e4a182d86a4727 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Tue, 31 Aug 2004 21:10:57 +0300 Subject: [PATCH 2/2] after review patch --- mysql-test/r/negation_elimination.result | 13 ++++++ mysql-test/t/negation_elimination.test | 4 ++ sql/item.h | 4 +- sql/item_cmpfunc.cc | 5 +-- sql/item_cmpfunc.h | 3 ++ sql/mysql_priv.h | 4 +- sql/sql_parse.cc | 36 ++++++++++++++++ sql/sql_select.cc | 54 ------------------------ sql/sql_select.h | 1 - sql/sql_yacc.yy | 20 +++++---- 10 files changed, 74 insertions(+), 70 deletions(-) diff --git a/mysql-test/r/negation_elimination.result b/mysql-test/r/negation_elimination.result index a3a2bad7ec6..9193a125cd1 100644 --- a/mysql-test/r/negation_elimination.result +++ b/mysql-test/r/negation_elimination.result @@ -375,4 +375,17 @@ a 13 14 15 +delete from t1 where a > 3; +select a, not(not(a)) from t1; +a not(not(a)) +NULL NULL +0 0 +1 1 +2 1 +3 1 +explain extended select a, not(not(a)), not(a <= 2 and not(a)), not(a not like "1"), not (a not in (1,2)), not(a != 2) from t1 where not(not(a)) having not(not(a)); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 5 Using where; Using index +Warnings: +Note 1003 select test.t1.a AS `a`,(test.t1.a <> 0) AS `not(not(a))`,((test.t1.a > 2) or test.t1.a) AS `not(a <= 2 and not(a))`,(test.t1.a like _latin1'1') AS `not(a not like "1")`,(test.t1.a in (1,2)) AS `not (a not in (1,2))`,(test.t1.a = 2) AS `not(a != 2)` from test.t1 where test.t1.a having test.t1.a drop table t1; diff --git a/mysql-test/t/negation_elimination.test b/mysql-test/t/negation_elimination.test index 49428cc238b..c50a9678edb 100644 --- a/mysql-test/t/negation_elimination.test +++ b/mysql-test/t/negation_elimination.test @@ -65,4 +65,8 @@ 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))); +delete from t1 where a > 3; +select a, not(not(a)) from t1; +explain extended select a, not(not(a)), not(a <= 2 and not(a)), not(a not like "1"), not (a not in (1,2)), not(a != 2) from t1 where not(not(a)) having not(not(a)); + drop table t1; diff --git a/sql/item.h b/sql/item.h index 6900fa11b90..742cf934381 100644 --- a/sql/item.h +++ b/sql/item.h @@ -239,6 +239,7 @@ public: virtual void top_level_item() {} virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } + virtual bool is_bool_func() { return 0; } virtual void save_in_result_field(bool no_conversions) {} virtual void no_rows_in_result() {} virtual Item *copy_or_same(THD *thd) { return this; } @@ -268,8 +269,7 @@ 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(THD *thd) { return NULL; } void delete_self() { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index de37e858bac..53ec17fd59d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2707,9 +2707,6 @@ longlong Item_cond_xor::val_int() 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()). @@ -2718,7 +2715,7 @@ longlong Item_cond_xor::val_int() Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ { // We should apply negation elimination to the argument of the NOT function - return eliminate_not_funcs(thd, args[0]); + return args[0]; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c3551b35d63..f1a2b11aaa8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -89,6 +89,7 @@ public: Item_bool_func(Item *a) :Item_int_func(a) {} Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {} Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {} + bool is_bool_func() { return 1; } void fix_length_and_dec() { decimals=0; max_length=1; } }; @@ -201,6 +202,7 @@ public: bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; } void print(String *str) { Item_func::print_op(str); } bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } + bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; } friend class Arg_comparator; @@ -748,6 +750,7 @@ class Item_func_in :public Item_int_func enum Functype functype() const { return IN_FUNC; } const char *func_name() const { return " IN "; } bool nulls_in_row(); + bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1949ecf26dc..a9ee6b4b691 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -297,7 +297,8 @@ enum enum_parsing_place { NO_MATTER, IN_HAVING, - SELECT_LIST + SELECT_LIST, + IN_WHERE }; struct st_table; @@ -376,6 +377,7 @@ int delete_precheck(THD *thd, TABLE_LIST *tables); int insert_precheck(THD *thd, TABLE_LIST *tables, bool update); int create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); +Item *negate_expression(THD *thd, Item *expr); #include "sql_class.h" #include "opt_range.h" diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 3cb356d42c8..79a011b9501 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5401,3 +5401,39 @@ int create_table_precheck(THD *thd, TABLE_LIST *tables, check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) ? 1 : 0); } + + +/* + negate given expression + + SYNOPSIS + negate_expression() + thd therad handler + expr expression for negation + + RETURN + negated expression +*/ + +Item *negate_expression(THD *thd, Item *expr) +{ + Item *negated; + if (expr->type() == Item::FUNC_ITEM && + ((Item_func *) expr)->functype() == Item_func::NOT_FUNC) + { + /* it is NOT(NOT( ... )) */ + Item *arg= ((Item_func *) expr)->arguments()[0]; + enum_parsing_place place= thd->lex->current_select->parsing_place; + if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING) + return arg; + /* + if it is not boolean function then we have to emulate value of + not(not(a)), it will be a != 0 + */ + return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1)); + } + + if ((negated= expr->neg_transformer(thd)) != 0) + return negated; + return new Item_func_not(expr); +} diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 701d2597d3d..72e169c77af 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4339,60 +4339,6 @@ propagate_cond_constants(I_List *save_list,COND *and_father, } -/* - Eliminate NOT functions from the condition tree. - - SYNPOSIS - eliminate_not_funcs() - thd thread handler - 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(THD *thd, COND *cond) -{ - if (!cond) - return cond; - if (cond->type() == Item::COND_ITEM) /* OR or AND */ - { - List_iterator li(*((Item_cond*) cond)->argument_list()); - Item *item; - while ((item= li++)) - { - Item *new_item= eliminate_not_funcs(thd, 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(thd); - 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. - */ - cond= new_cond; - } - } - return cond; -} - - static COND * optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value) { diff --git a/sql/sql_select.h b/sql/sql_select.h index 8aca43484d2..34eaa7e272d 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -438,4 +438,3 @@ bool cp_buffer_from_ref(TABLE_REF *ref); bool error_if_full_join(JOIN *join); int report_error(TABLE *table, int error); int safe_index_read(JOIN_TAB *tab); -COND *eliminate_not_funcs(THD *thd, COND *cond); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index afb55463ad1..fa772a9cf11 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2757,13 +2757,11 @@ simple_expr: | '~' expr %prec NEG { $$= new Item_func_bit_neg($2); } | NOT expr %prec NEG { - if (($$= $2->neg_transformer(YYTHD)) == 0) - $$= new Item_func_not($2); + $$= negate_expression(YYTHD, $2); } | '!' expr %prec NEG { - if (($$= $2->neg_transformer(YYTHD)) == 0) - $$= new Item_func_not($2); + $$= negate_expression(YYTHD, $2); } | '(' expr ')' { $$= $2; } | '(' expr ',' expr_list ')' @@ -3606,11 +3604,17 @@ opt_all: where_clause: /* empty */ { Select->where= 0; } - | WHERE expr + | WHERE + { + Select->parsing_place= IN_WHERE; + } + expr { - Select->where= $2; - if ($2) - $2->top_level_item(); + SELECT_LEX *select= Select; + select->where= $3; + select->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); } ;