diff options
author | Alexander Barkov <alexander.barkov@oracle.com> | 2011-04-08 17:15:23 +0400 |
---|---|---|
committer | Alexander Barkov <alexander.barkov@oracle.com> | 2011-04-08 17:15:23 +0400 |
commit | ece1996b5afeb53b9266719be481656d7c1c0409 (patch) | |
tree | 2709dddaf34dda1e528467d7759bf2355f9f0503 /sql | |
parent | 6a293922899ed406d30b43d9018503f2bab2c5b7 (diff) | |
download | mariadb-git-ece1996b5afeb53b9266719be481656d7c1c0409.tar.gz |
Bug#11926811 / Bug#60625 Illegal mix of collations
Problem: comparison of a DATETIME sp variable and NOW()
led to Illegal mix of collations error when
character_set_connection=utf8.
Introduced by "WL#2649 Number-to-string conversions".
Error happened in Arg_comparator::set_compare_func(),
because the first argument was errouneously converted to utf8,
while the second argument was not.
Fix: separate agg_arg_charsets_for_comparison() into two functions:
- agg_arg_charsets_for_comparison() - for pure comparison,
when we don't need to return any string result and therefore
don't need to convert arguments to @@character_set_connection:
SELECT a = b;
- agg_arg_charsets_for_string_results_with_comparison() - when
we need to return a string result, but we also need to do
comparison internally: SELECT REPLACE(a,b,c)
If all arguments are numbers:
SELECT REPLACE(123,2,3) -> 133
we convert arguments to @@character_set_connection.
@ mysql-test/include/ctype_numconv.inc
@ mysql-test/r/ctype_binary.result
@ mysql-test/r/ctype_cp1251.result
@ mysql-test/r/ctype_latin1.result
@ mysql-test/r/ctype_ucs.result
@ mysql-test/r/ctype_utf8.result
Adding tests
@ sql/item.cc
@ sql/item.h
@ sql/item_func.cc
@ sql/item_func.h
@ sql/item_strfunc.cc
Introducing and using new function
agg_item_charsets_for_string_result_with_comparison() and
its Item_func wrapper agg_arg_charsets_for_string_result_with_comparison().
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 3 | ||||
-rw-r--r-- | sql/item.h | 19 | ||||
-rw-r--r-- | sql/item_func.cc | 3 | ||||
-rw-r--r-- | sql/item_func.h | 25 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 7 |
5 files changed, 51 insertions, 6 deletions
diff --git a/sql/item.cc b/sql/item.cc index a1482a6d634..af3917c09c1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1746,7 +1746,8 @@ bool agg_item_collations(DTCollation &c, const char *fname, } /* If all arguments where numbers, reset to @@collation_connection */ - if (c.derivation == DERIVATION_NUMERIC) + if (flags & MY_COLL_ALLOW_NUMERIC_CONV && + c.derivation == DERIVATION_NUMERIC) c.set(Item::default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_NUMERIC); return FALSE; diff --git a/sql/item.h b/sql/item.h index 3e67ffacfc8..96b8c9eb2e4 100644 --- a/sql/item.h +++ b/sql/item.h @@ -53,6 +53,8 @@ char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg) (i.e. constant). MY_COLL_ALLOW_CONV - allow any kind of conversion (combination of the above two) + MY_COLL_ALLOW_NUMERIC_CONV - if all items were numbers, convert to + @@character_set_connection MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE (e.g. when aggregating for comparison) MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV @@ -62,6 +64,7 @@ char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg) #define MY_COLL_ALLOW_SUPERSET_CONV 1 #define MY_COLL_ALLOW_COERCIBLE_CONV 2 #define MY_COLL_DISALLOW_NONE 4 +#define MY_COLL_ALLOW_NUMERIC_CONV 8 #define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV) #define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE) @@ -1561,7 +1564,8 @@ agg_item_charsets_for_string_result(DTCollation &c, const char *name, int item_sep= 1) { uint flags= MY_COLL_ALLOW_SUPERSET_CONV | - MY_COLL_ALLOW_COERCIBLE_CONV; + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_ALLOW_NUMERIC_CONV; return agg_item_charsets(c, name, items, nitems, flags, item_sep); } inline bool @@ -1574,6 +1578,19 @@ agg_item_charsets_for_comparison(DTCollation &c, const char *name, MY_COLL_DISALLOW_NONE; return agg_item_charsets(c, name, items, nitems, flags, item_sep); } +inline bool +agg_item_charsets_for_string_result_with_comparison(DTCollation &c, + const char *name, + Item **items, uint nitems, + int item_sep= 1) +{ + uint flags= MY_COLL_ALLOW_SUPERSET_CONV | + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_ALLOW_NUMERIC_CONV | + MY_COLL_DISALLOW_NONE; + return agg_item_charsets(c, name, items, nitems, flags, item_sep); +} + class Item_num: public Item_basic_constant { diff --git a/sql/item_func.cc b/sql/item_func.cc index 427910c3625..82310365d27 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2537,7 +2537,8 @@ void Item_func_min_max::fix_length_and_dec() } if (cmp_type == STRING_RESULT) { - agg_arg_charsets_for_comparison(collation, args, arg_count); + agg_arg_charsets_for_string_result_with_comparison(collation, + args, arg_count); if (datetime_found) { thd= current_thd; diff --git a/sql/item_func.h b/sql/item_func.h index e60effc352c..9198b092539 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -165,6 +165,11 @@ public: { return agg_item_charsets(c, func_name(), items, nitems, flags, item_sep); } + /* + Aggregate arguments for string result, e.g: CONCAT(a,b) + - convert to @@character_set_connection if all arguments are numbers + - allow DERIVATION_NONE + */ bool agg_arg_charsets_for_string_result(DTCollation &c, Item **items, uint nitems, int item_sep= 1) @@ -172,6 +177,11 @@ public: return agg_item_charsets_for_string_result(c, func_name(), items, nitems, item_sep); } + /* + Aggregate arguments for comparison, e.g: a=b, a LIKE b, a RLIKE b + - don't convert to @@character_set_connection if all arguments are numbers + - don't allow DERIVATION_NONE + */ bool agg_arg_charsets_for_comparison(DTCollation &c, Item **items, uint nitems, int item_sep= 1) @@ -179,6 +189,21 @@ public: return agg_item_charsets_for_comparison(c, func_name(), items, nitems, item_sep); } + /* + Aggregate arguments for string result, when some comparison + is involved internally, e.g: REPLACE(a,b,c) + - convert to @@character_set_connection if all arguments are numbers + - disallow DERIVATION_NONE + */ + bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c, + Item **items, + uint nitems, + int item_sep= 1) + { + return agg_item_charsets_for_string_result_with_comparison(c, func_name(), + items, nitems, + item_sep); + } bool walk(Item_processor processor, bool walk_subquery, uchar *arg); Item *transform(Item_transformer transformer, uchar *arg); Item* compile(Item_analyzer analyzer, uchar **arg_p, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ad18d5dabbe..e5c47c110f4 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1168,7 +1168,7 @@ void Item_func_replace::fix_length_and_dec() char_length+= max_substrs * (uint) diff; } - if (agg_arg_charsets_for_comparison(collation, args, 3)) + if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3)) return; fix_char_length_ulonglong(char_length); } @@ -1458,7 +1458,7 @@ void Item_func_substr::fix_length_and_dec() void Item_func_substr_index::fix_length_and_dec() { - if (agg_arg_charsets_for_comparison(collation, args, 2)) + if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2)) return; fix_char_length(args[0]->max_char_length()); } @@ -1797,7 +1797,8 @@ void Item_func_trim::fix_length_and_dec() { // Handle character set for args[1] and args[0]. // Note that we pass args[1] as the first item, and args[0] as the second. - if (agg_arg_charsets_for_comparison(collation, &args[1], 2, -1)) + if (agg_arg_charsets_for_string_result_with_comparison(collation, + &args[1], 2, -1)) return; } fix_char_length(args[0]->max_char_length()); |