summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/item.cc432
-rw-r--r--sql/item.h134
-rw-r--r--sql/sql_prepare.cc1
-rw-r--r--sql/sql_type.cc8
-rw-r--r--sql/sql_type.h4
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
{