From 45e40892c5bcd541cd93aebe8ba15b7a27289621 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 23 Jan 2017 22:25:29 +0400 Subject: MDEV-11134 Assertion `fixed' failed in Item::const_charset_converter(THD*, CHARSET_INFO*, bool, const char*) Problem: Item_param::basic_const_item() returned true when fixed==false. This unexpected combination made Item::const_charset_converter() crash on asserts. Fix: - Changing all Item_param::set_xxx() to set "fixed" to true. This fixes the problem. - Additionally, changing all Item_param::set_xxx() to set Item_param::item_type, to avoid duplicate code, and for consistency, to make the code symmetric between different constant types. Before this patch only set_null() set item_type. - Moving Item_param::state and Item_param::item_type from public to private, to make sure easier that these members are in sync with "fixed" and to each other. - Adding a new argument "unsigned_arg" to Item::set_decimal(), and reusing it in two places instead of duplicate code. - Adding a new method Item_param::fix_temporal() and reusing it in two places. - Adding methods has_no_value(), has_long_data_value(), has_int_value(), instead of direct access to Item_param::state. --- sql/item.cc | 69 ++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 31 deletions(-) (limited to 'sql/item.cc') diff --git a/sql/item.cc b/sql/item.cc index 8698b0f7fa4..bab49115482 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3309,9 +3309,9 @@ Item_param::Item_param(THD *thd, uint pos_in_query_arg): Rewritable_query_parameter(pos_in_query_arg, 1), Type_handler_hybrid_field_type(MYSQL_TYPE_VARCHAR), state(NO_VALUE), - indicators(0), indicator(STMT_INDICATOR_NONE), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), + indicators(0), indicator(STMT_INDICATOR_NONE), set_param_func(default_set_param_func), m_out_param_info(NULL) { @@ -3338,7 +3338,7 @@ void Item_param::set_null() max_length= 0; decimals= 0; state= NULL_VALUE; - item_type= Item::NULL_ITEM; + fix_type(Item::NULL_ITEM); DBUG_VOID_RETURN; } @@ -3350,6 +3350,7 @@ void Item_param::set_int(longlong i, uint32 max_length_arg) max_length= max_length_arg; decimals= 0; maybe_null= 0; + fix_type(Item::INT_ITEM); DBUG_VOID_RETURN; } @@ -3361,6 +3362,7 @@ void Item_param::set_double(double d) max_length= DBL_DIG + 8; decimals= NOT_FIXED_DEC; maybe_null= 0; + fix_type(Item::REAL_ITEM); DBUG_VOID_RETURN; } @@ -3390,21 +3392,41 @@ void Item_param::set_decimal(const char *str, ulong length) my_decimal_precision_to_length_no_truncation(decimal_value.precision(), decimals, unsigned_flag); maybe_null= 0; + fix_type(Item::DECIMAL_ITEM); DBUG_VOID_RETURN; } -void Item_param::set_decimal(const my_decimal *dv) +void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg) { state= DECIMAL_VALUE; my_decimal2decimal(dv, &decimal_value); decimals= (uint8) decimal_value.frac; - unsigned_flag= !decimal_value.sign(); + unsigned_flag= unsigned_arg; max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); + fix_type(Item::DECIMAL_ITEM); +} + + +void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg) +{ + state= TIME_VALUE; + max_length= max_length_arg; + decimals= decimals_arg; + fix_type(Item::DATE_ITEM); } + +void Item_param::set_time(const MYSQL_TIME *tm, + uint32 max_length_arg, uint decimals_arg) +{ + value.time= *tm; + fix_temporal(max_length_arg, decimals_arg); +} + + /** Set parameter value from MYSQL_TIME value. @@ -3433,11 +3455,9 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, &str, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } - - state= TIME_VALUE; maybe_null= 0; - max_length= max_length_arg; - decimals= tm->second_part > 0 ? TIME_SECOND_PART_DIGITS : 0; + fix_temporal(max_length_arg, + tm->second_part > 0 ? TIME_SECOND_PART_DIGITS : 0); DBUG_VOID_RETURN; } @@ -3458,6 +3478,7 @@ bool Item_param::set_str(const char *str, ulong length) maybe_null= 0; /* max_length and decimals are set after charset conversion */ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ + fix_type(Item::STRING_ITEM); DBUG_RETURN(FALSE); } @@ -3489,6 +3510,7 @@ bool Item_param::set_longdata(const char *str, ulong length) DBUG_RETURN(TRUE); state= LONG_DATA_VALUE; maybe_null= 0; + fix_type(Item::STRING_ITEM); DBUG_RETURN(FALSE); } @@ -3547,7 +3569,6 @@ bool Item_param::set_from_item(THD *thd, Item *item) { unsigned_flag= item->unsigned_flag; set_int(val, MY_INT64_NUM_DECIMAL_DIGITS); - item_type= Item::INT_ITEM; set_handler_by_result_type(item->result_type()); DBUG_RETURN(!unsigned_flag && value.integer < 0 ? 1 : 0); } @@ -3559,12 +3580,10 @@ bool Item_param::set_from_item(THD *thd, Item *item) switch (item->cmp_type()) { case REAL_RESULT: set_double(tmp.value.m_double); - item_type= Item::REAL_ITEM; set_handler_by_field_type(MYSQL_TYPE_DOUBLE); break; case INT_RESULT: set_int(tmp.value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS); - item_type= Item::INT_ITEM; set_handler_by_field_type(MYSQL_TYPE_LONGLONG); break; case STRING_RESULT: @@ -3574,7 +3593,6 @@ bool Item_param::set_from_item(THD *thd, Item *item) Exact value of max_length is not known unless data is converted to charset of connection, so we have to set it later. */ - item_type= Item::STRING_ITEM; set_handler_by_field_type(MYSQL_TYPE_VARCHAR); if (set_str(tmp.m_string.ptr(), tmp.m_string.length())) @@ -3583,24 +3601,13 @@ bool Item_param::set_from_item(THD *thd, Item *item) } case DECIMAL_RESULT: { - const my_decimal *ent_value= &tmp.m_decimal; - my_decimal2decimal(ent_value, &decimal_value); - state= DECIMAL_VALUE; - decimals= ent_value->frac; - max_length= - my_decimal_precision_to_length_no_truncation(ent_value->precision(), - decimals, unsigned_flag); - item_type= Item::DECIMAL_ITEM; + set_decimal(&tmp.m_decimal, unsigned_flag); set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); break; } case TIME_RESULT: { - value.time= tmp.value.m_time; - state= TIME_VALUE; - max_length= item->max_length; - decimals= item->decimals; - item_type= Item::DATE_ITEM; + set_time(&tmp.value.m_time, item->max_length, item->decimals); set_handler(item->type_handler()); break; } @@ -3641,6 +3648,7 @@ void Item_param::reset() state= NO_VALUE; maybe_null= 1; null_value= 0; + fixed= false; /* Don't reset item_type to PARAM_ITEM: it's only needed to guard us from item optimizations at prepare stage, when item doesn't yet @@ -3978,6 +3986,7 @@ 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) return FALSE; return TRUE; @@ -4112,6 +4121,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src) maybe_null= src->maybe_null; null_value= src->null_value; state= src->state; + fixed= src->fixed; value= src->value; decimal_value.swap(src->decimal_value); @@ -4123,6 +4133,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src) void Item_param::set_default() { state= DEFAULT_VALUE; + fixed= true; /* When Item_param is set to DEFAULT_VALUE: - its val_str() and val_decimal() return NULL @@ -4137,6 +4148,7 @@ void Item_param::set_default() void Item_param::set_ignore() { state= IGNORE_VALUE; + fixed= true; null_value= true; } @@ -4182,18 +4194,15 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) str_value.charset()); collation.set(str_value.charset(), DERIVATION_COERCIBLE); decimals= 0; - item_type= Item::STRING_ITEM; break; } case REAL_RESULT: set_double(arg->val_real()); - item_type= Item::REAL_ITEM; break; case INT_RESULT: set_int(arg->val_int(), arg->max_length); - item_type= Item::INT_ITEM; break; case DECIMAL_RESULT: @@ -4204,8 +4213,7 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) if (!dv) return TRUE; - set_decimal(dv); - item_type= Item::DECIMAL_ITEM; + set_decimal(dv, !dv->sign()); break; } @@ -4215,7 +4223,6 @@ Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it) DBUG_ASSERT(TRUE); // Abort in debug mode. set_null(); // Set to NULL in release mode. - item_type= Item::NULL_ITEM; return FALSE; } -- cgit v1.2.1