Commit e83e1053 authored by unknown's avatar unknown

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
parent 0f41fb42
...@@ -676,3 +676,28 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp ...@@ -676,3 +676,28 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp
select ? from t1; 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 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; 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;
...@@ -702,4 +702,20 @@ select ??; ...@@ -702,4 +702,20 @@ select ??;
select ? from t1; select ? from t1;
--enable_ps_protocol --enable_ps_protocol
drop table t1; 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;
...@@ -3031,6 +3031,36 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const ...@@ -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() longlong Item_func_inet_aton::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
...@@ -3375,22 +3405,21 @@ longlong Item_func_bit_xor::val_int() ...@@ -3375,22 +3405,21 @@ longlong Item_func_bit_xor::val_int()
0 error 0 error
# constant item # constant item
*/ */
Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component) 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 && if (component.str == 0 &&
!my_strcasecmp(system_charset_info, name.str, "VERSION")) !my_strcasecmp(system_charset_info, name.str, "VERSION"))
return new Item_string("@@VERSION", server_version, return new Item_string("@@VERSION", server_version,
(uint) strlen(server_version), (uint) strlen(server_version),
system_charset_info, DERIVATION_SYSCONST); 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) if (component.str)
{ {
base_name= &component; base_name= &component;
...@@ -3412,9 +3441,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, ...@@ -3412,9 +3441,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
return 0; return 0;
} }
} }
if (!(item=var->item(thd, var_type, component_name)))
return 0; // Impossible
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
buff[0]='@'; buff[0]='@';
buff[1]='@'; buff[1]='@';
pos=buff+2; pos=buff+2;
...@@ -3435,28 +3463,8 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, ...@@ -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); memcpy(pos, base_name->str, base_name->length);
pos+= base_name->length; pos+= base_name->length;
// set_name() will allocate the name return new Item_func_get_system_var(var, var_type, component_name,
item->set_name(buff,(uint) (pos-buff), system_charset_info); buff, pos - buff);
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;
} }
......
...@@ -988,6 +988,29 @@ class Item_func_get_user_var :public Item_func ...@@ -988,6 +988,29 @@ class Item_func_get_user_var :public Item_func
}; };
/* 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 class Item_func_inet_aton : public Item_int_func
{ {
public: public:
......
...@@ -358,6 +358,11 @@ inline THD *_current_thd(void) ...@@ -358,6 +358,11 @@ inline THD *_current_thd(void)
#include "protocol.h" #include "protocol.h"
#include "sql_udf.h" #include "sql_udf.h"
class user_var_entry; class user_var_entry;
enum enum_var_type
{
OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL
};
class sys_var;
#include "item.h" #include "item.h"
typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
/* sql_parse.cc */ /* sql_parse.cc */
...@@ -1119,12 +1124,9 @@ extern bool sql_cache_init(); ...@@ -1119,12 +1124,9 @@ extern bool sql_cache_init();
extern void sql_cache_free(); extern void sql_cache_free();
extern int sql_cache_hit(THD *thd, char *inBuf, uint length); 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, Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component); 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, int get_var_with_binlog(THD *thd, LEX_STRING &name,
user_var_entry **out_entry); user_var_entry **out_entry);
/* log.cc */ /* log.cc */
......
...@@ -1551,15 +1551,7 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names) ...@@ -1551,15 +1551,7 @@ bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names)
/* /*
Return an Item for a variable. Used with @@[global.]variable_name Return an Item for a variable. Used with @@[global.]variable_name
If type is not given, return local value if exists, else global 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) 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) ...@@ -1568,8 +1560,8 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base)
{ {
if (var_type != OPT_DEFAULT) if (var_type != OPT_DEFAULT)
{ {
net_printf(thd, ER_INCORRECT_GLOBAL_LOCAL_VAR, my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL"); name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
return 0; return 0;
} }
/* As there was no local variable, return the global value */ /* 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) ...@@ -1613,7 +1605,7 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base)
return tmp; return tmp;
} }
default: default:
net_printf(thd, ER_VAR_CANT_BE_READ, name); my_error(ER_VAR_CANT_BE_READ, MYF(0), name);
} }
return 0; return 0;
} }
......
...@@ -30,11 +30,6 @@ class set_var; ...@@ -30,11 +30,6 @@ class set_var;
typedef struct system_variables SV; typedef struct system_variables SV;
extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib; 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 int (*sys_check_func)(THD *, set_var *);
typedef bool (*sys_update_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *);
typedef void (*sys_after_update_func)(THD *,enum_var_type); typedef void (*sys_after_update_func)(THD *,enum_var_type);
......
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