Fix for BUG#32694 "NOT NULL table field in a subquery produces invalid results"

  
The problem was that when convert_constant_item is called for subqueries,
this happens when we already started executing the top-level query, and
the field argument of convert_constant_item pointed to a valid table row.
In turn convert_constant_item used the field buffer to compute the value
of its item argument. This copied the item's value into the field,
and made equalities with outer references always true.
  
The fix saves/restores the original field's value when it belongs to an
outer table.
parent 987ec3f3
...@@ -505,4 +505,59 @@ select sum(a) from t1 group by convert(a, datetime); ...@@ -505,4 +505,59 @@ select sum(a) from t1 group by convert(a, datetime);
sum(a) sum(a)
NULL NULL
drop table t1; drop table t1;
create table t1 (id int(10) not null, cur_date datetime not null);
create table t2 (id int(10) not null, cur_date date not null);
insert into t1 (id, cur_date) values (1, '2007-04-25 18:30:22');
insert into t2 (id, cur_date) values (1, '2007-04-25');
explain extended
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
Note 1276 Field or reference 'test.t1.cur_date' of SELECT #2 was resolved in SELECT #1
Note 1003 select '1' AS `id`,'2007-04-25 18:30:22' AS `cur_date` from `test`.`t1` where <in_optimizer>('1',<exists>(select 1 AS `Not_used` from `test`.`t1` `x1` where 0))
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
id cur_date
explain extended
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
Note 1276 Field or reference 'test.t2.cur_date' of SELECT #2 was resolved in SELECT #1
Note 1003 select '1' AS `id`,'2007-04-25' AS `cur_date` from `test`.`t2` where <in_optimizer>('1',<exists>(select 1 AS `Not_used` from `test`.`t2` `x1` where 0))
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
id cur_date
insert into t1 (id, cur_date) values (2, '2007-04-26 18:30:22');
insert into t2 (id, cur_date) values (2, '2007-04-26');
explain extended
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY x1 ALL NULL NULL NULL NULL 2 Using where
Warnings:
Note 1276 Field or reference 'test.t1.cur_date' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`cur_date` AS `cur_date` from `test`.`t1` where <in_optimizer>(`test`.`t1`.`id`,<exists>(select 1 AS `Not_used` from `test`.`t1` `x1` where ((`test`.`t1`.`cur_date` = 0) and (<cache>(`test`.`t1`.`id`) = `test`.`x1`.`id`))))
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
id cur_date
explain extended
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
2 DEPENDENT SUBQUERY x1 ALL NULL NULL NULL NULL 2 Using where
Warnings:
Note 1276 Field or reference 'test.t2.cur_date' of SELECT #2 was resolved in SELECT #1
Note 1003 select `test`.`t2`.`id` AS `id`,`test`.`t2`.`cur_date` AS `cur_date` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`id`,<exists>(select 1 AS `Not_used` from `test`.`t2` `x1` where ((`test`.`t2`.`cur_date` = 0) and (<cache>(`test`.`t2`.`id`) = `test`.`x1`.`id`))))
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
id cur_date
drop table t1,t2;
End of 5.0 tests End of 5.0 tests
...@@ -338,4 +338,41 @@ insert into t1 values (), (), (); ...@@ -338,4 +338,41 @@ insert into t1 values (), (), ();
select sum(a) from t1 group by convert(a, datetime); select sum(a) from t1 group by convert(a, datetime);
drop table t1; drop table t1;
#
# Bug #32694: NOT NULL table field in a subquery produces invalid results
#
create table t1 (id int(10) not null, cur_date datetime not null);
create table t2 (id int(10) not null, cur_date date not null);
insert into t1 (id, cur_date) values (1, '2007-04-25 18:30:22');
insert into t2 (id, cur_date) values (1, '2007-04-25');
explain extended
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
explain extended
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
insert into t1 (id, cur_date) values (2, '2007-04-26 18:30:22');
insert into t2 (id, cur_date) values (2, '2007-04-26');
explain extended
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
select * from t1
where id in (select id from t1 as x1 where (t1.cur_date is null));
explain extended
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
select * from t2
where id in (select id from t2 as x1 where (t2.cur_date is null));
drop table t1,t2;
--echo End of 5.0 tests --echo End of 5.0 tests
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
#include <m_ctype.h> #include <m_ctype.h>
#include "sql_select.h" #include "sql_select.h"
static bool convert_constant_item(THD *thd, Field *field, Item **item); static bool convert_constant_item(THD *thd, Item_field *field_item,
Item **item);
static Item_result item_store_type(Item_result a, Item *item, static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag) my_bool unsigned_flag)
...@@ -317,7 +318,7 @@ longlong Item_func_nop_all::val_int() ...@@ -317,7 +318,7 @@ longlong Item_func_nop_all::val_int()
SYNOPSIS SYNOPSIS
convert_constant_item() convert_constant_item()
thd thread handle thd thread handle
field item will be converted using the type of this field field_item item will be converted using the type of this field
item [in/out] reference to the item to convert item [in/out] reference to the item to convert
DESCRIPTION DESCRIPTION
...@@ -340,30 +341,46 @@ longlong Item_func_nop_all::val_int() ...@@ -340,30 +341,46 @@ longlong Item_func_nop_all::val_int()
1 Item was replaced with an integer version of the item 1 Item was replaced with an integer version of the item
*/ */
static bool convert_constant_item(THD *thd, Field *field, Item **item) static bool convert_constant_item(THD *thd, Item_field *field_item,
Item **item)
{ {
Field *field= field_item->field;
int result= 0;
if (!(*item)->with_subselect && (*item)->const_item()) if (!(*item)->with_subselect && (*item)->const_item())
{ {
/* For comparison purposes allow invalid dates like 2000-01-32 */ /* For comparison purposes allow invalid dates like 2000-01-32 */
ulong orig_sql_mode= thd->variables.sql_mode; ulong orig_sql_mode= thd->variables.sql_mode;
enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields; enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields;
ulonglong orig_field_val; /* original field value if valid */
thd->variables.sql_mode= (orig_sql_mode & ~MODE_NO_ZERO_DATE) | thd->variables.sql_mode= (orig_sql_mode & ~MODE_NO_ZERO_DATE) |
MODE_INVALID_DATES; MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE; thd->count_cuted_fields= CHECK_FIELD_IGNORE;
/*
Store the value of the field if it references an outer field because
the call to save_in_field below overrides that value.
*/
if (field_item->depended_from)
orig_field_val= field->val_int();
if (!(*item)->is_null() && !(*item)->save_in_field(field, 1)) if (!(*item)->is_null() && !(*item)->save_in_field(field, 1))
{ {
Item *tmp=new Item_int_with_ref(field->val_int(), *item, Item *tmp=new Item_int_with_ref(field->val_int(), *item,
test(field->flags & UNSIGNED_FLAG)); test(field->flags & UNSIGNED_FLAG));
thd->variables.sql_mode= orig_sql_mode;
thd->count_cuted_fields= orig_count_cuted_fields;
if (tmp) if (tmp)
thd->change_item_tree(item, tmp); thd->change_item_tree(item, tmp);
return 1; // Item was replaced result= 1; // Item was replaced
}
/* Restore the original field value. */
if (field_item->depended_from)
{
result= field->store(orig_field_val, TRUE);
/* orig_field_val must be a valid value that can be restored back. */
DBUG_ASSERT(!result);
} }
thd->variables.sql_mode= orig_sql_mode; thd->variables.sql_mode= orig_sql_mode;
thd->count_cuted_fields= orig_count_cuted_fields; thd->count_cuted_fields= orig_count_cuted_fields;
} }
return 0; return result;
} }
...@@ -411,15 +428,14 @@ void Item_bool_func2::fix_length_and_dec() ...@@ -411,15 +428,14 @@ void Item_bool_func2::fix_length_and_dec()
thd= current_thd; thd= current_thd;
if (!thd->is_context_analysis_only()) if (!thd->is_context_analysis_only())
{ {
Item *arg_real_item= args[0]->real_item(); if (args[0]->real_item()->type() == FIELD_ITEM)
if (arg_real_item->type() == FIELD_ITEM)
{ {
Field *field=((Item_field*) arg_real_item)->field; Item_field *field_item= (Item_field*) (args[0]->real_item());
if (field->can_be_compared_as_longlong() && if (field_item->field->can_be_compared_as_longlong() &&
!(arg_real_item->is_datetime() && !(field_item->is_datetime() &&
args[1]->result_type() == STRING_RESULT)) args[1]->result_type() == STRING_RESULT))
{ {
if (convert_constant_item(thd, field,&args[1])) if (convert_constant_item(thd, field_item, &args[1]))
{ {
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
INT_RESULT); // Works for all types. INT_RESULT); // Works for all types.
...@@ -428,15 +444,14 @@ void Item_bool_func2::fix_length_and_dec() ...@@ -428,15 +444,14 @@ void Item_bool_func2::fix_length_and_dec()
} }
} }
} }
arg_real_item= args[1]->real_item(); if (args[1]->real_item()->type() == FIELD_ITEM)
if (arg_real_item->type() == FIELD_ITEM)
{ {
Field *field=((Item_field*) arg_real_item)->field; Item_field *field_item= (Item_field*) (args[1]->real_item());
if (field->can_be_compared_as_longlong() && if (field_item->field->can_be_compared_as_longlong() &&
!(arg_real_item->is_datetime() && !(field_item->is_datetime() &&
args[0]->result_type() == STRING_RESULT)) args[0]->result_type() == STRING_RESULT))
{ {
if (convert_constant_item(thd, field,&args[0])) if (convert_constant_item(thd, field_item, &args[0]))
{ {
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
INT_RESULT); // Works for all types. INT_RESULT); // Works for all types.
...@@ -1889,16 +1904,16 @@ void Item_func_between::fix_length_and_dec() ...@@ -1889,16 +1904,16 @@ void Item_func_between::fix_length_and_dec()
thd->lex->sql_command != SQLCOM_CREATE_VIEW && thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
thd->lex->sql_command != SQLCOM_SHOW_CREATE) thd->lex->sql_command != SQLCOM_SHOW_CREATE)
{ {
Field *field=((Item_field*) (args[0]->real_item()))->field; Item_field *field_item= (Item_field*) (args[0]->real_item());
if (field->can_be_compared_as_longlong()) if (field_item->field->can_be_compared_as_longlong())
{ {
/* /*
The following can't be recoded with || as convert_constant_item The following can't be recoded with || as convert_constant_item
changes the argument changes the argument
*/ */
if (convert_constant_item(thd, field,&args[1])) if (convert_constant_item(thd, field_item, &args[1]))
cmp_type=INT_RESULT; // Works for all types. cmp_type=INT_RESULT; // Works for all types.
if (convert_constant_item(thd, field,&args[2])) if (convert_constant_item(thd, field_item, &args[2]))
cmp_type=INT_RESULT; // Works for all types. cmp_type=INT_RESULT; // Works for all types.
} }
} }
...@@ -3491,13 +3506,13 @@ void Item_func_in::fix_length_and_dec() ...@@ -3491,13 +3506,13 @@ void Item_func_in::fix_length_and_dec()
thd->lex->sql_command != SQLCOM_SHOW_CREATE && thd->lex->sql_command != SQLCOM_SHOW_CREATE &&
cmp_type != INT_RESULT) cmp_type != INT_RESULT)
{ {
Field *field= ((Item_field*) (args[0]->real_item()))->field; Item_field *field_item= (Item_field*) (args[0]->real_item());
if (field->can_be_compared_as_longlong()) if (field_item->field->can_be_compared_as_longlong())
{ {
bool all_converted= TRUE; bool all_converted= TRUE;
for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++)
{ {
if (!convert_constant_item (thd, field, &arg[0])) if (!convert_constant_item (thd, field_item, &arg[0]))
all_converted= FALSE; all_converted= FALSE;
} }
if (all_converted) if (all_converted)
......
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