diff options
author | unknown <stewart@mysql.com> | 2005-07-18 15:02:21 +1000 |
---|---|---|
committer | unknown <stewart@mysql.com> | 2005-07-18 15:02:21 +1000 |
commit | d605e3fb4695d300e05957993f88439a732bdba8 (patch) | |
tree | 55e3cfc1489d4779d6ab4a03c478a99e69079758 /sql | |
parent | 768f3038ccb6a0ea379fa8325da4ba8315173f93 (diff) | |
parent | 653d467ca538b20b1f537ebcf68e16eac2230d88 (diff) | |
download | mariadb-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.cc | 7 | ||||
-rw-r--r-- | sql/item.cc | 103 | ||||
-rw-r--r-- | sql/item.h | 5 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 44 | ||||
-rw-r--r-- | sql/item_create.cc | 12 | ||||
-rw-r--r-- | sql/item_func.cc | 81 | ||||
-rw-r--r-- | sql/item_func.h | 25 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 28 | ||||
-rw-r--r-- | sql/item_strfunc.h | 26 | ||||
-rw-r--r-- | sql/mysql_priv.h | 10 | ||||
-rw-r--r-- | sql/opt_range.cc | 3 | ||||
-rw-r--r-- | sql/set_var.cc | 8 | ||||
-rw-r--r-- | sql/set_var.h | 5 | ||||
-rw-r--r-- | sql/slave.cc | 2 | ||||
-rw-r--r-- | sql/sp_head.cc | 6 | ||||
-rw-r--r-- | sql/sql_base.cc | 34 | ||||
-rw-r--r-- | sql/sql_class.h | 16 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.cc | 11 | ||||
-rw-r--r-- | sql/sql_parse.cc | 1 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 55 | ||||
-rw-r--r-- | sql/sql_select.cc | 44 | ||||
-rw-r--r-- | sql/sql_select.h | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 11 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 41 |
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, ¬null)) + 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; } } |