Commit 2c28412e authored by Sergey Petrunya's avatar Sergey Petrunya

Port of code for: (part of testcase is in mysql-test/t/subquery*.test and will...

Port of code for: (part of testcase is in mysql-test/t/subquery*.test and will be ported separately)

Bug#11766642: crash in Item_field::register_field_in_read_map 
              with view

(Former 59793)

Prior to the refactoring in this patch, Item_cond_xor behaved 
partially as an Item_cond and partially as an Item_func. The
reasoning behind this was that XOR is currently not optimized
(thus should be Item_func instead of Item_cond), but it was 
planned optimize it in the future (thus, made Item_cond anyway 
to ease optimization later). 

Even though Item_cond inherits from Item_func, there are 
differences between these two. One difference is that the 
arguments are stored differently. Item_cond stores them in a 
list while Item_func store them in an args[]. 

BUG no 45221 was caused by Item_cond_xor storing arguments in 
the list while users of the objects would look for them in 
args[]. The fix back then was to store the arguments in both 
locations.

In this bug, Item_cond_xor initially gets two Item_field 
arguments. These are stored in the list inherited from 
Item_cond and in args[] inherited from Item_func. During
resolution, find_field_in_view() replaces the Item_fields 
stored in the list with Item_direct_view_refs, but args[] 
still points to the unresolved Item_fields. This shows that 
the fix for 45221 was incorrect.

The refactoring performed in this patch removes the confusion
by making the XOR item an Item_func period. A neg_transformer() 
is also implemented for Item_func_xor to improve performance 
when negating XOR expressions. An XOR is negated by negating 
one of the operands.
parent 62cc4df4
......@@ -375,6 +375,121 @@ a
13
14
15
# XOR (Note: XOR is negated by negating one of the operands)
# Should return 6,7
SELECT * FROM t1 WHERE ((a > 5) XOR (a > 7));
a
6
7
# Should return 0..5,8..19
SELECT * FROM t1 WHERE ((NOT (a > 5)) XOR (a > 7));
a
0
1
2
3
4
5
8
9
10
11
12
13
14
15
16
17
18
19
SELECT * FROM t1 WHERE ((a > 5) XOR (NOT (a > 7)));
a
0
1
2
3
4
5
8
9
10
11
12
13
14
15
16
17
18
19
SELECT * FROM t1 WHERE NOT ((a > 5) XOR (a > 7));
a
0
1
2
3
4
5
8
9
10
11
12
13
14
15
16
17
18
19
# Should return 6,7
SELECT * FROM t1 WHERE NOT ((NOT (a > 5)) XOR (a > 7));
a
6
7
SELECT * FROM t1 WHERE NOT ((a > 5) XOR (NOT (a > 7)));
a
6
7
# Should return 0..5,8..19
SELECT * FROM t1 WHERE NOT ((NOT (a > 5)) XOR (NOT (a > 7)));
a
0
1
2
3
4
5
8
9
10
11
12
13
14
15
16
17
18
19
# Should have empty result
SELECT * FROM t1 WHERE (NULL XOR (a > 7));
a
SELECT * FROM t1 WHERE NOT (NULL XOR (a > 7));
a
# Should be simplified to "...WHERE (a XOR a)
EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT ((NOT a) XOR (a));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index NULL a 5 NULL 21 100.00 Using where; Using index
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` xor `test`.`t1`.`a`)
# Should be simplified to "...WHERE (a XOR a)
EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT (a XOR (NOT a));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index NULL a 5 NULL 21 100.00 Using where; Using index
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` xor `test`.`t1`.`a`)
# End XOR
delete from t1 where a > 3;
select a, not(not(a)) from t1;
a not(not(a))
......
......@@ -65,6 +65,35 @@ 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)));
--echo # XOR (Note: XOR is negated by negating one of the operands)
--echo # Should return 6,7
SELECT * FROM t1 WHERE ((a > 5) XOR (a > 7));
--echo # Should return 0..5,8..19
SELECT * FROM t1 WHERE ((NOT (a > 5)) XOR (a > 7));
SELECT * FROM t1 WHERE ((a > 5) XOR (NOT (a > 7)));
SELECT * FROM t1 WHERE NOT ((a > 5) XOR (a > 7));
--echo # Should return 6,7
SELECT * FROM t1 WHERE NOT ((NOT (a > 5)) XOR (a > 7));
SELECT * FROM t1 WHERE NOT ((a > 5) XOR (NOT (a > 7)));
--echo # Should return 0..5,8..19
SELECT * FROM t1 WHERE NOT ((NOT (a > 5)) XOR (NOT (a > 7)));
--echo # Should have empty result
SELECT * FROM t1 WHERE (NULL XOR (a > 7));
SELECT * FROM t1 WHERE NOT (NULL XOR (a > 7));
--echo # Should be simplified to "...WHERE (a XOR a)
EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT ((NOT a) XOR (a));
--echo # Should be simplified to "...WHERE (a XOR a)
EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT (a XOR (NOT a));
--echo # End XOR
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));
......
......@@ -5065,23 +5065,21 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
very fast to use.
*/
longlong Item_cond_xor::val_int()
longlong Item_func_xor::val_int()
{
DBUG_ASSERT(fixed == 1);
List_iterator<Item> li(list);
Item *item;
int result=0;
null_value=0;
while ((item=li++))
int result= 0;
null_value= false;
for (uint i= 0; i < arg_count; i++)
{
result^= (item->val_int() != 0);
if (item->null_value)
result^= (args[i]->val_int() != 0);
if (args[i]->null_value)
{
null_value=1;
null_value= true;
return 0;
}
}
return (longlong) result;
return result;
}
/**
......@@ -5122,6 +5120,33 @@ Item *Item_bool_rowready_func2::neg_transformer(THD *thd)
return item;
}
/**
XOR can be negated by negating one of the operands:
NOT (a XOR b) => (NOT a) XOR b
=> a XOR (NOT b)
@param thd Thread handle
@return New negated item
*/
Item *Item_func_xor::neg_transformer(THD *thd)
{
Item *neg_operand;
Item_func_xor *new_item;
if ((neg_operand= args[0]->neg_transformer(thd)))
// args[0] has neg_tranformer
new_item= new(thd->mem_root) Item_func_xor(neg_operand, args[1]);
else if ((neg_operand= args[1]->neg_transformer(thd)))
// args[1] has neg_tranformer
new_item= new(thd->mem_root) Item_func_xor(args[0], neg_operand);
else
{
neg_operand= new(thd->mem_root) Item_func_not(args[0]);
new_item= new(thd->mem_root) Item_func_xor(neg_operand, args[1]);
}
return new_item;
}
/**
a IS NULL -> a IS NOT NULL.
......
......@@ -391,6 +391,22 @@ class Item_bool_rowready_func2 :public Item_bool_func2
}
};
/**
XOR inherits from Item_bool_func2 because it is not optimized yet.
Later, when XOR is optimized, it needs to inherit from
Item_cond instead. See WL#5800.
*/
class Item_func_xor :public Item_bool_func2
{
public:
Item_func_xor(Item *i1, Item *i2) :Item_bool_func2(i1, i2) {}
enum Functype functype() const { return XOR_FUNC; }
const char *func_name() const { return "xor"; }
longlong val_int();
void top_level_item() {}
Item *neg_transformer(THD *thd);
};
class Item_func_not :public Item_bool_func
{
public:
......@@ -1817,45 +1833,6 @@ inline bool is_cond_or(Item *item)
return (cond_item->functype() == Item_func::COND_OR_FUNC);
}
/*
XOR is Item_cond, not an Item_int_func because we could like to
optimize (a XOR b) later on. It's low prio, though
*/
class Item_cond_xor :public Item_cond
{
public:
Item_cond_xor(Item *i1,Item *i2) :Item_cond(i1,i2)
{
/*
Items must be stored in args[] as well because this Item_cond is
treated as a FUNC_ITEM (see type()). I.e., users of it will get
it's children by calling arguments(), not argument_list(). This
is a temporary solution until XOR is optimized and treated like
a full Item_cond citizen.
*/
arg_count= 2;
args= tmp_arg;
args[0]= i1;
args[1]= i2;
}
enum Functype functype() const { return COND_XOR_FUNC; }
/* TODO: remove the next line when implementing XOR optimization */
enum Type type() const { return FUNC_ITEM; }
longlong val_int();
const char *func_name() const { return "xor"; }
void top_level_item() {}
/* Since child Items are stored in args[], Items cannot be added.
However, since Item_cond_xor is treated as a FUNC_ITEM (see
type()), the methods below should never be called.
*/
bool add(Item *item) { DBUG_ASSERT(FALSE); return FALSE; }
bool add_at_head(Item *item) { DBUG_ASSERT(FALSE); return FALSE; }
bool add_at_head(List<Item> *nlist) { DBUG_ASSERT(FALSE); return FALSE; }
void copy_andor_arguments(THD *thd, Item_cond *item) { DBUG_ASSERT(FALSE); }
};
/* Some useful inline functions */
inline Item *and_conds(Item *a, Item *b)
......
......@@ -44,7 +44,7 @@ class Item_func :public Item_result_field
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
GE_FUNC,GT_FUNC,FT_FUNC,
LIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC,
COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC,
COND_AND_FUNC, COND_OR_FUNC, XOR_FUNC,
BETWEEN, IN_FUNC, MULT_EQUAL_FUNC,
INTERVAL_FUNC, ISNOTNULLTEST_FUNC,
SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC,
......
......@@ -7172,7 +7172,7 @@ expr:
| expr XOR expr %prec XOR
{
/* XOR is a proprietary extension */
$$ = new (YYTHD->mem_root) Item_cond_xor($1, $3);
$$ = new (YYTHD->mem_root) Item_func_xor($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
......
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