summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/ps.result25
-rw-r--r--mysql-test/r/select.result11
-rw-r--r--mysql-test/t/ps.test17
-rw-r--r--mysql-test/t/select.test11
-rw-r--r--sql/item_func.cc68
-rw-r--r--sql/item_func.h23
-rw-r--r--sql/mysql_priv.h10
-rw-r--r--sql/set_var.cc8
-rw-r--r--sql/set_var.h5
-rw-r--r--sql/sql_select.cc20
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;