summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <stewart@mysql.com>2005-07-18 15:02:21 +1000
committerunknown <stewart@mysql.com>2005-07-18 15:02:21 +1000
commitd605e3fb4695d300e05957993f88439a732bdba8 (patch)
tree55e3cfc1489d4779d6ab4a03c478a99e69079758 /sql
parent768f3038ccb6a0ea379fa8325da4ba8315173f93 (diff)
parent653d467ca538b20b1f537ebcf68e16eac2230d88 (diff)
downloadmariadb-git-d605e3fb4695d300e05957993f88439a732bdba8.tar.gz
Merge ssmith@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/stewart/Documents/MySQL/5.0/main sql/slave.cc: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc7
-rw-r--r--sql/item.cc103
-rw-r--r--sql/item.h5
-rw-r--r--sql/item_cmpfunc.cc44
-rw-r--r--sql/item_create.cc12
-rw-r--r--sql/item_func.cc81
-rw-r--r--sql/item_func.h25
-rw-r--r--sql/item_strfunc.cc28
-rw-r--r--sql/item_strfunc.h26
-rw-r--r--sql/mysql_priv.h10
-rw-r--r--sql/opt_range.cc3
-rw-r--r--sql/set_var.cc8
-rw-r--r--sql/set_var.h5
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/sp_head.cc6
-rw-r--r--sql/sql_base.cc34
-rw-r--r--sql/sql_class.h16
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc11
-rw-r--r--sql/sql_parse.cc1
-rw-r--r--sql/sql_prepare.cc55
-rw-r--r--sql/sql_select.cc44
-rw-r--r--sql/sql_select.h2
-rw-r--r--sql/sql_table.cc11
-rw-r--r--sql/sql_yacc.yy41
25 files changed, 425 insertions, 157 deletions
diff --git a/sql/field.cc b/sql/field.cc
index 925fca8ac43..52f260e7b2d 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -47,6 +47,8 @@ uchar Field_null::null[1]={1};
const char field_separator=',';
#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320
+#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
+((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1)))
/*
Rules for merging different types of fields in UNION
@@ -6696,7 +6698,7 @@ Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
struct st_table *table_arg,uint blob_pack_length,
CHARSET_INFO *cs)
- :Field_longstr(ptr_arg, (1L << min(blob_pack_length,3)*8)-1L,
+ :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
table_arg, cs),
packlength(blob_pack_length)
@@ -8485,7 +8487,8 @@ create_field::create_field(Field *old_field,Field *orig_field)
else
interval=0;
def=0;
- if (!old_field->is_real_null() && ! (flags & BLOB_FLAG) &&
+ if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
+ !old_field->is_real_null() &&
old_field->ptr && orig_field)
{
char buff[MAX_FIELD_WIDTH],*pos;
diff --git a/sql/item.cc b/sql/item.cc
index c9af5d5a639..d26e4ba7c20 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -601,15 +601,75 @@ bool Item::eq(const Item *item, bool binary_cmp) const
Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
{
/*
+ Allow conversion from and to "binary".
Don't allow automatic conversion to non-Unicode charsets,
as it potentially loses data.
*/
- if (!(tocs->state & MY_CS_UNICODE))
+ if (collation.collation != &my_charset_bin &&
+ tocs != &my_charset_bin &&
+ !(tocs->state & MY_CS_UNICODE))
return NULL; // safe conversion is not possible
return new Item_func_conv_charset(this, tocs);
}
+/*
+ Created mostly for mysql_prepare_table(). Important
+ when a string ENUM/SET column is described with a numeric default value:
+
+ CREATE TABLE t1(a SET('a') DEFAULT 1);
+
+ We cannot use generic Item::safe_charset_converter(), because
+ the latter returns a non-fixed Item, so val_str() crashes afterwards.
+ Override Item_num method, to return a fixed item.
+*/
+Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ char buf[64];
+ String *s, tmp(buf, sizeof(buf), &my_charset_bin);
+ s= val_str(&tmp);
+ if ((conv= new Item_string(s->ptr(), s->length(), s->charset())))
+ {
+ conv->str_value.copy();
+ conv->str_value.mark_as_const();
+ }
+ return conv;
+}
+
+
+Item *Item_static_int_func::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ char buf[64];
+ String *s, tmp(buf, sizeof(buf), &my_charset_bin);
+ s= val_str(&tmp);
+ if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(),
+ s->charset())))
+ {
+ conv->str_value.copy();
+ conv->str_value.mark_as_const();
+ }
+ return conv;
+}
+
+
+Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ char buf[64];
+ String *s, tmp(buf, sizeof(buf), &my_charset_bin);
+ s= val_str(&tmp);
+ if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(),
+ s->charset())))
+ {
+ conv->str_value.copy();
+ conv->str_value.mark_as_const();
+ }
+ return conv;
+}
+
+
Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
{
Item_string *conv;
@@ -635,6 +695,33 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
}
+Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ uint conv_errors;
+ String tmp, cstr, *ostr= val_str(&tmp);
+ cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
+ if (conv_errors ||
+ !(conv= new Item_static_string_func(func_name,
+ cstr.ptr(), cstr.length(),
+ cstr.charset(),
+ collation.derivation)))
+ {
+ /*
+ Safe conversion is not possible (or EOM).
+ We could not convert a string into the requested character set
+ without data loss. The target charset does not cover all the
+ characters from the string. Operation cannot be done correctly.
+ */
+ return NULL;
+ }
+ conv->str_value.copy();
+ /* Ensure that no one is going to change the result string */
+ conv->str_value.mark_as_const();
+ return conv;
+}
+
+
bool Item_string::eq(const Item *item, bool binary_cmp) const
{
if (type() == item->type() && item->basic_const_item())
@@ -3754,6 +3841,20 @@ bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const
return FALSE;
}
+
+Item *Item_hex_string::safe_charset_converter(CHARSET_INFO *tocs)
+{
+ Item_string *conv;
+ String tmp, *str= val_str(&tmp);
+
+ if (!(conv= new Item_string(str->ptr(), str->length(), tocs)))
+ return NULL;
+ conv->str_value.copy();
+ conv->str_value.mark_as_const();
+ return conv;
+}
+
+
/*
bin item.
In string context this is a binary string.
diff --git a/sql/item.h b/sql/item.h
index ce151ec85bf..f195557fb69 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -771,6 +771,7 @@ class Item_num: public Item
{
public:
virtual Item_num *neg()= 0;
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
};
#define NO_CACHED_FIELD_INDEX ((uint)(-1))
@@ -1131,6 +1132,7 @@ public:
Item_static_int_func(const char *str_arg, longlong i, uint length)
:Item_int(NullS, i, length), func_name(str_arg)
{}
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
void print(String *str) { str->append(func_name); }
};
@@ -1241,6 +1243,7 @@ public:
:Item_float(NullS, val_arg, decimal_par, length), func_name(str)
{}
void print(String *str) { str->append(func_name); }
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
};
@@ -1313,6 +1316,7 @@ public:
Derivation dv= DERIVATION_COERCIBLE)
:Item_string(NullS, str, length, cs, dv), func_name(name_par)
{}
+ Item *safe_charset_converter(CHARSET_INFO *tocs);
void print(String *str) { str->append(func_name); }
};
@@ -1369,6 +1373,7 @@ public:
// to prevent drop fixed flag (no need parent cleanup call)
void cleanup() {}
bool eq(const Item *item, bool binary_cmp) const;
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 5ed857319be..d7c117db6c0 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -237,32 +237,35 @@ void Item_bool_func2::fix_length_and_dec()
set_cmp_func();
return;
}
-
- Item *real_item= args[0]->real_item();
- if (real_item->type() == FIELD_ITEM)
+
+ if (!thd->is_context_analysis_only())
{
- Field *field= ((Item_field*) real_item)->field;
- if (field->can_be_compared_as_longlong())
+ Item *real_item= args[0]->real_item();
+ if (real_item->type() == FIELD_ITEM)
{
- if (convert_constant_item(thd, field,&args[1]))
+ Field *field=((Item_field*) real_item)->field;
+ if (field->can_be_compared_as_longlong())
{
- cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
- INT_RESULT); // Works for all types.
- return;
+ if (convert_constant_item(thd, field,&args[1]))
+ {
+ cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
+ INT_RESULT); // Works for all types.
+ return;
+ }
}
}
- }
- real_item= args[1]->real_item();
- if (real_item->type() == FIELD_ITEM)
- {
- Field *field= ((Item_field*) real_item)->field;
- if (field->can_be_compared_as_longlong())
+ real_item= args[1]->real_item();
+ if (real_item->type() == FIELD_ITEM /* && !real_item->const_item() */)
{
- if (convert_constant_item(thd, field,&args[0]))
+ Field *field=((Item_field*) real_item)->field;
+ if (field->can_be_compared_as_longlong())
{
- cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
- INT_RESULT); // Works for all types.
- return;
+ if (convert_constant_item(thd, field,&args[0]))
+ {
+ cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
+ INT_RESULT); // Works for all types.
+ return;
+ }
}
}
}
@@ -990,7 +993,8 @@ void Item_func_between::fix_length_and_dec()
if (args[0]->type() == FIELD_ITEM)
{
Field *field=((Item_field*) args[0])->field;
- if (field->can_be_compared_as_longlong())
+ if (!thd->is_context_analysis_only() &&
+ field->can_be_compared_as_longlong())
{
/*
The following can't be recoded with || as convert_constant_item
diff --git a/sql/item_create.cc b/sql/item_create.cc
index b9073a6c0b3..b7d8d50f9b3 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -299,16 +299,8 @@ Item *create_func_pow(Item* a, Item *b)
Item *create_func_current_user()
{
- THD *thd=current_thd;
- char buff[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
- uint length;
-
- thd->lex->safe_to_cache_query= 0;
- length= (uint) (strxmov(buff, thd->priv_user, "@", thd->priv_host, NullS) -
- buff);
- return new Item_static_string_func("current_user()",
- thd->memdup(buff, length), length,
- system_charset_info);
+ current_thd->lex->safe_to_cache_query= 0;
+ return new Item_func_user(TRUE);
}
Item *create_func_radians(Item *a)
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 296083b256e..c3bdb11418f 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3157,7 +3157,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
THD* thd=current_thd;
User_level_lock* ull;
struct timespec abstime;
- int lock_name_len,error=0;
+ int lock_name_len;
lock_name_len=strlen(lock_name);
pthread_mutex_lock(&LOCK_user_locks);
@@ -3191,8 +3191,8 @@ void debug_sync_point(const char* lock_name, uint lock_timeout)
set_timespec(abstime,lock_timeout);
while (!thd->killed &&
- (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime))
- != ETIME && error != ETIMEDOUT && ull->locked) ;
+ pthread_cond_timedwait(&ull->cond, &LOCK_user_locks,
+ &abstime) != ETIMEDOUT && ull->locked) ;
if (ull->locked)
{
if (!--ull->count)
@@ -3294,14 +3294,14 @@ longlong Item_func_get_lock::val_int()
set_timespec(abstime,timeout);
while (!thd->killed &&
(error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime))
- != ETIME && error != ETIMEDOUT && error != EINVAL && ull->locked) ;
+ != ETIMEDOUT && error != EINVAL && ull->locked) ;
if (thd->killed)
error=EINTR; // Return NULL
if (ull->locked)
{
if (!--ull->count)
delete ull; // Should never happen
- if (error != ETIME && error != ETIMEDOUT)
+ if (error != ETIMEDOUT)
{
error=1;
null_value=1; // Return NULL
@@ -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, 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);
@@ -4395,6 +4425,9 @@ bool Item_func_match::fix_index()
if (key == NO_SUCH_KEY)
return 0;
+
+ if (!table)
+ goto err;
for (keynr=0 ; keynr < table->s->keys ; keynr++)
{
@@ -4560,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;
@@ -4597,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;
@@ -4620,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..43a85e6aa0b 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1199,6 +1199,31 @@ 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, Item **ref);
+ /*
+ Stubs for pure virtual methods. Should never be called: this
+ item is always substituted with a constant in fix_fields().
+ */
+ double val_real() { 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); }
+ /* TODO: fix to support views */
+ const char *func_name() const { return "get_system_var"; }
+};
+
+
class Item_func_inet_aton : public Item_int_func
{
public:
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 2fd0f25e699..ec30dbf8aea 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1552,9 +1552,11 @@ Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs)
uint conv_errors;
String tmp, cstr, *ostr= val_str(&tmp);
cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
+ if (conv_errors ||
+ !(conv= new Item_static_string_func(fully_qualified_func_name(),
+ cstr.ptr(), cstr.length(),
+ cstr.charset(),
+ collation.derivation)))
{
return NULL;
}
@@ -1584,13 +1586,24 @@ String *Item_func_user::val_str(String *str)
DBUG_ASSERT(fixed == 1);
THD *thd=current_thd;
CHARSET_INFO *cs= system_charset_info;
- const char *host= thd->host_or_ip;
+ const char *host, *user;
uint res_length;
+ if (is_current)
+ {
+ user= thd->priv_user;
+ host= thd->priv_host;
+ }
+ else
+ {
+ user= thd->user;
+ host= thd->host_or_ip;
+ }
+
// For system threads (e.g. replication SQL thread) user may be empty
- if (!thd->user)
+ if (!user)
return &my_empty_string;
- res_length= (strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen;
+ res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen;
if (str->alloc(res_length))
{
@@ -1598,12 +1611,13 @@ String *Item_func_user::val_str(String *str)
return 0;
}
res_length=cs->cset->snprintf(cs, (char*)str->ptr(), res_length, "%s@%s",
- thd->user, host);
+ user, host);
str->length(res_length);
str->set_charset(cs);
return str;
}
+
void Item_func_soundex::fix_length_and_dec()
{
collation.set(args[0]->collation);
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index d85210984d9..fa098849a43 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -357,8 +357,15 @@ public:
Item_func_sysconst()
{ collation.set(system_charset_info,DERIVATION_SYSCONST); }
Item *safe_charset_converter(CHARSET_INFO *tocs);
+ /*
+ Used to create correct Item name in new converted item in
+ safe_charset_converter, return string representation of this function
+ call
+ */
+ virtual const char *fully_qualified_func_name() const = 0;
};
+
class Item_func_database :public Item_func_sysconst
{
public:
@@ -370,18 +377,27 @@ public:
maybe_null=1;
}
const char *func_name() const { return "database"; }
+ const char *fully_qualified_func_name() const { return "database()"; }
};
+
class Item_func_user :public Item_func_sysconst
{
+ bool is_current;
+
public:
- Item_func_user() :Item_func_sysconst() {}
+ Item_func_user(bool is_current_arg)
+ :Item_func_sysconst(), is_current(is_current_arg) {}
String *val_str(String *);
- void fix_length_and_dec()
- {
- max_length= (USERNAME_LENGTH+HOSTNAME_LENGTH+1)*system_charset_info->mbmaxlen;
+ void fix_length_and_dec()
+ {
+ max_length= ((USERNAME_LENGTH + HOSTNAME_LENGTH + 1) *
+ system_charset_info->mbmaxlen);
}
- const char *func_name() const { return "user"; }
+ const char *func_name() const
+ { return is_current ? "current_user" : "user"; }
+ const char *fully_qualified_func_name() const
+ { return is_current ? "current_user()" : "user()"; }
};
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 70b6bc24467..49eb50be580 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= 0, 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/opt_range.cc b/sql/opt_range.cc
index c2760b08b6e..676f8d8a33d 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3531,7 +3531,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
if (arg->type() != Item::FUNC_ITEM)
DBUG_RETURN(0);
cond_func= (Item_func*) arg;
- if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE)
+ if (cond_func->functype() != Item_func::BETWEEN &&
+ cond_func->functype() != Item_func::IN_FUNC)
DBUG_RETURN(0);
inv= TRUE;
}
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/slave.cc b/sql/slave.cc
index d5d3ab19c01..86611566b93 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2753,7 +2753,7 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name,
else
pthread_cond_wait(&data_cond, &data_lock);
DBUG_PRINT("info",("Got signal of master update or timed out"));
- if (error == ETIMEDOUT || error == ETIME)
+ if (error == ETIMEDOUT)
{
error= -1;
break;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 272456d8c8e..02c006d01ee 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1023,6 +1023,7 @@ sp_head::reset_lex(THD *thd)
DBUG_ENTER("sp_head::reset_lex");
LEX *sublex;
LEX *oldlex= thd->lex;
+ my_lex_states state= oldlex->next_state; // Keep original next_state
(void)m_lex.push_front(oldlex);
thd->lex= sublex= new st_lex;
@@ -1030,6 +1031,11 @@ sp_head::reset_lex(THD *thd)
/* Reset most stuff. The length arguments doesn't matter here. */
lex_start(thd, oldlex->buf, (ulong) (oldlex->end_of_query - oldlex->ptr));
+ /*
+ * next_state is normally the same (0), but it happens that we swap lex in
+ * "mid-sentence", so we must restore it.
+ */
+ sublex->next_state= state;
/* We must reset ptr and end_of_query again */
sublex->ptr= oldlex->ptr;
sublex->end_of_query= oldlex->end_of_query;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 2ee1c8c24cc..7021f61a052 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1041,6 +1041,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (thd->locked_tables || thd->prelocked_mode)
{ // Using table locks
+ TABLE *best_table= 0;
+ int best_distance= INT_MIN, distance;
for (table=thd->open_tables; table ; table=table->next)
{
if (table->s->key_length == key_length &&
@@ -1049,11 +1051,37 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->query_id != thd->query_id && /* skip tables already used by this query */
!(thd->prelocked_mode && table->query_id))
{
- table->query_id= thd->query_id;
- DBUG_PRINT("info",("Using locked table"));
- goto reset;
+ distance= ((int) table->reginfo.lock_type -
+ (int) table_list->lock_type);
+ /*
+ Find a table that either has the exact lock type requested,
+ or has the best suitable lock. In case there is no locked
+ table that has an equal or higher lock than requested,
+ we still maitain the best_table to produce an error message
+ about wrong lock mode on the table. The best_table is changed
+ if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.
+
+ distance < 0 - we have not enough high lock mode
+ distance > 0 - we have lock mode higher then we require
+ distance == 0 - we have lock mode exactly which we need
+ */
+ if (best_distance < 0 && distance > best_distance ||
+ distance >= 0 && distance < best_distance)
+ {
+ best_distance= distance;
+ best_table= table;
+ if (best_distance == 0)
+ break;
+ }
}
}
+ if (best_table)
+ {
+ table= best_table;
+ table->query_id= thd->query_id;
+ DBUG_PRINT("info",("Using locked table"));
+ goto reset;
+ }
/*
is it view?
(it is work around to allow to open view with locked tables,
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 625b9c27b44..6c4315b95fa 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1162,12 +1162,22 @@ public:
This is to track items changed during execution of a prepared
statement/stored procedure. It's created by
register_item_tree_change() in memory root of THD, and freed in
- rollback_item_tree_changes(). For conventional execution it's always 0.
+ rollback_item_tree_changes(). For conventional execution it's always
+ empty.
*/
Item_change_list change_list;
/*
- Current prepared Query_arena if there one, or 0
+ A permanent memory area of the statement. For conventional
+ execution, the parsed tree and execution runtime reside in the same
+ memory root. In this case current_arena points to THD. In case of
+ a prepared statement or a stored procedure statement, thd->mem_root
+ conventionally points to runtime memory, and thd->current_arena
+ points to the memory of the PS/SP, where the parsed tree of the
+ statement resides. Whenever you need to perform a permanent
+ transformation of a parsed tree, you should allocate new memory in
+ current_arena, to allow correct re-execution of PS/SP.
+ Note: in the parser, current_arena == thd, even for PS/SP.
*/
Query_arena *current_arena;
/*
@@ -1457,6 +1467,8 @@ public:
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
}
void set_status_var_init();
+ bool is_context_analysis_only()
+ { return current_arena->is_stmt_prepare() || lex->view_prepare_mode; }
bool push_open_tables_state();
void pop_open_tables_state();
};
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 5d30cad2926..125390e4411 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1730,7 +1730,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
#endif
if (thd->killed || di->status)
break;
- if (error == ETIME || error == ETIMEDOUT)
+ if (error == ETIMEDOUT)
{
thd->killed= THD::KILL_CONNECTION;
break;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 2bf04273135..630a7e950f7 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -164,7 +164,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->current_select= &lex->select_lex;
lex->yacc_yyss=lex->yacc_yyvs=0;
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
- lex->sql_command=SQLCOM_END;
+ lex->sql_command= lex->orig_sql_command= SQLCOM_END;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
lex->sphead= NULL;
@@ -556,6 +556,15 @@ int yylex(void *arg, void *yythd)
lex->next_state= MY_LEX_START; // Allow signed numbers
if (c == ',')
lex->tok_start=lex->ptr; // Let tok_start point at next item
+ /*
+ Check for a placeholder: it should not precede a possible identifier
+ because of binlogging: when a placeholder is replaced with
+ its value in a query for the binlog, the query must stay
+ grammatically correct.
+ */
+ else if (c == '?' && ((THD*) yythd)->command == COM_STMT_PREPARE &&
+ !ident_map[cs, yyPeek()])
+ return(PARAM_MARKER);
return((int) c);
case MY_LEX_IDENT_OR_NCHAR:
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a2e5501ad7d..2e5cab4bb1c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5189,7 +5189,6 @@ mysql_init_select(LEX *lex)
{
SELECT_LEX *select_lex= lex->current_select;
select_lex->init_select();
- lex->orig_sql_command= SQLCOM_END;
lex->wild= 0;
if (select_lex == &lex->select_lex)
{
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 6d11518198c..3e291243e1e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -106,6 +106,7 @@ public:
virtual ~Prepared_statement();
void setup_set_params();
virtual Query_arena::Type type() const;
+ virtual void close_cursor();
};
static void execute_stmt(THD *thd, Prepared_statement *stmt,
@@ -312,24 +313,28 @@ static void set_param_int64(Item_param *param, uchar **pos, ulong len)
static void set_param_float(Item_param *param, uchar **pos, ulong len)
{
+ float data;
#ifndef EMBEDDED_LIBRARY
if (len < 4)
return;
-#endif
- float data;
float4get(data,*pos);
+#else
+ data= *(float*) *pos;
+#endif
param->set_double((double) data);
*pos+= 4;
}
static void set_param_double(Item_param *param, uchar **pos, ulong len)
{
+ double data;
#ifndef EMBEDDED_LIBRARY
if (len < 8)
return;
-#endif
- double data;
float8get(data,*pos);
+#else
+ data= *(double*) *pos;
+#endif
param->set_double((double) data);
*pos+= 8;
}
@@ -1986,10 +1991,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
cursor= stmt->cursor;
if (cursor && cursor->is_open())
- {
- my_error(ER_EXEC_STMT_WITH_OPEN_CURSOR, MYF(0));
- DBUG_VOID_RETURN;
- }
+ stmt->close_cursor();
DBUG_ASSERT(thd->free_list == NULL);
mysql_reset_thd_for_next_command(thd);
@@ -2284,6 +2286,7 @@ void mysql_stmt_reset(THD *thd, char *packet)
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
DBUG_VOID_RETURN;
+ stmt->close_cursor(); /* will reset statement params */
cursor= stmt->cursor;
if (cursor && cursor->is_open())
{
@@ -2295,12 +2298,6 @@ void mysql_stmt_reset(THD *thd, char *packet)
stmt->state= Query_arena::PREPARED;
- /*
- Clear parameters from data which could be set by
- mysql_stmt_send_long_data() call.
- */
- reset_stmt_params(stmt);
-
mysql_reset_thd_for_next_command(thd);
send_ok(thd);
@@ -2448,10 +2445,17 @@ void Prepared_statement::setup_set_params()
Prepared_statement::~Prepared_statement()
{
if (cursor)
+ {
+ if (cursor->is_open())
+ {
+ cursor->close(FALSE);
+ free_items();
+ free_root(cursor->mem_root, MYF(0));
+ }
cursor->Cursor::~Cursor();
- free_items();
- if (cursor)
- free_root(cursor->mem_root, MYF(0));
+ }
+ else
+ free_items();
delete lex->result;
}
@@ -2460,3 +2464,20 @@ Query_arena::Type Prepared_statement::type() const
{
return PREPARED_STATEMENT;
}
+
+
+void Prepared_statement::close_cursor()
+{
+ if (cursor && cursor->is_open())
+ {
+ thd->change_list= cursor->change_list;
+ cursor->close(FALSE);
+ cleanup_stmt_and_thd_after_use(this, thd);
+ free_root(cursor->mem_root, MYF(0));
+ }
+ /*
+ Clear parameters from data which could be set by
+ mysql_stmt_send_long_data() call.
+ */
+ reset_stmt_params(this);
+}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 66e783a2103..e60688fe574 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1915,12 +1915,6 @@ Cursor::close(bool is_active)
}
-Cursor::~Cursor()
-{
- if (is_open())
- close(FALSE);
-}
-
/*********************************************************************/
/*
@@ -2840,11 +2834,11 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level,
cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM &&
!(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT))
values--;
+ DBUG_ASSERT(cond_func->functype() != Item_func::IN_FUNC ||
+ cond_func->argument_count() != 2);
add_key_equal_fields(key_fields, *and_level, cond_func,
(Item_field*) (cond_func->key_item()->real_item()),
- cond_func->argument_count() == 2 &&
- cond_func->functype() == Item_func::IN_FUNC,
- values,
+ 0, values,
cond_func->argument_count()-1,
usable_tables);
}
@@ -5144,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.
@@ -5175,11 +5185,19 @@ 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;
- Item_func_isnotnull *notnull;
+ if (referred_tab->join != join)
+ continue;
+ Item *notnull;
if (!(notnull= new Item_func_isnotnull(not_null_item)))
DBUG_VOID_RETURN;
-
- notnull->quick_fix_field();
+ /*
+ We need to do full fix_fields() call here in order to have correct
+ notnull->const_item(). This is needed e.g. by test_quick_select
+ when it is called from make_join_select after this function is
+ called.
+ */
+ if (notnull->fix_fields(join->thd, &notnull))
+ DBUG_VOID_RETURN;
DBUG_EXECUTE("where",print_where(notnull,
referred_tab->table->alias););
add_cond_and_fix(&referred_tab->select_cond, notnull);
diff --git a/sql/sql_select.h b/sql/sql_select.h
index ac3e8898cc6..9285e33be33 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -408,7 +408,7 @@ public:
void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
Cursor(THD *thd);
- ~Cursor();
+ ~Cursor() {}
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 9e6d12eba13..89282d9fcb9 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -752,10 +752,15 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
Convert the default value from client character
set into the column character set if necessary.
*/
- if (sql_field->def)
+ if (sql_field->def && cs != sql_field->def->collation.collation)
{
- sql_field->def=
- sql_field->def->safe_charset_converter(cs);
+ if (!(sql_field->def=
+ sql_field->def->safe_charset_converter(cs)))
+ {
+ /* Could not convert */
+ my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
+ DBUG_RETURN(-1);
+ }
}
if (sql_field->sql_type == FIELD_TYPE_SET)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 84de06d5604..756a8a6ee3d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -465,6 +465,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PACK_KEYS_SYM
%token PARTIAL
%token PASSWORD
+%token PARAM_MARKER
%token PHASE_SYM
%token POINTFROMTEXT
%token POINT_SYM
@@ -4226,9 +4227,25 @@ bool_pri:
predicate:
bit_expr IN_SYM '(' expr_list ')'
- { $4->push_front($1); $$= new Item_func_in(*$4); }
+ {
+ if ($4->elements == 1)
+ $$= new Item_func_eq($1, $4->head());
+ else
+ {
+ $4->push_front($1);
+ $$= new Item_func_in(*$4);
+ }
+ }
| bit_expr not IN_SYM '(' expr_list ')'
- { $5->push_front($1); $$= negate_expression(YYTHD, new Item_func_in(*$5)); }
+ {
+ if ($5->elements == 1)
+ $$= new Item_func_ne($1, $5->head());
+ else
+ {
+ $5->push_front($1);
+ $$= negate_expression(YYTHD, new Item_func_in(*$5));
+ }
+ }
| bit_expr IN_SYM in_subselect
{ $$= new Item_in_subselect($1, $3); }
| bit_expr not IN_SYM in_subselect
@@ -4805,7 +4822,7 @@ simple_expr:
| UNIX_TIMESTAMP '(' expr ')'
{ $$= new Item_func_unix_timestamp($3); }
| USER '(' ')'
- { $$= new Item_func_user(); Lex->safe_to_cache_query=0; }
+ { $$= new Item_func_user(FALSE); Lex->safe_to_cache_query=0; }
| UTC_DATE_SYM optional_braces
{ $$= new Item_func_curdate_utc(); Lex->safe_to_cache_query=0;}
| UTC_TIME_SYM optional_braces
@@ -6933,23 +6950,15 @@ text_string:
;
param_marker:
- '?'
+ PARAM_MARKER
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
- if (thd->command == COM_STMT_PREPARE)
- {
- Item_param *item= new Item_param((uint) (lex->tok_start -
- (uchar *) thd->query));
- if (!($$= item) || lex->param_list.push_back(item))
- {
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- YYABORT;
- }
- }
- else
+ Item_param *item= new Item_param((uint) (lex->tok_start -
+ (uchar *) thd->query));
+ if (!($$= item) || lex->param_list.push_back(item))
{
- yyerror(ER(ER_SYNTAX_ERROR));
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
YYABORT;
}
}