summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <aivanov@mysql.com>2005-09-06 18:03:08 +0400
committerunknown <aivanov@mysql.com>2005-09-06 18:03:08 +0400
commitf1cc5049810a0bcf701fe994aa3c4f6c5bbae8ab (patch)
treed6ae557d23c29875371111ed32a93908ec4af8f1 /sql
parentf53de1f247373f56f30238c18893d6e01bad4d95 (diff)
downloadmariadb-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.cc158
-rw-r--r--sql/item_cmpfunc.h55
-rw-r--r--sql/opt_range.cc5
-rw-r--r--sql/sql_yacc.yy32
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); }