diff options
author | unknown <aivanov@mysql.com> | 2005-09-06 18:03:08 +0400 |
---|---|---|
committer | unknown <aivanov@mysql.com> | 2005-09-06 18:03:08 +0400 |
commit | f1cc5049810a0bcf701fe994aa3c4f6c5bbae8ab (patch) | |
tree | d6ae557d23c29875371111ed32a93908ec4af8f1 /sql | |
parent | f53de1f247373f56f30238c18893d6e01bad4d95 (diff) | |
download | mariadb-git-f1cc5049810a0bcf701fe994aa3c4f6c5bbae8ab.tar.gz |
item_cmpfunc.h:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
The classes Item_func_between, Item_func_if, Item_func_in are modified.
Item_func_between/in objects can represent now [NOT]BETWEEN/IN expressions.
The class Item_func_opt_neg is added to factor out the functionality
common for the modified classes Item_func_between and Item_func_in.
item_cmpfunc.cc:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
Added Item_func_between::fix_fields(), Item_func_if::fix_fields(),
Item_func_in::fix_fields(). They correct generic calculation of
the not_null_tables attribute when it is needed.
Modified Item_func_between::val_int(), Item_func_in::val_int().
opt_range.cc:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
The function get_mm_tree() is modified. There cannot be NOT before
BETWEEN/IN anymore. Rather Item_func_between/in objects can represent
now [NOT]BETWEEN/IN expressions.
sql_yacc.yy:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
Item_func_between/in objects can represent now [NOT]BETWEEN/IN expresions.
join_outer.result:
Fixed some testcases results (bugs #12101, #12102)
join_outer.test:
Added testcases for bugs #12101, #12102
mysql-test/t/join_outer.test:
Added testcases for bugs #12101, #12102
mysql-test/r/join_outer.result:
Fixed some testcases results (bugs #12101, #12102)
sql/sql_yacc.yy:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
Item_func_between/in objects can represent now [NOT]BETWEEN/IN expresions.
sql/opt_range.cc:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
The function get_mm_tree() is modified. There cannot be NOT before
BETWEEN/IN anymore. Rather Item_func_between/in objects can represent
now [NOT]BETWEEN/IN expressions.
sql/item_cmpfunc.cc:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
Added Item_func_between::fix_fields(), Item_func_if::fix_fields(),
Item_func_in::fix_fields(). They correct generic calculation of
the not_null_tables attribute when it is needed.
Modified Item_func_between::val_int(), Item_func_in::val_int().
sql/item_cmpfunc.h:
Fixed bugs #12101, #12102: wrong calculation of not_null_tables()
for some expressions.
The classes Item_func_between, Item_func_if, Item_func_in are modified.
Item_func_between/in objects can represent now [NOT]BETWEEN/IN expressions.
The class Item_func_opt_neg is added to factor out the functionality
common for the modified classes Item_func_between and Item_func_in.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_cmpfunc.cc | 158 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 55 | ||||
-rw-r--r-- | sql/opt_range.cc | 5 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 32 |
4 files changed, 218 insertions, 32 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9146b3c3b9e..99ca0df355b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -820,6 +820,54 @@ longlong Item_func_interval::val_int() return i-1; } + +/* + Perform context analysis of a BETWEEN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_between as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e BETWEEN e1 AND e2) = union(T1(e),T1(e1),T1(e2)) + T1(e BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T0(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T1(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_between::fix_fields(THD *thd, struct st_table_list *tables, Item **ref) +{ + if (Item_func_opt_neg::fix_fields(thd, tables, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ + if (pred_level && !negated) + return 0; + + /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */ + not_null_tables_cache= args[0]->not_null_tables() | + (args[1]->not_null_tables() & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_between::fix_length_and_dec() { max_length= 1; @@ -871,8 +919,9 @@ longlong Item_func_between::val_int() a=args[1]->val_str(&value1); b=args[2]->val_str(&value2); if (!args[1]->null_value && !args[2]->null_value) - return (sortcmp(value,a,cmp_collation.collation) >= 0 && - sortcmp(value,b,cmp_collation.collation) <= 0) ? 1 : 0; + return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 && + sortcmp(value,b,cmp_collation.collation) <= 0) != + negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -894,7 +943,7 @@ longlong Item_func_between::val_int() a=args[1]->val_int(); b=args[2]->val_int(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -914,7 +963,7 @@ longlong Item_func_between::val_int() a=args[1]->val(); b=args[2]->val(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -926,7 +975,7 @@ longlong Item_func_between::val_int() null_value= value >= a; } } - return 0; + return (longlong) (!null_value && negated); } @@ -1019,6 +1068,49 @@ Item_func_ifnull::val_str(String *str) } +/* + Perform context analysis of an IF item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_if as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(IF(e,e1,e2) = T1(IF(e,e1,e2)) + T1(IF(e,e1,e2)) = intersection(T1(e1),T1(e2)) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_if::fix_fields(THD *thd, struct st_table_list *tlist, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + args[0]->top_level_item(); + + if (Item_func::fix_fields(thd, tlist, ref)) + return 1; + + not_null_tables_cache= (args[1]->not_null_tables() + & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_if::fix_length_and_dec() { @@ -1750,6 +1842,56 @@ bool Item_func_in::nulls_in_row() } +/* + Perform context analysis of an IN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_in as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T1(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T0(e NOT IN(e1,...,en)) = union(T1(e),union(T1(ei))) + T1(e NOT IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_in::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +{ + Item **arg, **arg_end; + + if (Item_func_opt_neg::fix_fields(thd, tables, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ + if (pred_level && negated) + return 0; + + /* not_null_tables_cache = union(T1(e),intersection(T1(ei))) */ + not_null_tables_cache= ~(table_map) 0; + for (arg= args + 1, arg_end= args + arg_count; arg != arg_end; arg++) + not_null_tables_cache&= (*arg)->not_null_tables(); + not_null_tables_cache|= (*args)->not_null_tables(); + return 0; +} + + static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, @@ -1840,7 +1982,7 @@ longlong Item_func_in::val_int() { int tmp=array->find(args[0]); null_value=args[0]->null_value || (!tmp && have_null); - return tmp; + return (longlong) (!null_value && tmp != negated); } in_item->store_value(args[0]); if ((null_value=args[0]->null_value)) @@ -1849,11 +1991,11 @@ longlong Item_func_in::val_int() for (uint i=1 ; i < arg_count ; i++) { if (!in_item->cmp(args[i]) && !args[i]->null_value) - return 1; // Would maybe be nice with i ? + return (longlong) (!negated); have_null|= args[i]->null_value; } null_value= have_null; - return 0; + return (longlong) (!null_value && negated); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 47884f6064e..2ebf794f0a8 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -358,17 +358,49 @@ public: }; -class Item_func_between :public Item_int_func +/* + The class Item_func_opt_neg is defined to factor out the functionality + common for the classes Item_func_between and Item_func_in. The objects + of these classes can express predicates or there negations. + The alternative approach would be to create pairs Item_func_between, + Item_func_notbetween and Item_func_in, Item_func_notin. + +*/ + +class Item_func_opt_neg :public Item_int_func +{ +public: + bool negated; /* <=> the item represents NOT <func> */ + bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ +public: + Item_func_opt_neg(Item *a, Item *b, Item *c) + :Item_int_func(a, b, c), negated(0), pred_level(0) {} + Item_func_opt_neg(List<Item> &list) + :Item_int_func(list), negated(0), pred_level(0) {} +public: + inline void negate() { negated= !negated; } + inline void top_level_item() { pred_level= 1; } + Item *neg_transformer(THD *thd) + { + negated= !negated; + return this; + } +}; + + +class Item_func_between :public Item_func_opt_neg { DTCollation cmp_collation; public: Item_result cmp_type; String value0,value1,value2; - Item_func_between(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {} + Item_func_between(Item *a, Item *b, Item *c) + :Item_func_opt_neg(a, b, c) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_KEY; } enum Functype functype() const { return BETWEEN; } const char *func_name() const { return "between"; } + bool fix_fields(THD *, struct st_table_list *, Item **); void fix_length_and_dec(); void print(String *str); CHARSET_INFO *compare_collation() { return cmp_collation.collation; } @@ -433,15 +465,9 @@ public: longlong val_int(); String *val_str(String *str); enum Item_result result_type () const { return cached_result_type; } - bool fix_fields(THD *thd,struct st_table_list *tlist, Item **ref) - { - DBUG_ASSERT(fixed == 0); - args[0]->top_level_item(); - return Item_func::fix_fields(thd, tlist, ref); - } + bool fix_fields(THD *, struct st_table_list *, Item **); void fix_length_and_dec(); const char *func_name() const { return "if"; } - table_map not_null_tables() const { return 0; } }; @@ -736,7 +762,7 @@ public: } }; -class Item_func_in :public Item_int_func +class Item_func_in :public Item_func_opt_neg { Item_result cmp_type; in_vector *array; @@ -745,11 +771,12 @@ class Item_func_in :public Item_int_func DTCollation cmp_collation; public: Item_func_in(List<Item> &list) - :Item_int_func(list), array(0), in_item(0), have_null(0) + :Item_func_opt_neg(list), array(0), in_item(0), have_null(0) { allowed_arg_cols= 0; // Fetch this value from first argument } longlong val_int(); + bool fix_fields(THD *, struct st_table_list *, Item **); void fix_length_and_dec(); void cleanup() { @@ -769,12 +796,6 @@ class Item_func_in :public Item_int_func bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - /* - IN() protect from NULL only first argument, if construction like - "expression IN ()" will be allowed, we will need to check number of - argument here, because "NOT(NULL IN ())" is TRUE. - */ - table_map not_null_tables() const { return args[0]->not_null_tables(); } }; /* Functions used by where clause */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 2dd097cbaab..5cb330100f8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -849,7 +849,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (cond_func->functype() == Item_func::BETWEEN) { - if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) + if (!((Item_func_between *)(cond_func))->negated && + cond_func->arguments()[0]->type() == Item::FIELD_ITEM) { Field *field=((Item_field*) (cond_func->arguments()[0]))->field; Item_result cmp_type=field->cmp_type(); @@ -866,7 +867,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (cond_func->functype() == Item_func::IN_FUNC) { // COND OR Item_func_in *func=(Item_func_in*) cond_func; - if (func->key_item()->type() == Item::FIELD_ITEM) + if (!func->negated && func->key_item()->type() == Item::FIELD_ITEM) { Field *field=((Item_field*) (func->key_item()))->field; Item_result cmp_type=field->cmp_type(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index be8ead8e157..6283cad7cc8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2598,7 +2598,12 @@ expr_expr: expr IN_SYM '(' expr_list ')' { $4->push_front($1); $$= new Item_func_in(*$4); } | expr NOT IN_SYM '(' expr_list ')' - { $5->push_front($1); $$= new Item_func_not(new Item_func_in(*$5)); } + { + $5->push_front($1); + Item_func_in *item= new Item_func_in(*$5); + item->negate(); + $$= item; + } | expr IN_SYM in_subselect { $$= new Item_in_subselect($1, $3); } | expr NOT IN_SYM in_subselect @@ -2608,7 +2613,11 @@ expr_expr: | expr BETWEEN_SYM no_and_expr AND_SYM expr { $$= new Item_func_between($1,$3,$5); } | expr NOT BETWEEN_SYM no_and_expr AND_SYM expr - { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } | expr OR_OR_CONCAT expr { $$= or_or_concat(YYTHD, $1,$3); } | expr OR_SYM expr { $$= new Item_cond_or($1,$3); } | expr XOR expr { $$= new Item_cond_xor($1,$3); } @@ -2656,7 +2665,11 @@ no_in_expr: no_in_expr BETWEEN_SYM no_and_expr AND_SYM expr { $$= new Item_func_between($1,$3,$5); } | no_in_expr NOT BETWEEN_SYM no_and_expr AND_SYM expr - { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } | no_in_expr OR_OR_CONCAT expr { $$= or_or_concat(YYTHD, $1,$3); } | no_in_expr OR_SYM expr { $$= new Item_cond_or($1,$3); } | no_in_expr XOR expr { $$= new Item_cond_xor($1,$3); } @@ -2704,7 +2717,12 @@ no_and_expr: no_and_expr IN_SYM '(' expr_list ')' { $4->push_front($1); $$= new Item_func_in(*$4); } | no_and_expr NOT IN_SYM '(' expr_list ')' - { $5->push_front($1); $$= new Item_func_not(new Item_func_in(*$5)); } + { + $5->push_front($1); + Item_func_in *item= new Item_func_in(*$5); + item->negate(); + $$= item; + } | no_and_expr IN_SYM in_subselect { $$= new Item_in_subselect($1, $3); } | no_and_expr NOT IN_SYM in_subselect @@ -2714,7 +2732,11 @@ no_and_expr: | no_and_expr BETWEEN_SYM no_and_expr AND_SYM expr { $$= new Item_func_between($1,$3,$5); } | no_and_expr NOT BETWEEN_SYM no_and_expr AND_SYM expr - { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } | no_and_expr OR_OR_CONCAT expr { $$= or_or_concat(YYTHD, $1,$3); } | no_and_expr OR_SYM expr { $$= new Item_cond_or($1,$3); } | no_and_expr XOR expr { $$= new Item_cond_xor($1,$3); } |