Commit 876f3f4d authored by monty@mashka.mysql.fi's avatar monty@mashka.mysql.fi

Fixed that NULL and 0 returns 0 instead of NULL

This is coded to not cause a speed impact on top level AND expressions where we don't care if an AND expression returns 0 or NULL
parent b0f5c6d6
...@@ -203,7 +203,7 @@ a ...@@ -203,7 +203,7 @@ a
2 2
check table t1; check table t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 check error The handler for the table doesn't support check/repair test.t1 check error The handler for the table doesn't support check
drop table t1; drop table t1;
create table t1 (a int,b varchar(20)) type=bdb; create table t1 (a int,b varchar(20)) type=bdb;
insert into t1 values (1,""), (2,"testing"); insert into t1 values (1,""), (2,"testing");
......
DROP TABLE IF EXISTS t1;
SELECT IF(NULL AND 1, 1, 2), IF(1 AND NULL, 1, 2);
IF(NULL AND 1, 1, 2) IF(1 AND NULL, 1, 2)
2 2
SELECT NULL AND 1, 1 AND NULL, 0 AND NULL, NULL and 0;
NULL AND 1 1 AND NULL 0 AND NULL NULL and 0
NULL NULL 0 0
create table t1 (a int);
insert into t1 values (0),(1),(NULL);
SELECT * FROM t1 WHERE IF(a AND 1, 0, 1);
a
0
NULL
SELECT * FROM t1 WHERE IF(1 AND a, 0, 1);
a
0
NULL
SELECT * FROM t1 where NOT(a AND 1);
a
0
SELECT * FROM t1 where NOT(1 AND a);
a
0
SELECT * FROM t1 where (a AND 1)=0;
a
0
SELECT * FROM t1 where (1 AND a)=0;
a
0
SELECT * FROM t1 where (1 AND a)=1;
a
1
SELECT * FROM t1 where (1 AND a) IS NULL;
a
NULL
SET @a=0, @b=0;
SELECT * FROM t1 WHERE NULL AND (@a:=@a+1);
a
SELECT * FROM t1 WHERE NOT(a>=0 AND NULL AND (@b:=@b+1));
a
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;
...@@ -165,7 +165,7 @@ level id parent_id ...@@ -165,7 +165,7 @@ level id parent_id
1 1007 101 1 1007 101
optimize table t1; optimize table t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 optimize error The handler for the table doesn't support check/repair test.t1 optimize error The handler for the table doesn't support optimize
show keys from t1; show keys from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 0 PRIMARY 1 id A 87 NULL NULL BTREE t1 0 PRIMARY 1 id A 87 NULL NULL BTREE
...@@ -189,7 +189,7 @@ create table t1 (a int) type=innodb; ...@@ -189,7 +189,7 @@ create table t1 (a int) type=innodb;
insert into t1 values (1), (2); insert into t1 values (1), (2);
optimize table t1; optimize table t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 optimize error The handler for the table doesn't support check/repair test.t1 optimize error The handler for the table doesn't support optimize
delete from t1 where a = 1; delete from t1 where a = 1;
select * from t1; select * from t1;
a a
...@@ -208,7 +208,7 @@ create index skr on t1 (a); ...@@ -208,7 +208,7 @@ create index skr on t1 (a);
insert into t1 values (3,""), (4,"testing"); insert into t1 values (3,""), (4,"testing");
analyze table t1; analyze table t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 analyze error The handler for the table doesn't support check/repair test.t1 analyze error The handler for the table doesn't support analyze
show keys from t1; show keys from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 1 skr 1 a A 3 NULL NULL YES BTREE t1 1 skr 1 a A 3 NULL NULL YES BTREE
...@@ -724,7 +724,7 @@ world 2 ...@@ -724,7 +724,7 @@ world 2
hello 1 hello 1
optimize table t1; optimize table t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 optimize error The handler for the table doesn't support check/repair test.t1 optimize error The handler for the table doesn't support optimize
show keys from t1; show keys from t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
t1 0 PRIMARY 1 a A 2 NULL NULL BTREE t1 0 PRIMARY 1 a A 2 NULL NULL BTREE
......
...@@ -30,7 +30,7 @@ SELECT (NULL OR NULL) IS NULL; ...@@ -30,7 +30,7 @@ SELECT (NULL OR NULL) IS NULL;
1 1
select NULL AND 0, 0 and NULL; select NULL AND 0, 0 and NULL;
NULL AND 0 0 and NULL NULL AND 0 0 and NULL
NULL 0 0 0
select inet_ntoa(null),inet_aton(null),inet_aton("122.256"),inet_aton("122.226."),inet_aton(""); select inet_ntoa(null),inet_aton(null),inet_aton("122.256"),inet_aton("122.226."),inet_aton("");
inet_ntoa(null) inet_aton(null) inet_aton("122.256") inet_aton("122.226.") inet_aton("") inet_ntoa(null) inet_aton(null) inet_aton("122.256") inet_aton("122.226.") inet_aton("")
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
......
#
# Test of boolean operations with NULL
#
DROP TABLE IF EXISTS t1;
SELECT IF(NULL AND 1, 1, 2), IF(1 AND NULL, 1, 2);
SELECT NULL AND 1, 1 AND NULL, 0 AND NULL, NULL and 0;
create table t1 (a int);
insert into t1 values (0),(1),(NULL);
SELECT * FROM t1 WHERE IF(a AND 1, 0, 1);
SELECT * FROM t1 WHERE IF(1 AND a, 0, 1);
SELECT * FROM t1 where NOT(a AND 1);
SELECT * FROM t1 where NOT(1 AND a);
SELECT * FROM t1 where (a AND 1)=0;
SELECT * FROM t1 where (1 AND a)=0;
SELECT * FROM t1 where (1 AND a)=1;
SELECT * FROM t1 where (1 AND a) IS NULL;
# Verify that NULL optimisation works in AND clause:
SET @a=0, @b=0;
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;
...@@ -204,7 +204,7 @@ else ...@@ -204,7 +204,7 @@ else
fi fi
USER_OPTION="" USER_OPTION=""
if test "x$USER" = "xroot" if test -w / -o "$USER" = "root"
then then
if test "$user" != "root" -o $SET_USER = 1 if test "$user" != "root" -o $SET_USER = 1
then then
......
...@@ -85,6 +85,7 @@ class Item { ...@@ -85,6 +85,7 @@ class Item {
virtual bool get_time(TIME *ltime); virtual bool get_time(TIME *ltime);
virtual bool is_null() { return 0; } virtual bool is_null() { return 0; }
virtual unsigned int size_of()= 0; virtual unsigned int size_of()= 0;
virtual void top_level_item() {}
}; };
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
/* /*
Test functions Test functions
These returns 0LL if false and 1LL if true and null if some arg is null Most of these returns 0LL if false and 1LL if true and
'AND' and 'OR' never return null NULL if some arg is NULL.
*/ */
longlong Item_func_not::val_int() longlong Item_func_not::val_int()
...@@ -533,6 +533,7 @@ Item_func_if::fix_length_and_dec() ...@@ -533,6 +533,7 @@ Item_func_if::fix_length_and_dec()
else else
cached_result_type=arg1_type; // Should be INT_RESULT cached_result_type=arg1_type; // Should be INT_RESULT
} }
args[0]->top_level_item();
} }
...@@ -1128,6 +1129,8 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables) ...@@ -1128,6 +1129,8 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables)
const_item_cache&=item->const_item(); const_item_cache&=item->const_item();
if (item->maybe_null) if (item->maybe_null)
maybe_null=1; maybe_null=1;
if (abort_on_null)
item->top_level_item();
} }
if (thd) if (thd)
thd->cond_count+=list.elements; thd->cond_count+=list.elements;
...@@ -1196,28 +1199,41 @@ void Item_cond::print(String *str) ...@@ -1196,28 +1199,41 @@ void Item_cond::print(String *str)
str->append(')'); str->append(')');
} }
/*
Evalution of AND(expr, expr, expr ...)
NOTES:
abort_if_null is set for AND expressions for which we don't care if the
result is NULL or 0. This is set for:
- WHERE clause
- HAVING clause
- IF(expression)
RETURN VALUES
1 If all expressions are true
0 If all expressions are false or if we find a NULL expression and
'abort_on_null' is set.
NULL if all expression are either 1 or NULL
*/
longlong Item_cond_and::val_int() longlong Item_cond_and::val_int()
{ {
List_iterator_fast<Item> li(list); List_iterator_fast<Item> li(list);
Item *item; Item *item;
null_value= 0;
while ((item=li++)) while ((item=li++))
{ {
if (item->val_int() == 0) if (item->val_int() == 0)
{ {
/* if (abort_on_null || !(null_value= item->null_value))
TODO: In case of NULL, ANSI would require us to continue evaluation return 0; // return FALSE
until we get a FALSE value or run out of values; This would
require a lot of unnecessary evaluation, which we skip for now
*/
null_value=item->null_value;
return 0;
} }
} }
null_value=0; return null_value ? 0 : 1;
return 1;
} }
longlong Item_cond_or::val_int() longlong Item_cond_or::val_int()
{ {
List_iterator_fast<Item> li(list); List_iterator_fast<Item> li(list);
...@@ -1260,15 +1276,15 @@ longlong Item_cond_or::val_int() ...@@ -1260,15 +1276,15 @@ longlong Item_cond_or::val_int()
Item *and_expressions(Item *a, Item *b, Item **org_item) Item *and_expressions(Item *a, Item *b, Item **org_item)
{ {
if (!a) if (!a)
return (*org_item= (Item*) b); return (*org_item= b);
if (a == *org_item) if (a == *org_item)
{ {
Item_cond *res; Item_cond *res;
if ((res= new Item_cond_and(a, (Item*) b))) if ((res= new Item_cond_and(a, b)))
res->used_tables_cache= a->used_tables() | b->used_tables(); res->used_tables_cache= a->used_tables() | b->used_tables();
return res; return res;
} }
if (((Item_cond_and*) a)->add((Item*) b)) if (((Item_cond_and*) a)->add(b))
return 0; return 0;
((Item_cond_and*) a)->used_tables_cache|= b->used_tables(); ((Item_cond_and*) a)->used_tables_cache|= b->used_tables();
return a; return a;
......
...@@ -560,9 +560,11 @@ class Item_cond :public Item_bool_func ...@@ -560,9 +560,11 @@ class Item_cond :public Item_bool_func
{ {
protected: protected:
List<Item> list; List<Item> list;
bool abort_on_null;
public: public:
Item_cond() : Item_bool_func() { const_item_cache=0; } /* Item_cond() is only used to create top level items */
Item_cond(Item *i1,Item *i2) :Item_bool_func() 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)
{ list.push_back(i1); list.push_back(i2); } { list.push_back(i1); list.push_back(i2); }
~Item_cond() { list.delete_elements(); } ~Item_cond() { list.delete_elements(); }
bool add(Item *item) { return list.push_back(item); } bool add(Item *item) { return list.push_back(item); }
...@@ -576,6 +578,7 @@ class Item_cond :public Item_bool_func ...@@ -576,6 +578,7 @@ class Item_cond :public Item_bool_func
void split_sum_func(List<Item> &fields); void split_sum_func(List<Item> &fields);
friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
unsigned int size_of() { return sizeof(*this);} unsigned int size_of() { return sizeof(*this);}
void top_level_item() { abort_on_null=1; }
}; };
......
...@@ -1976,6 +1976,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ...@@ -1976,6 +1976,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
Item_cond_and *cond_and=new Item_cond_and(); Item_cond_and *cond_and=new Item_cond_and();
if (!cond_and) // If not out of memory if (!cond_and) // If not out of memory
DBUG_RETURN(1); DBUG_RETURN(1);
cond_and->top_level_item();
uint i,j; uint i,j;
for (i=0 ; i < t1->fields ; i++) for (i=0 ; i < t1->fields ; i++)
......
...@@ -3324,6 +3324,8 @@ static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) ...@@ -3324,6 +3324,8 @@ static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result)
void add_join_on(TABLE_LIST *b,Item *expr) void add_join_on(TABLE_LIST *b,Item *expr)
{ {
if (expr)
{
if (!b->on_expr) if (!b->on_expr)
b->on_expr=expr; b->on_expr=expr;
else else
...@@ -3331,6 +3333,8 @@ void add_join_on(TABLE_LIST *b,Item *expr) ...@@ -3331,6 +3333,8 @@ void add_join_on(TABLE_LIST *b,Item *expr)
// This only happens if you have both a right and left join // This only happens if you have both a right and left join
b->on_expr=new Item_cond_and(b->on_expr,expr); b->on_expr=new Item_cond_and(b->on_expr,expr);
} }
b->on_expr->top_level_item();
}
} }
......
...@@ -360,6 +360,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ...@@ -360,6 +360,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
{ {
conds->fix_fields(thd,tables); conds->fix_fields(thd,tables);
conds->change_ref_to_fields(thd,tables); conds->change_ref_to_fields(thd,tables);
conds->top_level_item();
having=0; having=0;
} }
} }
...@@ -869,6 +870,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ...@@ -869,6 +870,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
sort_table_cond))) sort_table_cond)))
goto err; goto err;
table->select_cond=table->select->cond; table->select_cond=table->select->cond;
table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(table->select->cond, DBUG_EXECUTE("where",print_where(table->select->cond,
"select and having");); "select and having"););
having=make_cond_for_table(having,~ (table_map) 0,~used_tables); having=make_cond_for_table(having,~ (table_map) 0,~used_tables);
...@@ -5490,6 +5492,7 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) ...@@ -5490,6 +5492,7 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table)
{ {
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
{ {
/* Create new top level AND item */
Item_cond_and *new_cond=new Item_cond_and; Item_cond_and *new_cond=new Item_cond_and;
if (!new_cond) if (!new_cond)
DBUG_RETURN((COND*) 0); // OOM /* purecov: inspected */ DBUG_RETURN((COND*) 0); // OOM /* purecov: inspected */
...@@ -5527,6 +5530,7 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) ...@@ -5527,6 +5530,7 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table)
new_cond->argument_list()->push_back(fix); new_cond->argument_list()->push_back(fix);
} }
new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache; new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache;
new_cond->top_level_item();
DBUG_RETURN(new_cond); DBUG_RETURN(new_cond);
} }
} }
...@@ -5886,6 +5890,7 @@ static bool fix_having(JOIN *join, Item **having) ...@@ -5886,6 +5890,7 @@ static bool fix_having(JOIN *join, Item **having)
sort_table_cond))) sort_table_cond)))
DBUG_RETURN(1); DBUG_RETURN(1);
table->select_cond=table->select->cond; table->select_cond=table->select->cond;
table->select_cond->top_level_item();
DBUG_EXECUTE("where",print_where(table->select_cond, DBUG_EXECUTE("where",print_where(table->select_cond,
"select and having");); "select and having"););
*having=make_cond_for_table(*having,~ (table_map) 0,~used_tables); *having=make_cond_for_table(*having,~ (table_map) 0,~used_tables);
......
...@@ -2178,15 +2178,25 @@ opt_table_alias: ...@@ -2178,15 +2178,25 @@ opt_table_alias:
where_clause: where_clause:
/* empty */ { Select->where= 0; } /* empty */ { Select->where= 0; }
| WHERE expr { Select->where= $2; }; | WHERE expr
{
Select->where= $2;
if ($2)
$2->top_level_item();
}
;
having_clause: having_clause:
/* empty */ /* empty */
| HAVING { Select->create_refs=1; } expr | HAVING { Select->create_refs=1; } expr
{ {
SELECT_LEX *sel=Select; SELECT_LEX *sel=Select;
sel->having= $3; sel->create_refs=0; sel->having= $3;
}; sel->create_refs=0;
if ($3)
$3->top_level_item();
}
;
opt_escape: opt_escape:
ESCAPE_SYM TEXT_STRING { $$= $2.str; } ESCAPE_SYM TEXT_STRING { $$= $2.str; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment