summaryrefslogtreecommitdiff
path: root/sql/item_cmpfunc.cc
diff options
context:
space:
mode:
authorAlexander Barkov <alexander.barkov@oracle.com>2011-03-01 15:09:37 +0300
committerAlexander Barkov <alexander.barkov@oracle.com>2011-03-01 15:09:37 +0300
commit8a83d304368ad9af25bfa86cbf517aaccba14605 (patch)
tree2620f375e9ea9a3ac044bd938c7fc3add6671e8f /sql/item_cmpfunc.cc
parent48126a574cb31952032eb3c0ff07eaab101dc9e0 (diff)
downloadmariadb-git-8a83d304368ad9af25bfa86cbf517aaccba14605.tar.gz
Bug#11753363 (bug#44793) CHARACTER SETS: CASE CLAUSE, UCS2 OR UTF32, FAILURE
Problem: in case of string CASE/WHEN arguments with different character sets, Item_func_case::find_item() called comparator cmp_items[x] on mixed character set Items, so a 8-bit value could be errouneously referenced to as being utf16/utf32 value, which led to crash on DBUG_ASSERT() because of wrong value length. This was wrong, as string comparator expects arguments in the same character set. Fix: modify Item_func_case's argument list after calling agg_arg_charsets_for_comparison() - put the Items in "agg" array back to "args", because some of the Items in the "agg" array might have been changed to character set converters: - to Item_func_conv_charset for non-constant items - to Item_string for constant items In other words, perform the same substitution which is done in all other operations string comparison or string result operations: Replace CASE latin1_item WHEN utf16_item THEN ... END to CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END Replace CASE utf16_item WHEN latin1_item THEN ... END to CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END @ mysql-test/r/ctype_utf16.result @ mysql-test/r/ctype_utf32.result @ mysql-test/t/ctype_utf16.test @ mysql-test/t/ctype_utf32.test Adding tests @ sql/item_cmpfunc.cc Put "agg" back to "args". @ sql/sql_string.cc Backporting a fix for String::set_or_copy_aligned() from 5.6, for better test coverage: "SELECT _utf16 0x61" should expand the string to 0x0061 rather than to 0x000061. This fix was made in 5.6 under terms of "WL#4616 Implement UTF16-LE".
Diffstat (limited to 'sql/item_cmpfunc.cc')
-rw-r--r--sql/item_cmpfunc.cc45
1 files changed, 42 insertions, 3 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index df541f603ee..68c63285693 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3054,20 +3054,59 @@ void Item_func_case::fix_length_and_dec()
agg[0]= args[first_expr_num];
left_result_type= agg[0]->result_type();
+ /*
+ As the first expression and WHEN expressions
+ are intermixed in args[] array THEN and ELSE items,
+ extract the first expression and all WHEN expressions into
+ a temporary array, to process them easier.
+ */
for (nagg= 0; nagg < ncases/2 ; nagg++)
agg[nagg+1]= args[nagg*2];
nagg++;
if (!(found_types= collect_cmp_types(agg, nagg)))
return;
+ if (found_types & (1 << STRING_RESULT))
+ {
+ /*
+ If we'll do string comparison, we also need to aggregate
+ character set and collation for first/WHEN items and
+ install converters for some of them to cmp_collation when necessary.
+ This is done because cmp_item compatators cannot compare
+ strings in two different character sets.
+ Some examples when we install converters:
+
+ 1. Converter installed for the first expression:
+
+ CASE latin1_item WHEN utf16_item THEN ... END
+
+ is replaced to:
+
+ CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END
+
+ 2. Converter installed for the left WHEN item:
+ CASE utf16_item WHEN latin1_item THEN ... END
+
+ is replaced to:
+
+ CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
+ */
+ if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg))
+ return;
+ /*
+ Now copy first expression and all WHEN expressions back to args[]
+ arrray, because some of the items might have been changed to converters
+ (e.g. Item_func_conv_charset, or Item_string for constants).
+ */
+ args[first_expr_num]= agg[0];
+ for (nagg= 0; nagg < ncases / 2; nagg++)
+ args[nagg * 2]= agg[nagg + 1];
+ }
for (i= 0; i <= (uint)DECIMAL_RESULT; i++)
{
if (found_types & (1 << i) && !cmp_items[i])
{
DBUG_ASSERT((Item_result)i != ROW_RESULT);
- if ((Item_result)i == STRING_RESULT &&
- agg_arg_charsets_for_comparison(cmp_collation, agg, nagg))
- return;
if (!(cmp_items[i]=
cmp_item::get_comparator((Item_result)i,
cmp_collation.collation)))