summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2016-04-20 08:53:30 +0400
committerAlexander Barkov <bar@mariadb.org>2016-04-20 08:53:30 +0400
commit9a987142f93756b37b2ff02d513034cc4079c978 (patch)
tree2763b09ba5f9af99a3d0fc654a2111c0b765dec8 /sql/item_cmpfunc.cc
parent6c0e231c0282b43d6a46a4983f5971e960d3b8ca (diff)
downloadmariadb-git-9a987142f93756b37b2ff02d513034cc4079c978.tar.gz
MDEV-9745 Crash with CASE WHEN TRUE THEN COALESCE(CAST(NULL AS UNSIGNED)) ELSE 4 END
This is a backport of the patch for MDEV-9653 (fixed earlier in 10.1.13). The code in Item_func_case::fix_length_and_dec() did not calculate max_length and decimals properly. In case of any numeric result (DECIMAL, REAL, INT) a generic method Item_func_case::agg_num_lengths() was called, which could erroneously result into a DECIMAL item with max_length==0 and decimals==0, so the constructor of Field_new_decimals tried to create a field of DECIMAL(0,0) type, which caused a crash. Unlike Item_func_case, the code responsible for merging attributes in Item_func_coalesce::fix_length_and_dec() works fine: it has specific execution branches for all distinct numeric types and correctly creates a DECIMAL(1,0) column instead of DECIMAL(0,0) for the same set of arguments. The fix does the following: - Moves the attribute merging code from Item_func_coalesce::fix_length_and_dec() to a new method Item_func_hybrid_result_type::fix_attributes() - Removes the wrong code from Item_func_case::fix_length_and_dec() and reuses fix_attributes() in both Item_func_coalesce::fix_length_and_dec() and Item_func_case::fix_length_and_dec() - Fixes count_real_length() and count_decimal_length() to get an array of Items as an argument, instead of using Item::args directly. This is needed for Item_func_case::fix_length_and_dec(). - Moves methods Item_func::count_xxx_length() from "public" to "protected". - Removes Item_func_case::agg_num_length(), as it's not used any more. - Additionally removes Item_func_case::agg_str_length(), as it also was not used (dead code).
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc51
1 files changed, 18 insertions, 33 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 9287b74a867..fb75c9af794 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3015,24 +3015,6 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
}
-void Item_func_case::agg_str_lengths(Item* arg)
-{
- fix_char_length(max(max_char_length(), arg->max_char_length()));
- set_if_bigger(decimals, arg->decimals);
- unsigned_flag= unsigned_flag && arg->unsigned_flag;
-}
-
-
-void Item_func_case::agg_num_lengths(Item *arg)
-{
- uint len= my_decimal_length_to_precision(arg->max_length, arg->decimals,
- arg->unsigned_flag) - arg->decimals;
- set_if_bigger(max_length, len);
- set_if_bigger(decimals, arg->decimals);
- unsigned_flag= unsigned_flag && arg->unsigned_flag;
-}
-
-
/**
Check if (*place) and new_value points to different Items and call
THD::change_item_tree() if needed.
@@ -3098,17 +3080,7 @@ void Item_func_case::fix_length_and_dec()
}
else
{
- collation.set_numeric();
- max_length=0;
- decimals=0;
- unsigned_flag= TRUE;
- for (uint i= 0; i < ncases; i+= 2)
- agg_num_lengths(args[i + 1]);
- if (else_expr_num != -1)
- agg_num_lengths(args[else_expr_num]);
- max_length= my_decimal_precision_to_length_no_truncation(max_length +
- decimals, decimals,
- unsigned_flag);
+ fix_attributes(agg, nagg);
}
/*
@@ -3336,19 +3308,32 @@ void Item_func_coalesce::fix_length_and_dec()
{
cached_field_type= agg_field_type(args, arg_count);
agg_result_type(&cached_result_type, args, arg_count);
+ fix_attributes(args, arg_count);
+}
+
+
+#if MYSQL_VERSION_ID > 100100
+#error Rename this to Item_hybrid_func::fix_attributes() when mering to 10.1
+#endif
+void Item_func_hybrid_result_type::fix_attributes(Item **items, uint nitems)
+{
switch (cached_result_type) {
case STRING_RESULT:
- if (count_string_result_length(cached_field_type, args, arg_count))
+ if (count_string_result_length(field_type(),
+ items, nitems))
return;
break;
case DECIMAL_RESULT:
- count_decimal_length();
+ collation.set_numeric();
+ count_decimal_length(items, nitems);
break;
case REAL_RESULT:
- count_real_length();
+ collation.set_numeric();
+ count_real_length(items, nitems);
break;
case INT_RESULT:
- count_only_length(args, arg_count);
+ collation.set_numeric();
+ count_only_length(items, nitems);
decimals= 0;
break;
case ROW_RESULT: