From f094fe551a833f50fbac7092b069462a00856719 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 9 Jan 2007 23:24:56 +0300 Subject: [PATCH] Fixed bug#16861: User defined variable can have a wrong value if a tmp table was used. The Item::save_in_field() function is called from fill_record() to fill the new row with data while execution of the CREATE TABLE ... SELECT statement. Item::save_in_field() calls val_xxx() methods in order to get values. val_xxx() methods do not take into account the result field. Due to this Item_func_set_user_var::val_xxx() methods returns values from the original table, not from the temporary one. The save_in_field() member function is added to the Item_func_set_user_var class. It detects whether the result field should be used and properly updates the value of the user variable. sql/item_func.cc: Bug#16861: User defined variable can have a wrong value if a tmp table was used. Added the save_in_field() member function to the Item_func_set_user_var class. sql/item_func.h: Bug#16861: User defined variable can have a wrong value if a tmp table was used. Added the save_in_field() member function to the Item_func_set_user_var class. mysql-test/r/user_var.result: Extended the test case for bug#18681: User defined variable can have a wrong value if a tmp table was used. mysql-test/t/user_var.test: Extended the test case for bug#18681: User defined variable can have a wrong value if a tmp table was used. --- mysql-test/r/user_var.result | 9 +++- mysql-test/t/user_var.test | 5 +- sql/item_func.cc | 99 ++++++++++++++++++++++++++++++++++++ sql/item_func.h | 1 + 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index 90954fc1ede..753c982155c 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -301,7 +301,14 @@ select @var:=f2 from t1 group by f1 order by f2 desc limit 1; select @var; @var 3 -drop table t1; +create table t2 as select @var:=f2 from t1 group by f1 order by f2 desc limit 1; +select * from t2; +@var:=f2 +3 +select @var; +@var +3 +drop table t1,t2; insert into city 'blah'; ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''blah'' at line 1 SHOW COUNT(*) WARNINGS; diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 65ca1b2c1b7..70f57fdf283 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -210,7 +210,10 @@ 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; +create table t2 as select @var:=f2 from t1 group by f1 order by f2 desc limit 1; +select * from t2; +select @var; +drop table t1,t2; # # Bug#19024 - SHOW COUNT(*) WARNINGS not return Errors diff --git a/sql/item_func.cc b/sql/item_func.cc index 0eab370237d..4138573785e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4056,6 +4056,105 @@ void Item_func_set_user_var::make_field(Send_field *tmp_field) Item::make_field(tmp_field); } + +/* + Save the value of a user variable into a field + + SYNOPSIS + save_in_field() + field target field to save the value to + no_conversion flag indicating whether conversions are allowed + + DESCRIPTION + Save the function value into a field and update the user variable + accordingly. If a result field is defined and the target field doesn't + coincide with it then the value from the result field will be used as + the new value of the user variable. + + The reason to have this method rather than simply using the result + field in the val_xxx() methods is that the value from the result field + not always can be used when the result field is defined. + Let's consider the following cases: + 1) when filling a tmp table the result field is defined but the value of it + is undefined because it has to be produced yet. Thus we can't use it. + 2) on execution of an INSERT ... SELECT statement the save_in_field() + function will be called to fill the data in the new record. If the SELECT + part uses a tmp table then the result field is defined and should be + used in order to get the correct result. + + The difference between the SET_USER_VAR function and regular functions + like CONCAT is that the Item_func objects for the regular functions are + replaced by Item_field objects after the values of these functions have + been stored in a tmp table. Yet an object of the Item_field class cannot + be used to update a user variable. + Due to this we have to handle the result field in a special way here and + in the Item_func_set_user_var::send() function. + + RETURN VALUES + FALSE Ok + TRUE Error +*/ + +int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions) +{ + bool use_result_field= (result_field && result_field != field); + int error; + + /* Update the value of the user variable */ + check(use_result_field); + update(); + + if (result_type() == STRING_RESULT || + result_type() == REAL_RESULT && + field->result_type() == STRING_RESULT) + { + String *result; + CHARSET_INFO *cs= collation.collation; + char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns + str_value.set_quick(buff, sizeof(buff), cs); + result= entry->val_str(&null_value, &str_value, decimals); + + if (null_value) + { + str_value.set_quick(0, 0, cs); + return set_field_to_null_with_conversions(field, no_conversions); + } + + /* NOTE: If null_value == FALSE, "result" must be not NULL. */ + + field->set_notnull(); + error=field->store(result->ptr(),result->length(),cs); + str_value.set_quick(0, 0, cs); + } + else if (result_type() == REAL_RESULT) + { + double nr= entry->val_real(&null_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store(nr); + } + else if (result_type() == DECIMAL_RESULT) + { + my_decimal decimal_value; + my_decimal *value= entry->val_decimal(&null_value, &decimal_value); + if (null_value) + return set_field_to_null(field); + field->set_notnull(); + error=field->store_decimal(value); + } + else + { + longlong nr= entry->val_int(&null_value); + if (null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + error=field->store(nr, unsigned_flag); + } + return error; +} + + String * Item_func_get_user_var::val_str(String *str) { diff --git a/sql/item_func.h b/sql/item_func.h index c116c18bc50..68591f9c6f5 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1188,6 +1188,7 @@ public: void print(String *str); void print_as_stmt(String *str); const char *func_name() const { return "set_user_var"; } + int save_in_field(Field *field, bool no_conversions); };