diff options
-rw-r--r-- | mysql-test/r/ps.result | 25 | ||||
-rw-r--r-- | mysql-test/r/select.result | 11 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 17 | ||||
-rw-r--r-- | mysql-test/t/select.test | 11 | ||||
-rw-r--r-- | sql/item_func.cc | 68 | ||||
-rw-r--r-- | sql/item_func.h | 23 | ||||
-rw-r--r-- | sql/mysql_priv.h | 10 | ||||
-rw-r--r-- | sql/set_var.cc | 8 | ||||
-rw-r--r-- | sql/set_var.h | 5 | ||||
-rw-r--r-- | sql/sql_select.cc | 20 |
10 files changed, 150 insertions, 48 deletions
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 81d85306e93..0050dfc0841 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -775,3 +775,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/r/select.result b/mysql-test/r/select.result index d0503d1b83b..d327b491ebf 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2746,3 +2746,14 @@ WHERE COUNT(*) 4 drop table t1,t2,t3; +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +f2 +1 +NULL +drop table t1,t2; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index e8ea1dc373b..de92c67518e 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -811,3 +811,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/mysql-test/t/select.test b/mysql-test/t/select.test index 712658a19a6..69343524928 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2330,3 +2330,14 @@ WHERE drop table t1,t2,t3; +# +# Bug #11482 4.1.12 produces different resultset for a complex query +# than in previous 4.1.x +create table t1 (f1 int); +insert into t1 values (1),(NULL); +create table t2 (f2 int, f3 int, f4 int); +create index idx1 on t2 (f4); +insert into t2 values (1,2,3),(2,4,6); +select A.f2 from t1 left join t2 A on A.f2 = f1 where A.f3=(select min(f3) +from t2 C where A.f4 = C.f4) or A.f3 IS NULL; +drop table t1,t2; diff --git a/sql/item_func.cc b/sql/item_func.cc index 0817282b673..faf73536eb6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4217,6 +4217,36 @@ void Item_user_var_as_out_param::print(String *str) } +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); @@ -4563,22 +4593,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; @@ -4600,9 +4629,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; @@ -4623,28 +4651,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 3ca37b1961f..b8638161787 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1199,6 +1199,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 70b6bc24467..b7d5098e230 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -475,6 +475,11 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #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" extern my_decimal decimal_zero; typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); @@ -1329,12 +1334,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 ae7e4bd844b..2d7c3364e41 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1652,15 +1652,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) diff --git a/sql/set_var.h b/sql/set_var.h index a6532323b34..a7e680cc7fa 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= 0, 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); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 04a9126d8ed..87ce26b24d5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5138,7 +5138,23 @@ inline void add_cond_and_fix(Item **e1, Item *e2) (where othertbl is a non-const table and othertbl.field may be NULL) and add them to conditions on correspoding tables (othertbl in this example). - + + Exception from that is the case when referred_tab->join != join. + I.e. don't add NOT NULL constraints from any embedded subquery. + Consider this query: + SELECT A.f2 FROM t1 LEFT JOIN t2 A ON A.f2 = f1 + WHERE A.f3=(SELECT MIN(f3) FROM t2 C WHERE A.f4 = C.f4) OR A.f3 IS NULL; + Here condition A.f3 IS NOT NULL is going to be added to the WHERE + condition of the embedding query. + Another example: + SELECT * FROM t10, t11 WHERE (t10.a < 10 OR t10.a IS NULL) + AND t11.b <=> t10.b AND (t11.a = (SELECT MAX(a) FROM t12 + WHERE t12.b = t10.a )); + Here condition t10.a IS NOT NULL is going to be added. + In both cases addition of NOT NULL condition will erroneously reject + some rows of the result set. + referred_tab->join != join constraint would disallow such additions. + This optimization doesn't affect the choices that ref, range, or join optimizer make. This was intentional because this was added after 4.1 was GA. @@ -5169,6 +5185,8 @@ static void add_not_null_conds(JOIN *join) DBUG_ASSERT(item->type() == Item::FIELD_ITEM); Item_field *not_null_item= (Item_field*)item; JOIN_TAB *referred_tab= not_null_item->field->table->reginfo.join_tab; + if (referred_tab->join != join) + continue; Item *notnull; if (!(notnull= new Item_func_isnotnull(not_null_item))) DBUG_VOID_RETURN; |