diff options
-rw-r--r-- | mysql-test/r/func_gconcat.result | 5 | ||||
-rw-r--r-- | mysql-test/t/func_gconcat.test | 8 | ||||
-rw-r--r-- | sql/item.h | 8 | ||||
-rw-r--r-- | sql/item_sum.cc | 25 |
4 files changed, 29 insertions, 17 deletions
diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index bc72e04b5a0..b60deae1c80 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -1086,3 +1086,8 @@ ERROR HY000: Row 3 was cut by GROUP_CONCAT() SET group_concat_max_len = DEFAULT; SET @@sql_mode = @old_sql_mode; DROP TABLE t1, t2; +create table t1 (a char(1) character set utf8); +insert into t1 values ('a'),('b'); +select 1 from t1 where a in (select group_concat(a) from t1); +1 +drop table t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index f84c112d303..936b93b49c9 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -795,3 +795,11 @@ INSERT INTO t2 SELECT GROUP_CONCAT(a), b FROM t1 GROUP BY b; SET group_concat_max_len = DEFAULT; SET @@sql_mode = @old_sql_mode; DROP TABLE t1, t2; + +# +# MDEV-3987 uninitialized read in Item_cond::fix_fields leads to crash: select .. where .. in ( select ... ) +# +create table t1 (a char(1) character set utf8); +insert into t1 values ('a'),('b'); +select 1 from t1 where a in (select group_concat(a) from t1); +drop table t1; diff --git a/sql/item.h b/sql/item.h index 42815c34286..0d38c5eb5f8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2,7 +2,7 @@ #define SQL_ITEM_INCLUDED /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009-2011 Monty Program Ab + Copyright (c) 2009, 2013 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -611,7 +611,11 @@ public: @see Query_arena::free_list */ Item *next; - uint32 max_length; /* Maximum length, in bytes */ + /* + The maximum value length in characters multiplied by collation->mbmaxlen. + Almost always it's the maximum value length in bytes. + */ + uint32 max_length; /* TODO: convert name and name_length fields into LEX_STRING to keep them in sync (see bug #11829681/60295 etc). Then also remove some strlen(name) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b04fda55736..e00ef3e592c 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab + Copyright (c) 2008, 2013 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2986,6 +2986,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), { Item_func_group_concat *item= (Item_func_group_concat *) item_arg; TABLE *table= item->table; + uint max_length= table->in_use->variables.group_concat_max_len; String tmp((char *)table->record[1], table->s->reclength, default_charset_info); String tmp2; @@ -3028,7 +3029,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), item->row_count++; /* stop if length of result more than max_length */ - if (result->length() > item->max_length) + if (result->length() > max_length) { int well_formed_error; CHARSET_INFO *cs= item->collation.collation; @@ -3041,7 +3042,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), */ add_length= cs->cset->well_formed_len(cs, ptr + old_length, - ptr + item->max_length, + ptr + max_length, result->length(), &well_formed_error); result->length(old_length + add_length); @@ -3205,19 +3206,11 @@ Field *Item_func_group_concat::make_string_field(TABLE *table) { Field *field; DBUG_ASSERT(collation.collation); - /* - max_characters is maximum number of characters - what can fit into max_length size. It's necessary - to use field size what allows to store group_concat - result without truncation. For this purpose we use - max_characters * CS->mbmaxlen. - */ - const uint32 max_characters= max_length / collation.collation->mbminlen; - if (max_characters > CONVERT_IF_BIGGER_TO_BLOB) - field= new Field_blob(max_characters * collation.collation->mbmaxlen, + if (too_big_for_varchar()) + field= new Field_blob(max_length, maybe_null, name, collation.collation, TRUE); else - field= new Field_varstring(max_characters * collation.collation->mbmaxlen, + field= new Field_varstring(max_length, maybe_null, name, table->s, collation.collation); if (field) @@ -3332,7 +3325,9 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) result.set_charset(collation.collation); result_field= 0; null_value= 1; - max_length= thd->variables.group_concat_max_len; + max_length= thd->variables.group_concat_max_len + / collation.collation->mbminlen + * collation.collation->mbmaxlen; uint32 offset; if (separator->needs_conversion(separator->length(), separator->charset(), |