diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 432 | ||||
-rw-r--r-- | sql/item.h | 134 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 1 | ||||
-rw-r--r-- | sql/sql_type.cc | 8 | ||||
-rw-r--r-- | sql/sql_type.h | 4 |
5 files changed, 343 insertions, 236 deletions
diff --git a/sql/item.cc b/sql/item.cc index a919a7010ae..c9f5462cca9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3765,8 +3765,9 @@ void Item_param::set_null() void Item_param::set_int(longlong i, uint32 max_length_arg) { DBUG_ENTER("Item_param::set_int"); + DBUG_ASSERT(value.type_handler()->cmp_type() == INT_RESULT); value.integer= (longlong) i; - state= INT_VALUE; + state= SHORT_DATA_VALUE; collation.set_numeric(); max_length= max_length_arg; decimals= 0; @@ -3778,8 +3779,9 @@ void Item_param::set_int(longlong i, uint32 max_length_arg) void Item_param::set_double(double d) { DBUG_ENTER("Item_param::set_double"); + DBUG_ASSERT(value.type_handler()->cmp_type() == REAL_RESULT); value.real= d; - state= REAL_VALUE; + state= SHORT_DATA_VALUE; collation.set_numeric(); max_length= DBL_DIG + 8; decimals= NOT_FIXED_DEC; @@ -3805,14 +3807,15 @@ void Item_param::set_decimal(const char *str, ulong length) { char *end; DBUG_ENTER("Item_param::set_decimal"); + DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT); end= (char*) str+length; - str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end); - state= DECIMAL_VALUE; - decimals= decimal_value.frac; + str2my_decimal(E_DEC_FATAL_ERROR, str, &value.m_decimal, &end); + state= SHORT_DATA_VALUE; + decimals= value.m_decimal.frac; collation.set_numeric(); max_length= - my_decimal_precision_to_length_no_truncation(decimal_value.precision(), + my_decimal_precision_to_length_no_truncation(value.m_decimal.precision(), decimals, unsigned_flag); maybe_null= 0; fix_type(Item::DECIMAL_ITEM); @@ -3821,14 +3824,15 @@ void Item_param::set_decimal(const char *str, ulong length) void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) { - state= DECIMAL_VALUE; + DBUG_ASSERT(value.type_handler()->cmp_type() == DECIMAL_RESULT); + state= SHORT_DATA_VALUE; - my_decimal2decimal(dv, &decimal_value); + my_decimal2decimal(dv, &value.m_decimal); - decimals= (uint8) decimal_value.frac; + decimals= (uint8) value.m_decimal.frac; collation.set_numeric(); unsigned_flag= unsigned_arg; - max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, + max_length= my_decimal_precision_to_length(value.m_decimal.intg + decimals, decimals, unsigned_flag); fix_type(Item::DECIMAL_ITEM); } @@ -3836,7 +3840,7 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) { - state= TIME_VALUE; + state= SHORT_DATA_VALUE; collation.set_numeric(); max_length= max_length_arg; decimals= decimals_arg; @@ -3847,6 +3851,7 @@ void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) void Item_param::set_time(const MYSQL_TIME *tm, uint32 max_length_arg, uint decimals_arg) { + DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT); value.time= *tm; fix_temporal(max_length_arg, decimals_arg); } @@ -3869,6 +3874,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, uint32 max_length_arg) { DBUG_ENTER("Item_param::set_time"); + DBUG_ASSERT(value.type_handler()->cmp_type() == TIME_RESULT); value.time= *tm; value.time.time_type= time_type; @@ -3891,12 +3897,13 @@ bool Item_param::set_str(const char *str, ulong length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { DBUG_ENTER("Item_param::set_str"); + DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT); /* Assign string with no conversion: data is converted only after it's been written to the binary log. */ uint dummy_errors; - if (str_value.copy(str, length, fromcs, tocs, &dummy_errors)) + if (value.m_string.copy(str, length, fromcs, tocs, &dummy_errors)) DBUG_RETURN(TRUE); /* Set str_value_ptr to make sure it's in sync with str_value. @@ -3909,10 +3916,10 @@ bool Item_param::set_str(const char *str, ulong length, later, which will convert the value from the client character set to the connection character set, and will reset both str_value and str_value_ptr. */ - str_value_ptr.set(str_value.ptr(), - str_value.length(), - str_value.charset()); - state= STRING_VALUE; + value.m_string_ptr.set(value.m_string.ptr(), + value.m_string.length(), + value.m_string.charset()); + state= SHORT_DATA_VALUE; collation.set(tocs, DERIVATION_COERCIBLE); max_length= length; maybe_null= 0; @@ -3926,6 +3933,7 @@ bool Item_param::set_str(const char *str, ulong length, bool Item_param::set_longdata(const char *str, ulong length) { DBUG_ENTER("Item_param::set_longdata"); + DBUG_ASSERT(value.type_handler()->cmp_type() == STRING_RESULT); /* If client character set is multibyte, end of long data packet @@ -3936,7 +3944,7 @@ bool Item_param::set_longdata(const char *str, ulong length) (here), and first have to concatenate all pieces together, write query to the binary log and only then perform conversion. */ - if (str_value.length() + length > max_long_data_size) + if (value.m_string.length() + length > max_long_data_size) { my_message(ER_UNKNOWN_ERROR, "Parameter of prepared statement which is set through " @@ -3946,7 +3954,7 @@ bool Item_param::set_longdata(const char *str, ulong length) DBUG_RETURN(true); } - if (str_value.append(str, length, &my_charset_bin)) + if (value.m_string.append(str, length, &my_charset_bin)) DBUG_RETURN(TRUE); state= LONG_DATA_VALUE; maybe_null= 0; @@ -4009,15 +4017,16 @@ bool Item_param::set_from_item(THD *thd, Item *item) else { unsigned_flag= item->unsigned_flag; - set_handler_by_result_type(item->result_type()); + set_handler(item->type_handler()); DBUG_RETURN(set_limit_clause_param(val)); } } struct st_value tmp; if (!item->save_in_value(&tmp)) { - if (item->type_handler()->Item_param_set_from_value(thd, this, item, &tmp)) - DBUG_RETURN(true); + const Type_handler *h= item->type_handler(); + set_handler(h); + DBUG_RETURN(set_value(thd, item, &tmp, h)); } else set_null(); @@ -4037,16 +4046,16 @@ void Item_param::reset() { DBUG_ENTER("Item_param::reset"); /* Shrink string buffer if it's bigger than max possible CHAR column */ - if (str_value.alloced_length() > MAX_CHAR_WIDTH) - str_value.free(); + if (value.m_string.alloced_length() > MAX_CHAR_WIDTH) + value.m_string.free(); else - str_value.length(0); - str_value_ptr.length(0); + value.m_string.length(0); + value.m_string_ptr.length(0); /* We must prevent all charset conversions until data has been written to the binary log. */ - str_value.set_charset(&my_charset_bin); + value.m_string.set_charset(&my_charset_bin); collation.set(&my_charset_bin, DERIVATION_COERCIBLE); state= NO_VALUE; maybe_null= 1; @@ -4075,19 +4084,9 @@ int Item_param::save_in_field(Field *field, bool no_conversions) Garbage (e.g. in case of a memory overrun) is handled after the switch. */ switch (state) { - case INT_VALUE: - return field->store(value.integer, unsigned_flag); - case REAL_VALUE: - return field->store(value.real); - case DECIMAL_VALUE: - return field->store_decimal(&decimal_value); - case TIME_VALUE: - field->store_time_dec(&value.time, decimals); - return 0; - case STRING_VALUE: + case SHORT_DATA_VALUE: case LONG_DATA_VALUE: - return field->store(str_value.ptr(), str_value.length(), - str_value.charset()); + return value.type_handler()->Item_save_in_field(this, field, no_conversions); case NULL_VALUE: return set_field_to_null_with_conversions(field, no_conversions); case DEFAULT_VALUE: @@ -4107,6 +4106,28 @@ int Item_param::save_in_field(Field *field, bool no_conversions) } +bool Item_param::can_return_value() const +{ + // There's no "default". See comments in Item_param::save_in_field(). + switch (state) { + case SHORT_DATA_VALUE: + case LONG_DATA_VALUE: + return true; + case IGNORE_VALUE: + case DEFAULT_VALUE: + invalid_default_param(); + // fall through + case NULL_VALUE: + return false; + case NO_VALUE: + DBUG_ASSERT(0); // Should not be possible + return false; + } + DBUG_ASSERT(0); // Garbage + return false; +} + + void Item_param::invalid_default_param() const { my_message(ER_INVALID_DEFAULT_PARAM, @@ -4116,7 +4137,14 @@ void Item_param::invalid_default_param() const bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) { - if (state == TIME_VALUE) + /* + LIMIT clause parameter should not call get_date() + For non-LIMIT parameters, handlers must be the same. + */ + DBUG_ASSERT(type_handler()->result_type() == + value.type_handler()->result_type()); + if (state == SHORT_DATA_VALUE && + value.type_handler()->cmp_type() == TIME_RESULT) { *res= value.time; return 0; @@ -4125,157 +4153,117 @@ bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate) } -double Item_param::val_real() +double Item_param::PValue::val_real() const { - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case REAL_VALUE: - return value.real; - case INT_VALUE: - return (double) value.integer; - case DECIMAL_VALUE: + switch (type_handler()->cmp_type()) { + case REAL_RESULT: + return real; + case INT_RESULT: + return (double) integer; + case DECIMAL_RESULT: { double result; - my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); + my_decimal2double(E_DEC_FATAL_ERROR, &m_decimal, &result); return result; } - case STRING_VALUE: - case LONG_DATA_VALUE: - { - return double_from_string_with_check(&str_value); - } - case TIME_VALUE: + case STRING_RESULT: + return double_from_string_with_check(&m_string); + case TIME_RESULT: /* This works for example when user says SELECT ?+0.0 and supplies time value for the placeholder. */ - return TIME_to_double(&value.time); - case IGNORE_VALUE: - case DEFAULT_VALUE: - invalid_default_param(); - // fall through - case NULL_VALUE: - return 0.0; - case NO_VALUE: - DBUG_ASSERT(0); // Should not be possible - return 0.0; + return TIME_to_double(&time); + case ROW_RESULT: + DBUG_ASSERT(0); + break; } - DBUG_ASSERT(0); // Garbage return 0.0; -} +} -longlong Item_param::val_int() -{ - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case REAL_VALUE: - return (longlong) rint(value.real); - case INT_VALUE: - return value.integer; - case DECIMAL_VALUE: +longlong Item_param::PValue::val_int(const Type_std_attributes *attr) const +{ + switch (type_handler()->cmp_type()) { + case REAL_RESULT: + return (longlong) rint(real); + case INT_RESULT: + return integer; + case DECIMAL_RESULT: { longlong i; - my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i); + my_decimal2int(E_DEC_FATAL_ERROR, &m_decimal, attr->unsigned_flag, &i); return i; } - case STRING_VALUE: - case LONG_DATA_VALUE: - { - return longlong_from_string_with_check(&str_value); - } - case TIME_VALUE: - return (longlong) TIME_to_ulonglong(&value.time); - case IGNORE_VALUE: - case DEFAULT_VALUE: - invalid_default_param(); - // fall through - case NULL_VALUE: - return 0; - case NO_VALUE: - DBUG_ASSERT(0); // Should not be possible - return 0; + case STRING_RESULT: + return longlong_from_string_with_check(&m_string); + case TIME_RESULT: + return (longlong) TIME_to_ulonglong(&time); + case ROW_RESULT: + DBUG_ASSERT(0); + break; } - DBUG_ASSERT(0); // Garbage return 0; } -my_decimal *Item_param::val_decimal(my_decimal *dec) +my_decimal *Item_param::PValue::val_decimal(my_decimal *dec, + const Type_std_attributes *attr) { - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case DECIMAL_VALUE: - return &decimal_value; - case REAL_VALUE: - double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec); + switch (type_handler()->cmp_type()) { + case DECIMAL_RESULT: + return &m_decimal; + case REAL_RESULT: + double2my_decimal(E_DEC_FATAL_ERROR, real, dec); return dec; - case INT_VALUE: - int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec); + case INT_RESULT: + int2my_decimal(E_DEC_FATAL_ERROR, integer, attr->unsigned_flag, dec); return dec; - case STRING_VALUE: - case LONG_DATA_VALUE: - return decimal_from_string_with_check(dec, &str_value); - case TIME_VALUE: - { - return TIME_to_my_decimal(&value.time, dec); - } - case IGNORE_VALUE: - case DEFAULT_VALUE: - invalid_default_param(); - // fall through - case NULL_VALUE: - return 0; - case NO_VALUE: - DBUG_ASSERT(0); // Should not be possible - return 0; + case STRING_RESULT: + return decimal_from_string_with_check(dec, &m_string); + case TIME_RESULT: + return TIME_to_my_decimal(&time, dec); + case ROW_RESULT: + DBUG_ASSERT(0); + break; } - DBUG_ASSERT(0); // Gabrage return 0; } -String *Item_param::val_str(String* str) -{ - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case STRING_VALUE: - case LONG_DATA_VALUE: - return &str_value_ptr; - case REAL_VALUE: - str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); +String *Item_param::PValue::val_str(String *str, + const Type_std_attributes *attr) +{ + switch (type_handler()->cmp_type()) { + case STRING_RESULT: + return &m_string_ptr; + case REAL_RESULT: + str->set_real(real, NOT_FIXED_DEC, &my_charset_bin); return str; - case INT_VALUE: - str->set(value.integer, &my_charset_bin); + case INT_RESULT: + str->set(integer, &my_charset_bin); return str; - case DECIMAL_VALUE: - if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, - 0, 0, 0, str) <= 1) + case DECIMAL_RESULT: + if (my_decimal2string(E_DEC_FATAL_ERROR, &m_decimal, 0, 0, 0, str) <= 1) return str; return NULL; - case TIME_VALUE: + case TIME_RESULT: { if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) - break; - str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr(), - decimals)); + return NULL; + str->length((uint) my_TIME_to_str(&time, (char*) str->ptr(), + attr->decimals)); str->set_charset(&my_charset_bin); return str; } - case IGNORE_VALUE: - case DEFAULT_VALUE: - invalid_default_param(); - // fall through - case NULL_VALUE: - return NULL; - case NO_VALUE: - DBUG_ASSERT(0); // Should not be possible - return NULL; + case ROW_RESULT: + DBUG_ASSERT(0); + break; } - DBUG_ASSERT(0); // Garbage return NULL; } + /** Return Param item values in string format, for generating the dynamic query used in update/binary logs. @@ -4287,32 +4275,31 @@ String *Item_param::val_str(String* str) that binary log contains wrong statement */ -const String *Item_param::query_val_str(THD *thd, String* str) const +const String *Item_param::value_query_val_str(THD *thd, String *str) const { - // There's no "default". See comments in Item_param::save_in_field(). - switch (state) { - case INT_VALUE: + switch (value.type_handler()->cmp_type()) { + case INT_RESULT: str->set_int(value.integer, unsigned_flag, &my_charset_bin); return str; - case REAL_VALUE: + case REAL_RESULT: str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); return str; - case DECIMAL_VALUE: - if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, + case DECIMAL_RESULT: + if (my_decimal2string(E_DEC_FATAL_ERROR, &value.m_decimal, 0, 0, 0, str) > 1) return &my_null_string; return str; - case TIME_VALUE: + case TIME_RESULT: { static const uint32 typelen= 9; // "TIMESTAMP" is the longest type name char *buf, *ptr; str->length(0); /* TODO: in case of error we need to notify replication - that binary log contains wrong statement + that binary log contains wrong statement */ if (str->reserve(MAX_DATE_STRING_REP_LENGTH + 3 + typelen)) - break; + return NULL; /* Create date string inplace */ switch (value.time.time_type) { @@ -4338,15 +4325,29 @@ const String *Item_param::query_val_str(THD *thd, String* str) const str->length((uint32) (ptr - buf)); return str; } - case STRING_VALUE: - case LONG_DATA_VALUE: + case STRING_RESULT: { str->length(0); append_query_string(value.cs_info.character_set_client, str, - str_value.ptr(), str_value.length(), + value.m_string.ptr(), value.m_string.length(), thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); return str; } + case ROW_RESULT: + DBUG_ASSERT(0); + break; + } + return NULL; +} + + +const String *Item_param::query_val_str(THD *thd, String* str) const +{ + // There's no "default". See comments in Item_param::save_in_field(). + switch (state) { + case SHORT_DATA_VALUE: + case LONG_DATA_VALUE: + return value_query_val_str(thd, str); case IGNORE_VALUE: case DEFAULT_VALUE: return &my_default_string; @@ -4369,19 +4370,20 @@ const String *Item_param::query_val_str(THD *thd, String* str) const bool Item_param::convert_str_value(THD *thd) { bool rc= FALSE; - if (state == STRING_VALUE || state == LONG_DATA_VALUE) + if ((state == SHORT_DATA_VALUE || state == LONG_DATA_VALUE) && + value.type_handler()->cmp_type() == STRING_RESULT) { - rc= value.cs_info.convert_if_needed(thd, &str_value); + rc= value.cs_info.convert_if_needed(thd, &value.m_string); /* Here str_value is guaranteed to be in final_character_set_of_str_value */ /* 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()); + value.m_string_ptr.set(value.m_string.ptr(), value.m_string.length(), + value.m_string.charset()); /* Synchronize item charset and length with value charset */ - fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE); + fix_charset_and_length_from_str_value(value.m_string, DERIVATION_COERCIBLE); } return rc; } @@ -4390,18 +4392,48 @@ bool Item_param::convert_str_value(THD *thd) bool Item_param::basic_const_item() const { DBUG_ASSERT(fixed || state == NO_VALUE); - if (state == NO_VALUE || state == TIME_VALUE) + if (state == NO_VALUE || + (state == SHORT_DATA_VALUE && type_handler()->cmp_type() == TIME_RESULT)) return FALSE; return TRUE; } +Item *Item_param::value_clone_item(THD *thd) +{ + MEM_ROOT *mem_root= thd->mem_root; + switch (value.type_handler()->cmp_type()) { + case INT_RESULT: + return (unsigned_flag ? + new (mem_root) Item_uint(thd, name.str, value.integer, max_length) : + new (mem_root) Item_int(thd, name.str, value.integer, max_length)); + case REAL_RESULT: + return new (mem_root) Item_float(thd, name.str, value.real, decimals, + max_length); + case DECIMAL_RESULT: + return 0; // Should create Item_decimal. See MDEV-11361. + case STRING_RESULT: + return new (mem_root) Item_string(thd, name.str, + value.m_string.c_ptr_quick(), + value.m_string.length(), + value.m_string.charset(), + collation.derivation, + collation.repertoire); + case TIME_RESULT: + break; + case ROW_RESULT: + DBUG_ASSERT(0); + break; + } + return 0; +} + + /* see comments in the header file */ Item * Item_param::clone_item(THD *thd) { - MEM_ROOT *mem_root= thd->mem_root; // There's no "default". See comments in Item_param::save_in_field(). switch (state) { case IGNORE_VALUE: @@ -4409,24 +4441,13 @@ Item_param::clone_item(THD *thd) invalid_default_param(); // fall through case NULL_VALUE: - return new (mem_root) Item_null(thd, name.str); - case INT_VALUE: - return (unsigned_flag ? - new (mem_root) Item_uint(thd, name.str, value.integer, max_length) : - new (mem_root) Item_int(thd, name.str, value.integer, max_length)); - case REAL_VALUE: - return new (mem_root) Item_float(thd, name.str, value.real, decimals, - max_length); - case DECIMAL_VALUE: - return 0; // Should create Item_decimal. See MDEV-11361. - case STRING_VALUE: + return new (thd->mem_root) Item_null(thd, name.str); + case SHORT_DATA_VALUE: case LONG_DATA_VALUE: - return new (mem_root) Item_string(thd, name.str, str_value.c_ptr_quick(), - str_value.length(), str_value.charset(), - collation.derivation, - collation.repertoire); - case TIME_VALUE: - return 0; + { + DBUG_ASSERT(type_handler()->cmp_type() == value.type_handler()->cmp_type()); + return value_clone_item(thd); + } case NO_VALUE: return 0; } @@ -4435,6 +4456,24 @@ Item_param::clone_item(THD *thd) } +bool Item_param::value_eq(const Item *item, bool binary_cmp) const +{ + switch (value.type_handler()->cmp_type()) { + case INT_RESULT: + return int_eq(value.integer, item); + case REAL_RESULT: + return real_eq(value.real, item); + case STRING_RESULT: + return str_eq(&value.m_string, item, binary_cmp); + case DECIMAL_RESULT: + case TIME_RESULT: + case ROW_RESULT: + break; + } + return false; +} + + bool Item_param::eq(const Item *item, bool binary_cmp) const { @@ -4449,15 +4488,9 @@ Item_param::eq(const Item *item, bool binary_cmp) const return false; case NULL_VALUE: return null_eq(item); - case INT_VALUE: - return int_eq(value.integer, item); - case REAL_VALUE: - return real_eq(value.real, item); - case STRING_VALUE: + case SHORT_DATA_VALUE: case LONG_DATA_VALUE: - return str_eq(&str_value, item, binary_cmp); - case DECIMAL_VALUE: - case TIME_VALUE: + return value_eq(item, binary_cmp); case NO_VALUE: return false; } @@ -4524,12 +4557,8 @@ Item_param::set_param_type_and_swap_value(Item_param *src) null_value= src->null_value; state= src->state; fixed= src->fixed; - value= src->value; - value.set_handler(src->value.type_handler()); - decimal_value.swap(src->decimal_value); - str_value.swap(src->str_value); - str_value_ptr.swap(src->str_value_ptr); + value.swap(src->value); } @@ -4575,8 +4604,15 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) { Item *arg= *it; struct st_value tmp; + /* + The OUT parameter is bound to some data type. + It's important not to touch m_type_handler, + to make sure the next mysql_stmt_execute() + correctly fetches the value from the client-server protocol, + using set_param_func(). + */ if (arg->save_in_value(&tmp) || - arg->type_handler()->Item_param_set_from_value(thd, this, arg, &tmp)) + set_value(thd, arg, &tmp, arg->type_handler())) { set_null(); return false; diff --git a/sql/item.h b/sql/item.h index 88146c4cee8..46a65b31b38 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2184,7 +2184,8 @@ protected: uint repertoire() const { return MY_STRING_METADATA::repertoire; } size_t char_length() const { return MY_STRING_METADATA::char_length; } }; - void fix_charset_and_length_from_str_value(Derivation dv, Metadata metadata) + void fix_charset_and_length(CHARSET_INFO *cs, + Derivation dv, Metadata metadata) { /* We have to have a different max_length than 'length' here to @@ -2193,13 +2194,13 @@ protected: number of chars for a string of this type because we in Create_field:: divide the max_length with mbmaxlen). */ - collation.set(str_value.charset(), dv, metadata.repertoire()); + collation.set(cs, dv, metadata.repertoire()); fix_char_length(metadata.char_length()); decimals= NOT_FIXED_DEC; } - void fix_charset_and_length_from_str_value(Derivation dv) + void fix_charset_and_length_from_str_value(const String &str, Derivation dv) { - fix_charset_and_length_from_str_value(dv, Metadata(&str_value)); + fix_charset_and_length(str.charset(), dv, Metadata(&str)); } Item_basic_value(THD *thd): Item(thd) {} /* @@ -3075,12 +3076,27 @@ public: For example in case of 'SELECT ?' you'll get MYSQL_TYPE_STRING both in result set and placeholders metadata, no matter what type you will supply for this placeholder in mysql_stmt_execute. + + Item_param has two Type_handler pointers, + which can point to different handlers: + + 1. In the Type_handler_hybrid_field_type member + It's initialized in: + - Item_param::setup_conversion(), for client-server PS protocol, + according to the bind type. + - Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE, + according to the actual parameter data type. + + 2. In the "value" member. + It's initialized in: + - Item_param::set_param_func(), for client-server PS protocol. + - Item_param::set_from_item(), for EXECUTE and EXECUTE IMMEDIATE. */ class Item_param :public Item_basic_value, private Settable_routine_parameter, public Rewritable_query_parameter, - public Type_handler_hybrid_field_type, + private Type_handler_hybrid_field_type, public Type_geometry_attributes { /* @@ -3126,9 +3142,8 @@ class Item_param :public Item_basic_value, */ enum enum_item_param_state { - NO_VALUE, NULL_VALUE, INT_VALUE, REAL_VALUE, - STRING_VALUE, TIME_VALUE, LONG_DATA_VALUE, - DECIMAL_VALUE, DEFAULT_VALUE, IGNORE_VALUE + NO_VALUE, NULL_VALUE, SHORT_DATA_VALUE, LONG_DATA_VALUE, + DEFAULT_VALUE, IGNORE_VALUE } state; enum Type item_type; @@ -3182,24 +3197,11 @@ class Item_param :public Item_basic_value, } }; - /* - A buffer for string and long data values. Historically all allocated - values returned from val_str() were treated as eligible to - modification. I. e. in some cases Item_func_concat can append it's - second argument to return value of the first one. Because of that we - can't return the original buffer holding string data from val_str(), - and have to have one buffer for data and another just pointing to - the data. This is the latter one and it's returned from val_str(). - Can not be declared inside the union as it's not a POD type. - */ - String str_value_ptr; - bool m_empty_string_is_null; - class PValue: public Type_handler_hybrid_field_type + class PValue_simple { public: - PValue(): Type_handler_hybrid_field_type(&type_handler_null) {} union { longlong integer; @@ -3207,11 +3209,52 @@ class Item_param :public Item_basic_value, CONVERSION_INFO cs_info; MYSQL_TIME time; }; + void swap(PValue_simple &other) + { + swap_variables(PValue_simple, *this, other); + } + }; + + class PValue: public Type_handler_hybrid_field_type, + public PValue_simple, + public Value_source + { + public: + PValue(): Type_handler_hybrid_field_type(&type_handler_null) {} + my_decimal m_decimal; + String m_string; + /* + A buffer for string and long data values. Historically all allocated + values returned from val_str() were treated as eligible to + modification. I. e. in some cases Item_func_concat can append it's + second argument to return value of the first one. Because of that we + can't return the original buffer holding string data from val_str(), + and have to have one buffer for data and another just pointing to + the data. This is the latter one and it's returned from val_str(). + Can not be declared inside the union as it's not a POD type. + */ + String m_string_ptr; + + void swap(PValue &other) + { + Type_handler_hybrid_field_type::swap(other); + PValue_simple::swap(other); + m_decimal.swap(other.m_decimal); + m_string.swap(other.m_string); + m_string_ptr.swap(other.m_string_ptr); + } + double val_real() const; + longlong val_int(const Type_std_attributes *attr) const; + my_decimal *val_decimal(my_decimal *dec, const Type_std_attributes *attr); + String *val_str(String *str, const Type_std_attributes *attr); }; PValue value; - my_decimal decimal_value; + const String *value_query_val_str(THD *thd, String* str) const; + bool value_eq(const Item *item, bool binary_cmp) const; + Item *value_clone_item(THD *thd); + bool can_return_value() const; public: /* @@ -3237,10 +3280,22 @@ public: return item_type; } - double val_real(); - longlong val_int(); - my_decimal *val_decimal(my_decimal*); - String *val_str(String*); + double val_real() + { + return can_return_value() ? value.val_real() : 0e0; + } + longlong val_int() + { + return can_return_value() ? value.val_int(this) : 0; + } + my_decimal *val_decimal(my_decimal *dec) + { + return can_return_value() ? value.val_decimal(dec, this) : NULL; + } + String *val_str(String *str) + { + return can_return_value() ? value.val_str(str, this) : NULL; + } bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate); int save_in_field(Field *field, bool no_conversions); @@ -3283,11 +3338,29 @@ public: */ void set_param_func(uchar **pos, ulong len) { - value.type_handler()->Item_param_set_param_func(this, pos, len); + /* + To avoid Item_param::set_xxx() asserting on data type mismatch, + we set the value type handler here: + - It can not be initialized yet after Item_param::setup_conversion(). + - Also, for LIMIT clause parameters, the value type handler might have + changed from the real type handler to type_handler_longlong. + So here we'll restore it. + */ + const Type_handler *h= Item_param::type_handler(); + value.set_handler(h); + h->Item_param_set_param_func(this, pos, len); + } + + bool set_value(THD *thd, const Type_all_attributes *attr, + const st_value *val, const Type_handler *h) + { + value.set_handler(h); // See comments in set_param_func() + return h->Item_param_set_from_value(thd, this, attr, val); } bool set_limit_clause_param(longlong nr) { + value.set_handler(&type_handler_longlong); set_int(nr, MY_INT64_NUM_DECIMAL_DIGITS); return !unsigned_flag && value.integer < 0; } @@ -3316,7 +3389,8 @@ public: } bool has_int_value() const { - return state == INT_VALUE; + return state == SHORT_DATA_VALUE && + value.type_handler()->cmp_type() == INT_RESULT; } /* This method is used to make a copy of a basic constant item when @@ -3571,7 +3645,7 @@ class Item_string :public Item_basic_constant protected: void fix_from_value(Derivation dv, const Metadata metadata) { - fix_charset_and_length_from_str_value(dv, metadata); + fix_charset_and_length(str_value.charset(), dv, metadata); // it is constant => can be used without fix_fields (and frequently used) fixed= 1; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9e17f388cff..5932fe136d7 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -751,7 +751,6 @@ void Item_param::setup_conversion(THD *thd, uchar param_type) if (!h) h= &type_handler_string; set_handler(h); - value.set_handler(h); h->Item_param_setup_conversion(thd, this); } diff --git a/sql/sql_type.cc b/sql/sql_type.cc index c2b2ebceab8..dd4cb2b8068 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -5015,7 +5015,6 @@ bool Type_handler_real_result:: { param->unsigned_flag= attr->unsigned_flag; param->set_double(val->value.m_double); - param->set_handler(&type_handler_double); return false; } @@ -5027,8 +5026,7 @@ bool Type_handler_int_result:: const st_value *val) const { param->unsigned_flag= attr->unsigned_flag; - param->set_int(val->value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS); - param->set_handler(&type_handler_longlong); + param->set_int(val->value.m_longlong, attr->max_length); return false; } @@ -5041,7 +5039,6 @@ bool Type_handler_decimal_result:: { param->unsigned_flag= attr->unsigned_flag; param->set_decimal(&val->m_decimal, attr->unsigned_flag); - param->set_handler(&type_handler_newdecimal); return false; } @@ -5058,7 +5055,6 @@ bool Type_handler_string_result:: Exact value of max_length is not known unless data is converted to charset of connection, so we have to set it later. */ - param->set_handler(&type_handler_varchar); return param->set_str(val->m_string.ptr(), val->m_string.length(), attr->collation.collation, attr->collation.collation); @@ -5073,7 +5069,6 @@ bool Type_handler_temporal_result:: { param->unsigned_flag= attr->unsigned_flag; param->set_time(&val->value.m_time, attr->max_length, attr->decimals); - param->set_handler(this); return false; } @@ -5087,7 +5082,6 @@ bool Type_handler_geometry:: { param->unsigned_flag= false; param->setup_conversion_blob(thd); - param->set_handler(&type_handler_geometry); param->set_geometry_type(attr->uint_geometry_type()); return param->set_str(val->m_string.ptr(), val->m_string.length(), &my_charset_bin, &my_charset_bin); diff --git a/sql/sql_type.h b/sql/sql_type.h index 9b8b6863141..6dbd51a1ebd 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2838,6 +2838,10 @@ public: Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other) :m_type_handler(other->m_type_handler) { } + void swap(Type_handler_hybrid_field_type &other) + { + swap_variables(const Type_handler *, m_type_handler, other.m_type_handler); + } const Type_handler *type_handler() const { return m_type_handler; } enum_field_types real_field_type() const { |