summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/user_var.result9
-rw-r--r--mysql-test/t/user_var.test10
-rw-r--r--sql/item_func.cc42
-rw-r--r--sql/item_func.h6
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_select.cc4
7 files changed, 58 insertions, 17 deletions
diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result
index 3a197cd2a47..1664a907c99 100644
--- a/mysql-test/r/user_var.result
+++ b/mysql-test/r/user_var.result
@@ -292,3 +292,12 @@ SELECT @a;
@a
18446744071710965857
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;
diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test
index 58c52b59a5a..b2a9728de00 100644
--- a/mysql-test/t/user_var.test
+++ b/mysql-test/t/user_var.test
@@ -202,3 +202,13 @@ SET @a := (select * from bigfailure where afield = (SELECT afield FROM bigfailur
SELECT @a;
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;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a5786a2c87f..36e8fd33729 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -548,7 +548,7 @@ void Item_func::signal_divide_by_null()
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 copy_or_same(thd);
}
@@ -3719,30 +3719,38 @@ my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val)
*/
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");
+ if (use_result_field)
+ DBUG_ASSERT(result_field);
switch (cached_result_type) {
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;
}
case INT_RESULT:
{
- save_result.vint= args[0]->val_int();
- unsigned_flag= args[0]->unsigned_flag;
+ save_result.vint= use_result_field ? result_field->val_int() :
+ args[0]->val_int();
+ unsigned_flag= use_result_field ? ((Field_num*)result_field)->unsigned_flag:
+ args[0]->unsigned_flag;
break;
}
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;
}
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;
}
case ROW_RESULT:
@@ -3828,7 +3836,7 @@ Item_func_set_user_var::update()
double Item_func_set_user_var::val_real()
{
DBUG_ASSERT(fixed == 1);
- check();
+ check(0);
update(); // Store expression
return entry->val_real(&null_value);
}
@@ -3836,7 +3844,7 @@ double Item_func_set_user_var::val_real()
longlong Item_func_set_user_var::val_int()
{
DBUG_ASSERT(fixed == 1);
- check();
+ check(0);
update(); // Store expression
return entry->val_int(&null_value);
}
@@ -3844,7 +3852,7 @@ longlong Item_func_set_user_var::val_int()
String *Item_func_set_user_var::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- check();
+ check(0);
update(); // Store expression
return entry->val_str(&null_value, str, decimals);
}
@@ -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)
{
DBUG_ASSERT(fixed == 1);
- check();
+ check(0);
update(); // Store expression
return entry->val_decimal(&null_value, val);
}
@@ -3878,6 +3886,16 @@ void Item_func_set_user_var::print_as_stmt(String *str)
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 *
Item_func_get_user_var::val_str(String *str)
@@ -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
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());
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 30b62f3642d..09429accbad 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -54,7 +54,7 @@ public:
SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
NOT_FUNC, NOT_ALL_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 };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
@@ -1167,13 +1167,15 @@ public:
Item_func_set_user_var(LEX_STRING a,Item *b)
:Item_func(b), cached_result_type(INT_RESULT), name(a)
{}
+ enum Functype functype() const { return SUSERVAR_FUNC; }
double val_real();
longlong val_int();
String *val_str(String *str);
my_decimal *val_decimal(my_decimal *);
bool update_hash(void *ptr, uint length, enum Item_result type,
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();
enum Item_result result_type () const { return cached_result_type; }
bool fix_fields(THD *thd, Item **ref);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index f3ea3d13493..b61f6d58347 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -3252,7 +3252,7 @@ int set_var_user::check(THD *thd)
0 can be passed as last argument (reference on item)
*/
return (user_var_item->fix_fields(thd, (Item**) 0) ||
- user_var_item->check()) ? -1 : 0;
+ user_var_item->check(0)) ? -1 : 0;
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 4cb9cfc53c6..759e3b1a60b 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1883,7 +1883,7 @@ bool select_dumpvar::send_data(List<Item> &items)
{
if ((xx=li++))
{
- xx->check();
+ xx->check(0);
xx->update();
}
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 2f16b350d04..8225f868128 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -13365,7 +13365,9 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
{
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;
else
{