Commit f17a536e authored by evgen@sunlight.local's avatar evgen@sunlight.local

Fixed bug#16861: User defined variable can have a wrong value if a tmp table was

used.

Sorting by RAND() uses a temporary table in order to get a correct results.
User defined variable was set during filling the temporary table and later
on it is substituted for its value from the temporary table. Due to this
it contains the last value stored in the temporary table.

Now if the result_field is set for the Item_func_set_user_var object it 
updates variable from the result_field value when being sent to a client.

The Item_func_set_user_var::check() now accepts a use_result_field
parameter. Depending on its value the result_field or the args[0] is used
to get current value.
parent 07a1ed65
...@@ -292,3 +292,12 @@ SELECT @a; ...@@ -292,3 +292,12 @@ SELECT @a;
@a @a
18446744071710965857 18446744071710965857
drop table bigfailure; drop table bigfailure;
create table t1(f1 int, f2 int);
insert into t1 values (1,2),(2,3),(3,1);
select @var:=f2 from t1 group by f1 order by f2 desc limit 1;
@var:=f2
3
select @var;
@var
3
drop table t1;
...@@ -202,3 +202,13 @@ SET @a := (select * from bigfailure where afield = (SELECT afield FROM bigfailur ...@@ -202,3 +202,13 @@ SET @a := (select * from bigfailure where afield = (SELECT afield FROM bigfailur
SELECT @a; SELECT @a;
drop table bigfailure; drop table bigfailure;
#
# Bug#16861: User defined variable can have a wrong value if a tmp table was
# used.
#
create table t1(f1 int, f2 int);
insert into t1 values (1,2),(2,3),(3,1);
select @var:=f2 from t1 group by f1 order by f2 desc limit 1;
select @var;
drop table t1;
...@@ -548,7 +548,7 @@ void Item_func::signal_divide_by_null() ...@@ -548,7 +548,7 @@ void Item_func::signal_divide_by_null()
Item *Item_func::get_tmp_table_item(THD *thd) Item *Item_func::get_tmp_table_item(THD *thd)
{ {
if (!with_sum_func && !const_item()) if (!with_sum_func && !const_item() && functype() != SUSERVAR_FUNC)
return new Item_field(result_field); return new Item_field(result_field);
return copy_or_same(thd); return copy_or_same(thd);
} }
...@@ -3719,30 +3719,38 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) ...@@ -3719,30 +3719,38 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val)
*/ */
bool bool
Item_func_set_user_var::check() Item_func_set_user_var::check(bool use_result_field)
{ {
DBUG_ENTER("Item_func_set_user_var::check"); DBUG_ENTER("Item_func_set_user_var::check");
if (use_result_field)
DBUG_ASSERT(result_field);
switch (cached_result_type) { switch (cached_result_type) {
case REAL_RESULT: case REAL_RESULT:
{ {
save_result.vreal= args[0]->val_real(); save_result.vreal= use_result_field ? result_field->val_real() :
args[0]->val_real();
break; break;
} }
case INT_RESULT: case INT_RESULT:
{ {
save_result.vint= args[0]->val_int(); save_result.vint= use_result_field ? result_field->val_int() :
unsigned_flag= args[0]->unsigned_flag; args[0]->val_int();
unsigned_flag= use_result_field ? ((Field_num*)result_field)->unsigned_flag:
args[0]->unsigned_flag;
break; break;
} }
case STRING_RESULT: case STRING_RESULT:
{ {
save_result.vstr= args[0]->val_str(&value); save_result.vstr= use_result_field ? result_field->val_str(&value) :
args[0]->val_str(&value);
break; break;
} }
case DECIMAL_RESULT: case DECIMAL_RESULT:
{ {
save_result.vdec= args[0]->val_decimal(&decimal_buff); save_result.vdec= use_result_field ?
result_field->val_decimal(&decimal_buff) :
args[0]->val_decimal(&decimal_buff);
break; break;
} }
case ROW_RESULT: case ROW_RESULT:
...@@ -3828,7 +3836,7 @@ Item_func_set_user_var::update() ...@@ -3828,7 +3836,7 @@ Item_func_set_user_var::update()
double Item_func_set_user_var::val_real() double Item_func_set_user_var::val_real()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
check(); check(0);
update(); // Store expression update(); // Store expression
return entry->val_real(&null_value); return entry->val_real(&null_value);
} }
...@@ -3836,7 +3844,7 @@ double Item_func_set_user_var::val_real() ...@@ -3836,7 +3844,7 @@ double Item_func_set_user_var::val_real()
longlong Item_func_set_user_var::val_int() longlong Item_func_set_user_var::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
check(); check(0);
update(); // Store expression update(); // Store expression
return entry->val_int(&null_value); return entry->val_int(&null_value);
} }
...@@ -3844,7 +3852,7 @@ longlong Item_func_set_user_var::val_int() ...@@ -3844,7 +3852,7 @@ longlong Item_func_set_user_var::val_int()
String *Item_func_set_user_var::val_str(String *str) String *Item_func_set_user_var::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
check(); check(0);
update(); // Store expression update(); // Store expression
return entry->val_str(&null_value, str, decimals); return entry->val_str(&null_value, str, decimals);
} }
...@@ -3853,7 +3861,7 @@ String *Item_func_set_user_var::val_str(String *str) ...@@ -3853,7 +3861,7 @@ String *Item_func_set_user_var::val_str(String *str)
my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val) my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
check(); check(0);
update(); // Store expression update(); // Store expression
return entry->val_decimal(&null_value, val); return entry->val_decimal(&null_value, val);
} }
...@@ -3878,6 +3886,16 @@ void Item_func_set_user_var::print_as_stmt(String *str) ...@@ -3878,6 +3886,16 @@ void Item_func_set_user_var::print_as_stmt(String *str)
str->append(')'); str->append(')');
} }
bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg)
{
if (result_field)
{
check(1);
update();
return protocol->store(result_field);
}
return Item::send(protocol, str_arg);
}
String * String *
Item_func_get_user_var::val_str(String *str) Item_func_get_user_var::val_str(String *str)
...@@ -4143,7 +4161,7 @@ bool Item_func_get_user_var::set_value(THD *thd, ...@@ -4143,7 +4161,7 @@ bool Item_func_get_user_var::set_value(THD *thd,
Item_func_set_user_var is not fixed after construction, call Item_func_set_user_var is not fixed after construction, call
fix_fields(). fix_fields().
*/ */
return (!suv || suv->fix_fields(thd, it) || suv->check() || suv->update()); return (!suv || suv->fix_fields(thd, it) || suv->check(0) || suv->update());
} }
......
...@@ -54,7 +54,7 @@ public: ...@@ -54,7 +54,7 @@ public:
SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
NOT_FUNC, NOT_ALL_FUNC, NOT_FUNC, NOT_ALL_FUNC,
NOW_FUNC, TRIG_COND_FUNC, NOW_FUNC, TRIG_COND_FUNC,
GUSERVAR_FUNC, COLLATE_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC }; EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL }; OPTIMIZE_EQUAL };
...@@ -1167,13 +1167,15 @@ public: ...@@ -1167,13 +1167,15 @@ public:
Item_func_set_user_var(LEX_STRING a,Item *b) Item_func_set_user_var(LEX_STRING a,Item *b)
:Item_func(b), cached_result_type(INT_RESULT), name(a) :Item_func(b), cached_result_type(INT_RESULT), name(a)
{} {}
enum Functype functype() const { return SUSERVAR_FUNC; }
double val_real(); double val_real();
longlong val_int(); longlong val_int();
String *val_str(String *str); String *val_str(String *str);
my_decimal *val_decimal(my_decimal *); my_decimal *val_decimal(my_decimal *);
bool update_hash(void *ptr, uint length, enum Item_result type, bool update_hash(void *ptr, uint length, enum Item_result type,
CHARSET_INFO *cs, Derivation dv, bool unsigned_arg= 0); CHARSET_INFO *cs, Derivation dv, bool unsigned_arg= 0);
bool check(); bool send(Protocol *protocol, String *str_arg);
bool check(bool use_result_field);
bool update(); bool update();
enum Item_result result_type () const { return cached_result_type; } enum Item_result result_type () const { return cached_result_type; }
bool fix_fields(THD *thd, Item **ref); bool fix_fields(THD *thd, Item **ref);
......
...@@ -3252,7 +3252,7 @@ int set_var_user::check(THD *thd) ...@@ -3252,7 +3252,7 @@ int set_var_user::check(THD *thd)
0 can be passed as last argument (reference on item) 0 can be passed as last argument (reference on item)
*/ */
return (user_var_item->fix_fields(thd, (Item**) 0) || return (user_var_item->fix_fields(thd, (Item**) 0) ||
user_var_item->check()) ? -1 : 0; user_var_item->check(0)) ? -1 : 0;
} }
......
...@@ -1883,7 +1883,7 @@ bool select_dumpvar::send_data(List<Item> &items) ...@@ -1883,7 +1883,7 @@ bool select_dumpvar::send_data(List<Item> &items)
{ {
if ((xx=li++)) if ((xx=li++))
{ {
xx->check(); xx->check(0);
xx->update(); xx->update();
} }
} }
......
...@@ -13365,7 +13365,9 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array, ...@@ -13365,7 +13365,9 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
{ {
Field *field; Field *field;
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
(item->type() == Item::FUNC_ITEM &&
((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC))
item_field= item; item_field= item;
else else
{ {
......
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