3184 Alexander Barkov 2009-04-07
Bug#34021 Character sets: crash if concatenate utf32 and number
Problem:
When inserting a CONCAT between an UTF32 string
and a number into an UTF32 column, mysqld could crash.
That happened was:
- Item_num::safe_charset_converter() returned wrong length,
which is not divisible by mbminlen=4.
- As a result, Item_func_concat() returned wrong length.
- Then this call stack happened:
Field_string::store() ->
well_formed_copy_nchars() ->
to_cs->cset->well_formed_len() ->
my_well_formed_len_utf32()
- The last call crashed on assert:
DBUG_ASSERT((length % 4) == 0);
Fix:
Item_num_safe_charset_converter() now adds leading
zeros if str_val() returned a string with length not
divisible by mbminlen.
modified:
mysql-test/r/ctype_utf32.result
mysql-test/t/ctype_utf32.test
sql/item.cc
modified:
mysql-test/r/ctype_utf32.result
mysql-test/t/ctype_utf32.test
sql/item.cc
3183 Alexander Barkov 2009-04-06
#
# Bug#24690 Stored functions: RETURNing UTF8 strings
# do not return UTF8_UNICODE_CI collation
#
mysql-test/r/sp-ucs2.result:
mysql-test/t/sp-ucs2.test:
Adding tests
sql/mysql_priv.h:
Adding prototype
sql/sp.cc
Remember COLLATE clause for non-default collations
sql/sql_parse.cc
Adding new helper function
sql/sql_yacc.yy
- Allow "CHARACTER SET cs COLLATE cl" in
SP parameters, RETURNS, DECLARE
- Minor reorganization for "ASCII" and "UNICODE"
related rules, to make the code more readable,
also to allow these aliases:
* "VARCHAR(10) ASCII BINARY" -> CHARACTER SET latin1 COLLATE latin1_bin
* "VARCHAR(10) BINARY ASCII" -> CHARACTER SET latin1 COLLATE latin1_bin
* "VARCHAR(10) UNICODE BINARY" -> CHARACTER SET ucs2 COLLATE ucs2_bin
* "VARCHAR(10) BINARY UNICODE" -> CHARACTER SET ucs2 COLLATE ucs2_bin
Previously these four aliases returned the error
"This version of MySQL does not yet support return value collation".
Note:
This patch allows "VARCHAR(10) CHARACTER SET cs COLLATE cl"
and the above four aliases.
"VARCHAR(10) COLLATE cl" is still not allowed
i.e. when COLLATE is given without CHARACTER SET.
If we want to support this, we need an architecture decision
which character set to use by default.
modified:
mysql-test/r/sp-ucs2.result
mysql-test/t/sp-ucs2.test
sql/mysql_priv.h
sql/sp.cc
sql/sql_parse.cc
sql/sql_yacc.yy
=== modified file 'mysql-test/r/ctype_utf32.result'
--- a/mysql-test/r/ctype_utf32.result 2008-07-24 12:15:24 +0000
+++ b/mysql-test/r/ctype_utf32.result 2009-04-07 06:01:03 +0000
@@ -1411,3 +1411,40 @@ hex(weight_string(s1, 4, 2, 0))
DROP TABLE t1;
SET max_sort_length=DEFAULT;
SET NAMES latin1;
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 1) from t1;
+select hex(s1) from t1;
+hex(s1)
+00000061
+0000006100000031
+drop table t1;
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 0.1) from t1;
+Warnings:
+Warning 1366 Incorrect string value: '\x00\x30\x2E\x31' for column 's1' at row 1
+select hex(s1) from t1;
+hex(s1)
+00000061
+00000061
+drop table t1;
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 1e-1) from t1;
+Warnings:
+Warning 1366 Incorrect string value: '\x00\x30\x2E\x31' for column 's1' at row 1
+select hex(s1) from t1;
+hex(s1)
+00000061
+00000061
+drop table t1;
=== modified file 'mysql-test/t/ctype_utf32.test'
--- a/mysql-test/t/ctype_utf32.test 2008-07-24 12:15:24 +0000
+++ b/mysql-test/t/ctype_utf32.test 2009-04-07 06:01:03 +0000
@@ -788,3 +788,32 @@ SELECT hex(weight_string(s1, 4, 2, 0)) F
DROP TABLE t1;
SET max_sort_length=DEFAULT;
SET NAMES latin1;
+
+#
+# Bug#34021 Character sets: crash if concatenate utf32 and number
+#
+
+# Test concat with INT
+drop table if exists t1;
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 1) from t1;
+select hex(s1) from t1;
+drop table t1;
+
+# Test concat with DECIMAL (warning is expected)
+drop table if exists t1;
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 0.1) from t1;
+select hex(s1) from t1;
+drop table t1;
+
+# Test concat with FLOAT (warning is expected)
+drop table if exists t1;
+create table t1 (s1 char(5) character set utf32);
+insert into t1 values ('a');
+insert into t1 select concat(s1, 1e-1) from t1;
+select hex(s1) from t1;
+drop table t1;
+
=== modified file 'sql/item.cc'
--- a/sql/item.cc 2009-04-01 11:13:40 +0000
+++ b/sql/item.cc 2009-04-07 06:01:03 +0000
@@ -784,14 +784,46 @@ Item *Item::safe_charset_converter(CHARS
We cannot use generic Item::safe_charset_converter(), because
the latter returns a non-fixed Item, so val_str() crashes afterwards.
Override Item_num method, to return a fixed item.
+
+ If Item_num's length is not divisible to mbmaxlen,
+ then value is left padded with zero bytes 0x00.
*/
Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
{
+ const size_t max_mbminlen= 4;
Item_string *conv;
- char buf[64];
- String *s, tmp(buf, sizeof(buf), &my_charset_bin);
- s= val_str(&tmp);
- if ((conv= new Item_string(s->ptr(), s->length(), s->charset())))
+ char buf[64 + max_mbminlen];
+ String tmp(buf + max_mbminlen, sizeof(buf) - max_mbminlen, &my_charset_bin);
+ String *s= val_str(&tmp);
+ uint32 nzeros= tocs->mbminlen - (s->length() % tocs->mbminlen);
+
+ if (nzeros)
+ {
+ /*
+ Need to left-pad some 0x00 bytes to return correct length.
+ Make sure that we reserved enough space for the leading 0x00 bytes,
+ then clear the leading bytes:
+ Note, int4store() should be a little bit faster than memset(buf,4).
+ */
+ DBUG_ASSERT(tocs->mbminlen <= max_mbminlen);
+ int4store(buf, 0);
+
+ /*
+ Make sure val_str returns pointer to "buf",
+ (otherwise padding won't work).
+ That should be always true, according to
+ Item_num::val_str() implementation.
+ */
+ DBUG_ASSERT(s->ptr() == ((const char*) &buf) + 4);
+ }
+
+
+ /*
+ TODO for 6.1: remove this trick with zeros
+ when WL#2649 "Number-to-string conversions" is done.
+ */
+ if ((conv= new Item_string(s->ptr() - nzeros,
+ s->length() + nzeros, s->charset())))
{
conv->str_value.copy();
conv->str_value.mark_as_const();
Attachment: [text/bzr-bundle] bzr/bar@mysql.com-20090407060103-ghb9vaj2dbf996kv.bundle
| Thread |
|---|
| • bzr push into mysql-6.0-bugteam branch (bar:3183 to 3184) Bug#34021 | Alexander Barkov | 7 Apr |