From e83e10533194cca56596226e5c2c08c7bb6d5b9a Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 16 Jul 2005 03:29:13 +0400 Subject: [PATCH] A fix and a test case for Bug#9359 "Prepared statements take snapshot of system vars at PREPARE time": implement a special Item to handle system variables. This item substitutes itself with a basic constant containing variable value at fix_fields. mysql-test/r/ps.result: - test results fixed (Bug#9359). mysql-test/t/ps.test: - add a test case for Bug#9359 "Prepared statements take snapshot of system vars at PREPARE time" sql/item_func.cc: - implement Item_func_get_system_var: we should not evaluate system variables in the parser, but instead should create an item which is evaluated to a constant at execute. - remove an unused function sql/item_func.h: Add a new item, Item_func_get_system_var sql/mysql_priv.h: Move necessary declarations to make set_var.h objects visible in item_func.h sql/set_var.cc: - we should not print to network from get_system_var: if it's called from prepared statement prepare, we get packets out of order when using the binary protocol. Instead report the error to be sent to the user later. This is a backport from 5.0. sql/set_var.h: - declaration of enum_var_type moved to mysql_priv.h --- mysql-test/r/ps.result | 25 ++++++++++++++++ mysql-test/t/ps.test | 18 ++++++++++- sql/item_func.cc | 68 +++++++++++++++++++++++------------------- sql/item_func.h | 23 ++++++++++++++ sql/mysql_priv.h | 10 ++++--- sql/set_var.cc | 14 ++------- sql/set_var.h | 5 ---- 7 files changed, 112 insertions(+), 51 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 6cd32cbe8d0..c17015df757 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -676,3 +676,28 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp select ? from t1; 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 '? from t1' at line 1 drop table t1; +prepare stmt from "select @@time_zone"; +execute stmt; +@@time_zone +SYSTEM +set @@time_zone:='Japan'; +execute stmt; +@@time_zone +Japan +prepare stmt from "select @@tx_isolation"; +execute stmt; +@@tx_isolation +REPEATABLE-READ +set transaction isolation level read committed; +execute stmt; +@@tx_isolation +READ-COMMITTED +set transaction isolation level serializable; +execute stmt; +@@tx_isolation +SERIALIZABLE +set @@tx_isolation=default; +execute stmt; +@@tx_isolation +REPEATABLE-READ +deallocate prepare stmt; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index beae95d462a..a4911115c9f 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -702,4 +702,20 @@ select ??; select ? from t1; --enable_ps_protocol drop table t1; -# +# +# Bug#9359 "Prepared statements take snapshot of system vars at PREPARE +# time" +# +prepare stmt from "select @@time_zone"; +execute stmt; +set @@time_zone:='Japan'; +execute stmt; +prepare stmt from "select @@tx_isolation"; +execute stmt; +set transaction isolation level read committed; +execute stmt; +set transaction isolation level serializable; +execute stmt; +set @@tx_isolation=default; +execute stmt; +deallocate prepare stmt; diff --git a/sql/item_func.cc b/sql/item_func.cc index 0128209cb22..c8f1d209d26 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3031,6 +3031,36 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const } +Item_func_get_system_var:: +Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, + LEX_STRING *component_arg, const char *name_arg, + size_t name_len_arg) + :var(var_arg), var_type(var_type_arg), component(*component_arg) +{ + /* set_name() will allocate the name */ + set_name(name_arg, name_len_arg, system_charset_info); +} + + +bool +Item_func_get_system_var::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +{ + Item *item= var->item(thd, var_type, &component); + DBUG_ENTER("Item_func_get_system_var::fix_fields"); + /* + Evaluate the system variable and substitute the result (a basic constant) + instead of this item. If the variable can not be evaluated, + the error is reported in sys_var::item(). + */ + if (item == 0) + DBUG_RETURN(1); // Impossible + item->set_name(name, 0, system_charset_info); // don't allocate a new name + thd->change_item_tree(ref, item); + + DBUG_RETURN(0); +} + + longlong Item_func_inet_aton::val_int() { DBUG_ASSERT(fixed == 1); @@ -3375,22 +3405,21 @@ longlong Item_func_bit_xor::val_int() 0 error # constant item */ - + Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component) { + sys_var *var; + char buff[MAX_SYS_VAR_LENGTH*2+4+8], *pos; + LEX_STRING *base_name, *component_name; + if (component.str == 0 && !my_strcasecmp(system_charset_info, name.str, "VERSION")) return new Item_string("@@VERSION", server_version, (uint) strlen(server_version), system_charset_info, DERIVATION_SYSCONST); - Item *item; - sys_var *var; - char buff[MAX_SYS_VAR_LENGTH*2+4+8], *pos; - LEX_STRING *base_name, *component_name; - if (component.str) { base_name= &component; @@ -3412,9 +3441,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, return 0; } } - if (!(item=var->item(thd, var_type, component_name))) - return 0; // Impossible thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + buff[0]='@'; buff[1]='@'; pos=buff+2; @@ -3435,28 +3463,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, memcpy(pos, base_name->str, base_name->length); pos+= base_name->length; - // set_name() will allocate the name - item->set_name(buff,(uint) (pos-buff), system_charset_info); - return item; -} - - -Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, - uint length, const char *item_name) -{ - Item *item; - sys_var *var; - LEX_STRING null_lex_string; - - null_lex_string.str= 0; - - var= find_sys_var(var_name, length); - DBUG_ASSERT(var != 0); - if (!(item=var->item(thd, var_type, &null_lex_string))) - return 0; // Impossible - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - item->set_name(item_name, 0, system_charset_info); // Will use original name - return item; + return new Item_func_get_system_var(var, var_type, component_name, + buff, pos - buff); } diff --git a/sql/item_func.h b/sql/item_func.h index 6b6e5d4b8ec..5e36f9863bb 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -988,6 +988,29 @@ public: }; +/* A system variable */ + +class Item_func_get_system_var :public Item_func +{ + sys_var *var; + enum_var_type var_type; + LEX_STRING component; +public: + Item_func_get_system_var(sys_var *var_arg, enum_var_type var_type_arg, + LEX_STRING *component_arg, const char *name_arg, + size_t name_len_arg); + bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); + /* + Stubs for pure virtual methods. Should never be called: this + item is always substituted with a constant in fix_fields(). + */ + double val() { DBUG_ASSERT(0); return 0.0; } + longlong val_int() { DBUG_ASSERT(0); return 0; } + String* val_str(String*) { DBUG_ASSERT(0); return 0; } + void fix_length_and_dec() { DBUG_ASSERT(0); } +}; + + class Item_func_inet_aton : public Item_int_func { public: diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 722b76796b2..351f12acad2 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -358,6 +358,11 @@ inline THD *_current_thd(void) #include "protocol.h" #include "sql_udf.h" class user_var_entry; +enum enum_var_type +{ + OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL +}; +class sys_var; #include "item.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* sql_parse.cc */ @@ -1119,12 +1124,9 @@ extern bool sql_cache_init(); extern void sql_cache_free(); extern int sql_cache_hit(THD *thd, char *inBuf, uint length); -/* item.cc */ +/* item_func.cc */ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component); -Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, - uint length, const char *item_name); -/* item_func.cc */ int get_var_with_binlog(THD *thd, LEX_STRING &name, user_var_entry **out_entry); /* log.cc */ diff --git a/sql/set_var.cc b/sql/set_var.cc index b0fa61a12bc..f7700d18607 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1551,15 +1551,7 @@ err: /* Return an Item for a variable. Used with @@[global.]variable_name - If type is not given, return local value if exists, else global - - We have to use netprintf() instead of my_error() here as this is - called on the parsing stage. - - TODO: - With prepared statements/stored procedures this has to be fixed - to create an item that gets the current value at fix_fields() stage. */ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) @@ -1568,8 +1560,8 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) { if (var_type != OPT_DEFAULT) { - net_printf(thd, ER_INCORRECT_GLOBAL_LOCAL_VAR, - name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); + my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), + name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); return 0; } /* As there was no local variable, return the global value */ @@ -1613,7 +1605,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) return tmp; } default: - net_printf(thd, ER_VAR_CANT_BE_READ, name); + my_error(ER_VAR_CANT_BE_READ, MYF(0), name); } return 0; } diff --git a/sql/set_var.h b/sql/set_var.h index d452ba03367..2ac4fc2c260 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -30,11 +30,6 @@ class set_var; typedef struct system_variables SV; extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib; -enum enum_var_type -{ - OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL -}; - typedef int (*sys_check_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *); typedef void (*sys_after_update_func)(THD *,enum_var_type);