diff options
Diffstat (limited to 'sql/item.cc')
-rw-r--r-- | sql/item.cc | 358 |
1 files changed, 92 insertions, 266 deletions
diff --git a/sql/item.cc b/sql/item.cc index 0fefa54d849..8fc87149bc9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -686,7 +686,7 @@ void Item::cleanup() { DBUG_ENTER("Item::cleanup"); DBUG_PRINT("enter", ("this: %p", this)); - fixed=0; + fixed= 0; marker= 0; join_tab_idx= MAX_TABLES; if (orig_name) @@ -1073,10 +1073,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) name_length= 0; return; } - if (cs->ctype) - { - const char *str_start= str; + const char *str_start= str; + if (!cs->ctype || cs->mbminlen > 1) + { + str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES); + length-= str - str_start; + } + else + { /* This will probably need a better implementation in the future: a function in CHARSET_INFO structure. @@ -1086,21 +1091,21 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) length--; str++; } - if (str != str_start && !is_autogenerated_name) - { - char buff[SAFE_NAME_LEN]; - strmake(buff, str_start, - MY_MIN(sizeof(buff)-1, length + (int) (str-str_start))); - - if (length == 0) - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), - buff); - else - push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, - ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), - buff); - } + } + if (str != str_start && !is_autogenerated_name) + { + char buff[SAFE_NAME_LEN]; + strmake(buff, str_start, + MY_MIN(sizeof(buff)-1, length + (int) (str-str_start))); + + if (length == 0) + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), + buff); + else + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, + ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), + buff); } if (!my_charset_same(cs, system_charset_info)) { @@ -1166,6 +1171,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const Item *Item::safe_charset_converter(CHARSET_INFO *tocs) { + if (!needs_charset_converter(tocs)) + return this; Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1); return conv->safe ? conv : NULL; } @@ -1192,123 +1199,55 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs) if (!(tocs->state & MY_CS_NONASCII)) return this; - Item_string *conv; - uint conv_errors; - char buf[64], buf2[64]; - String tmp(buf, sizeof(buf), &my_charset_bin); - String cstr(buf2, sizeof(buf2), &my_charset_bin); - String *ostr= val_str(&tmp); - char *ptr; - 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))) - { - /* - 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; - } - if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length()))) - return NULL; - conv->str_value.set(ptr, cstr.length(), cstr.charset()); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); - conv->fix_char_length(max_char_length()); - 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(); - } + Item *conv; + if ((conv= const_charset_converter(tocs, true))) + conv->fix_char_length(max_char_length()); return conv; } -Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) -{ - return charset_converter(tocs, true); -} - - /** - Convert a string item into the requested character set. + Create character set converter for constant items + using Item_null, Item_string or Item_static_string_func. @param tocs Character set to to convert the string to. @param lossless Whether data loss is acceptable. + @param func_name Function name, or NULL. - @return A new item representing the converted string. + @return this, if conversion is not needed, + NULL, if safe conversion is not possible, or + a new item representing the converted constant. */ -Item *Item_string::charset_converter(CHARSET_INFO *tocs, bool lossless) +Item *Item::const_charset_converter(CHARSET_INFO *tocs, + bool lossless, + const char *func_name) { - Item_string *conv; - uint conv_errors; - char *ptr; - String tmp, cstr, *ostr= val_str(&tmp); - cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - conv_errors= lossless && conv_errors; - if (conv_errors || !(conv= new Item_string(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; - } - if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length()))) - return NULL; - conv->str_value.set(ptr, cstr.length(), cstr.charset()); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); - return conv; -} + DBUG_ASSERT(const_item()); + DBUG_ASSERT(fixed); + StringBuffer<64>tmp; + String *s= val_str(&tmp); + if (!s) + return new Item_null((char *) func_name, tocs); -Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) -{ - if (const_item()) + if (!needs_charset_converter(s->length(), tocs)) { - uint cnv_errors; - String *ostr= val_str(&cnvstr); - cnvitem->str_value.copy(ostr->ptr(), ostr->length(), - ostr->charset(), tocs, &cnv_errors); - if (cnv_errors) - return NULL; - cnvitem->str_value.mark_as_const(); - cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen; - return cnvitem; + if (collation.collation == &my_charset_bin && tocs != &my_charset_bin && + !this->check_well_formed_result(s, true)) + return NULL; + return this; } - return Item::safe_charset_converter(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))) + Item_string *conv= func_name ? + new Item_static_string_func(func_name, + s, tocs, &conv_errors, + collation.derivation, + collation.repertoire) : + new Item_string(s, tocs, &conv_errors, + collation.derivation, + collation.repertoire); + + if (!conv || (conv_errors && lossless)) { /* Safe conversion is not possible (or EOM). @@ -1318,23 +1257,28 @@ Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) */ return NULL; } - conv->str_value.copy(); - /* Ensure that no one is going to change the result string */ - conv->str_value.mark_as_const(); + if (s->charset() == &my_charset_bin && tocs != &my_charset_bin && + !conv->check_well_formed_result(true)) + return NULL; return conv; } -bool Item_string::eq(const Item *item, bool binary_cmp) const +Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) { - if (type() == item->type() && item->basic_const_item()) - { - if (binary_cmp) - return !stringcmp(&str_value, &item->str_value); - return (collation.collation == item->collation.collation && - !sortcmp(&str_value, &item->str_value, collation.collation)); - } - return 0; + /* + Return "this" if in prepare. result_type may change at execition time, + to it's possible that the converter will not be needed at all: + + PREPARE stmt FROM 'SELECT * FROM t1 WHERE field = ?'; + SET @@arg= 1; + EXECUTE stms USING @arg; + + In the above example result_type is STRING_RESULT at prepare time, + and INT_RESULT at execution time. + */ + return !const_item() || state == NULL_VALUE ? + this : const_charset_converter(tocs, true); } @@ -2123,7 +2067,7 @@ bool agg_item_collations(DTCollation &c, const char *fname, bool unknown_cs= 0; c.set(av[0]->collation); - for (i= 1, arg= &av[item_sep]; i < count; i++, arg++) + for (i= 1, arg= &av[item_sep]; i < count; i++, arg+= item_sep) { if (c.aggregate((*arg)->collation, flags)) { @@ -2202,33 +2146,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) { - Item* conv; - uint32 dummy_offset; - if (!String::needs_conversion(1, (*arg)->collation.collation, - coll.collation, - &dummy_offset)) - continue; - - /* - No needs to add converter if an "arg" is NUMERIC or DATETIME - value (which is pure ASCII) and at the same time target DTCollation - is ASCII-compatible. For example, no needs to rewrite: - SELECT * FROM t1 WHERE datetime_field = '2010-01-01'; - to - SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01'; - - TODO: avoid conversion of any values with - repertoire ASCII and 7bit-ASCII-compatible, - not only numeric/datetime origin. - */ - if ((*arg)->collation.derivation == DERIVATION_NUMERIC && - (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII && - !((*arg)->collation.collation->state & MY_CS_NONASCII) && - !(coll.collation->state & MY_CS_NONASCII)) + Item* conv= (*arg)->safe_charset_converter(coll.collation); + if (conv == *arg) continue; - - if (!(conv= (*arg)->safe_charset_converter(coll.collation)) && - ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) + if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII)) conv= new Item_func_conv_charset(*arg, coll.collation, 1); if (!conv) @@ -3014,7 +2935,7 @@ String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); - str->set_real(value,decimals,&my_charset_bin); + str->set_real(value, decimals, &my_charset_numeric); return str; } @@ -3173,10 +3094,6 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value) } -bool Item_null::eq(const Item *item, bool binary_cmp) const -{ return item->type() == type(); } - - double Item_null::val_real() { // following assert is redundant, because fixed=1 assigned in constructor @@ -3245,8 +3162,6 @@ Item_param::Item_param(uint pos_in_query_arg) : value is set. */ maybe_null= 1; - cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE); - cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin); } @@ -3806,18 +3721,14 @@ bool Item_param::convert_str_value(THD *thd) str_value.set_charset(value.cs_info.final_character_set_of_str_value); /* Here str_value is guaranteed to be in final_character_set_of_str_value */ - max_length= str_value.numchars() * str_value.charset()->mbmaxlen; - - /* For the strings converted to numeric form within some functions */ - decimals= NOT_FIXED_DEC; /* str_value_ptr is returned from val_str(). It must be not alloced to prevent it's modification by val_str() invoker. */ str_value_ptr.set(str_value.ptr(), str_value.length(), str_value.charset()); - /* Synchronize item charset with value charset */ - collation.set(str_value.charset(), DERIVATION_COERCIBLE); + /* Synchronize item charset and length with value charset */ + fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE); } return rc; } @@ -3847,7 +3758,8 @@ Item_param::clone_item() case STRING_VALUE: case LONG_DATA_VALUE: return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), - str_value.charset()); + str_value.charset(), + collation.derivation, collation.repertoire); case TIME_VALUE: break; case NO_VALUE: @@ -3859,30 +3771,21 @@ Item_param::clone_item() bool -Item_param::eq(const Item *arg, bool binary_cmp) const +Item_param::eq(const Item *item, bool binary_cmp) const { - Item *item; - if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type()) + if (!basic_const_item()) return FALSE; - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - item= (Item*) arg; switch (state) { case NULL_VALUE: - return TRUE; + return null_eq(item); case INT_VALUE: - return value.integer == item->val_int() && - unsigned_flag == item->unsigned_flag; + return int_eq(value.integer, item); case REAL_VALUE: - return value.real == item->val_real(); + return real_eq(value.real, item); case STRING_VALUE: case LONG_DATA_VALUE: - if (binary_cmp) - return !stringcmp(&str_value, &item->str_value); - return !sortcmp(&str_value, &item->str_value, collation.collation); + return str_eq(&str_value, item, binary_cmp); default: break; } @@ -5380,13 +5283,6 @@ bool Item_field::vcol_in_partition_func_processor(uchar *int_arg) } -Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) -{ - no_const_subst= 1; - return Item::safe_charset_converter(tocs); -} - - void Item_field::cleanup() { DBUG_ENTER("Item_field::cleanup"); @@ -5692,10 +5588,7 @@ String *Item::check_well_formed_result(String *str, bool send_error) { /* Check whether we got a well-formed string */ CHARSET_INFO *cs= str->charset(); - int well_formed_error; - uint wlen= cs->cset->well_formed_len(cs, - str->ptr(), str->ptr() + str->length(), - str->length(), &well_formed_error); + uint wlen= str->well_formed_length(); if (wlen < str->length()) { THD *thd= current_thd; @@ -6183,24 +6076,6 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions) } -bool Item_int::eq(const Item *arg, bool binary_cmp) const -{ - /* No need to check for null value as basic constant can't be NULL */ - if (arg->basic_const_item() && arg->type() == type()) - { - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - Item *item= (Item*) arg; - return (item->val_int() == value && - ((longlong) value >= 0 || - (item->unsigned_flag == unsigned_flag))); - } - return FALSE; -} - - Item *Item_int_with_ref::clone_item() { DBUG_ASSERT(ref->const_item()); @@ -6318,27 +6193,6 @@ void Item_float::print(String *str, enum_query_type query_type) } -/* - hex item - In string context this is a binary string. - In number context this is a longlong value. -*/ - -bool Item_float::eq(const Item *arg, bool binary_cmp) const -{ - if (arg->basic_const_item() && arg->type() == type()) - { - /* - We need to cast off const to call val_int(). This should be OK for - a basic constant. - */ - Item *item= (Item*) arg; - return item->val_real() == value; - } - return FALSE; -} - - inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : @@ -6394,8 +6248,6 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions) ulonglong nr; uint32 length= str_value.length(); - if (!length) - return 1; if (length > 8) { @@ -6435,32 +6287,6 @@ void Item_hex_string::print(String *str, enum_query_type query_type) } -bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const -{ - if (arg->basic_const_item() && arg->type() == type() && - arg->cast_to_int_type() == cast_to_int_type()) - { - if (binary_cmp) - return !stringcmp(&str_value, &arg->str_value); - return !sortcmp(&str_value, &arg->str_value, collation.collation); - } - return FALSE; -} - - -Item *Item_hex_constant::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. |