Commit 5f857a7a authored by ram@gw.mysql.r18.ru's avatar ram@gw.mysql.r18.ru

Blob support for the group_concat() function.

(Bug #4315: GROUP_CONCAT with ORDER BY returns strange results for TEXT fields
 Bug #5564: Strange behaviour with group_concat and distinct
 Bug #5970: group_concat doesn't print warnings)
parent b751796a
This diff is collapsed.
...@@ -901,6 +901,8 @@ set group_concat_max_len=10; ...@@ -901,6 +901,8 @@ set group_concat_max_len=10;
select group_concat(a) FROM t1 group by b; select group_concat(a) FROM t1 group by b;
group_concat(a) group_concat(a)
1234567890 1234567890
Warnings:
Warning 1260 1 line(s) were cut by GROUP_CONCAT()
set group_concat_max_len=1024; set group_concat_max_len=1024;
select group_concat(a) FROM t1 group by b; select group_concat(a) FROM t1 group by b;
group_concat(a) group_concat(a)
......
...@@ -240,3 +240,40 @@ SELECT t1.A_ID, GROUP_CONCAT(t2.B_DESC) AS B_DESC FROM t1 LEFT JOIN t2 ON t1.A_I ...@@ -240,3 +240,40 @@ SELECT t1.A_ID, GROUP_CONCAT(t2.B_DESC) AS B_DESC FROM t1 LEFT JOIN t2 ON t1.A_I
DROP TABLE t1; DROP TABLE t1;
DROP TABLE t2; DROP TABLE t2;
#
# blobs
#
create table t1 (a int, b text);
insert into t1 values (1, 'bb'), (1, 'ccc'), (1, 'a'), (1, 'bb'), (1, 'ccc');
insert into t1 values (2, 'BB'), (2, 'CCC'), (2, 'A'), (2, 'BB'), (2, 'CCC');
select group_concat(b) from t1 group by a;
select group_concat(distinct b) from t1 group by a;
select group_concat(b order by b) from t1 group by a;
select group_concat(distinct b order by b) from t1 group by a;
set local group_concat_max_len=4;
select group_concat(b) from t1 group by a;
select group_concat(distinct b) from t1 group by a;
select group_concat(b order by b) from t1 group by a;
select group_concat(distinct b order by b) from t1 group by a;
#
# long blobs
#
insert into t1 values (1, concat(repeat('1', 300), '2')),
(1, concat(repeat('1', 300), '2')), (1, concat(repeat('0', 300), '1')),
(2, concat(repeat('1', 300), '2')), (2, concat(repeat('1', 300), '2')),
(2, concat(repeat('0', 300), '1'));
set local group_concat_max_len=1024;
select group_concat(b) from t1 group by a;
select group_concat(distinct b) from t1 group by a;
select group_concat(b order by b) from t1 group by a;
select group_concat(distinct b order by b) from t1 group by a;
set local group_concat_max_len=400;
select group_concat(b) from t1 group by a;
select group_concat(distinct b) from t1 group by a;
select group_concat(b order by b) from t1 group by a;
select group_concat(distinct b order by b) from t1 group by a;
drop table t1;
...@@ -1921,16 +1921,20 @@ bool Item_func_group_concat::add() ...@@ -1921,16 +1921,20 @@ bool Item_func_group_concat::add()
} }
null_value= FALSE; null_value= FALSE;
TREE_ELEMENT *el= 0; // Only for safety
if (tree_mode) if (tree_mode)
{ el= tree_insert(tree, table->record[0], 0, tree->custom_arg);
if (!tree_insert(tree, table->record[0], 0, tree->custom_arg)) /*
return 1; If the row is not a duplicate (el->count == 1)
} we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
else instead of doing tree traverse later.
{ */
if (result.length() <= group_concat_max_len && !warning_for_row) if (result.length() <= group_concat_max_len &&
dump_leaf_key(table->record[0], 1, this); !warning_for_row &&
} (!tree_mode || (el->count == 1 && distinct && !arg_count_order)))
dump_leaf_key(table->record[0], 1, this);
return 0; return 0;
} }
...@@ -1979,6 +1983,8 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1979,6 +1983,8 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
thd->allow_sum_func= 1; thd->allow_sum_func= 1;
if (!(tmp_table_param= new TMP_TABLE_PARAM)) if (!(tmp_table_param= new TMP_TABLE_PARAM))
return 1; return 1;
/* We'll convert all blobs to varchar fields in the temporary table */
tmp_table_param->convert_blob_length= group_concat_max_len;
tables_list= tables; tables_list= tables;
fixed= 1; fixed= 1;
return 0; return 0;
...@@ -2076,9 +2082,7 @@ bool Item_func_group_concat::setup(THD *thd) ...@@ -2076,9 +2082,7 @@ bool Item_func_group_concat::setup(THD *thd)
} }
else else
{ {
compare_key= NULL; compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct;
if (distinct)
compare_key= (qsort_cmp2) group_concat_key_cmp_with_distinct;
} }
/* /*
Create a tree of sort. Tree is used for a sort and a remove double Create a tree of sort. Tree is used for a sort and a remove double
...@@ -2121,6 +2125,12 @@ String* Item_func_group_concat::val_str(String* str) ...@@ -2121,6 +2125,12 @@ String* Item_func_group_concat::val_str(String* str)
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
if (null_value) if (null_value)
return 0; return 0;
if (count_cut_values && !warning_available)
{
warning_available= TRUE;
warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CUT_VALUE_GROUP_CONCAT, NULL);
}
if (result.length()) if (result.length())
return &result; return &result;
if (tree_mode) if (tree_mode)
...@@ -2128,12 +2138,6 @@ String* Item_func_group_concat::val_str(String* str) ...@@ -2128,12 +2138,6 @@ String* Item_func_group_concat::val_str(String* str)
tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this, tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
left_root_right); left_root_right);
} }
if (count_cut_values && !warning_available)
{
warning_available= TRUE;
warning= push_warning(item_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CUT_VALUE_GROUP_CONCAT, NULL);
}
return &result; return &result;
} }
......
...@@ -521,7 +521,7 @@ int mysql_union(THD *thd, LEX *lex, select_result *result, ...@@ -521,7 +521,7 @@ int mysql_union(THD *thd, LEX *lex, select_result *result,
int mysql_handle_derived(LEX *lex); int mysql_handle_derived(LEX *lex);
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item ***copy_func, Field **from_field, Item ***copy_func, Field **from_field,
bool group,bool modify_item); bool group, bool modify_item, uint convert_blob_length);
int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
List<create_field> &fields, List<create_field> &fields,
List<Key> &keys, uint &db_options, List<Key> &keys, uint &db_options,
......
...@@ -1218,10 +1218,12 @@ class TMP_TABLE_PARAM :public Sql_alloc ...@@ -1218,10 +1218,12 @@ class TMP_TABLE_PARAM :public Sql_alloc
uint group_parts,group_length,group_null_parts; uint group_parts,group_length,group_null_parts;
uint quick_group; uint quick_group;
bool using_indirect_summary_function; bool using_indirect_summary_function;
/* If >0 convert all blob fields to varchar(convert_blob_length) */
uint convert_blob_length;
TMP_TABLE_PARAM() TMP_TABLE_PARAM()
:copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0),
group_length(0), group_null_parts(0) group_length(0), group_null_parts(0), convert_blob_length(0)
{} {}
~TMP_TABLE_PARAM() ~TMP_TABLE_PARAM()
{ {
......
...@@ -4583,21 +4583,28 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) ...@@ -4583,21 +4583,28 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
the record in the original table. the record in the original table.
If modify_item is 0 then fill_record() will update If modify_item is 0 then fill_record() will update
the temporary table the temporary table
convert_blob_length If >0 create a varstring(convert_blob_length) field
instead of blob.
RETURN RETURN
0 on error 0 on error
new_created field new_created field
*/ */
static Field* create_tmp_field_from_field(THD *thd,
Field* org_field, static Field* create_tmp_field_from_field(THD *thd, Field* org_field,
Item *item, Item *item, TABLE *table,
TABLE *table, bool modify_item,
bool modify_item) uint convert_blob_length)
{ {
Field *new_field; Field *new_field;
// The following should always be true if (convert_blob_length && org_field->flags & BLOB_FLAG)
if ((new_field= org_field->new_field(&thd->mem_root,table))) new_field= new Field_varstring(convert_blob_length, org_field->maybe_null(),
org_field->field_name, table,
org_field->charset());
else
new_field= org_field->new_field(&thd->mem_root, table);
if (new_field)
{ {
if (modify_item) if (modify_item)
((Item_field *)item)->result_field= new_field; ((Item_field *)item)->result_field= new_field;
...@@ -4628,16 +4635,16 @@ static Field* create_tmp_field_from_field(THD *thd, ...@@ -4628,16 +4635,16 @@ static Field* create_tmp_field_from_field(THD *thd,
the record in the original table. the record in the original table.
If modify_item is 0 then fill_record() will update If modify_item is 0 then fill_record() will update
the temporary table the temporary table
convert_blob_length If >0 create a varstring(convert_blob_length) field
instead of blob.
RETURN RETURN
0 on error 0 on error
new_created field new_created field
*/ */
static Field* create_tmp_field_from_item(THD *thd, static Field* create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
Item *item, Item ***copy_func, bool modify_item,
TABLE *table, uint convert_blob_length)
Item ***copy_func,
bool modify_item)
{ {
bool maybe_null=item->maybe_null; bool maybe_null=item->maybe_null;
Field *new_field; Field *new_field;
...@@ -4654,13 +4661,18 @@ static Field* create_tmp_field_from_item(THD *thd, ...@@ -4654,13 +4661,18 @@ static Field* create_tmp_field_from_item(THD *thd,
break; break;
case STRING_RESULT: case STRING_RESULT:
if (item->max_length > 255) if (item->max_length > 255)
new_field= new Field_blob(item->max_length, maybe_null, {
item->name, table, if (convert_blob_length)
item->collation.collation); new_field= new Field_varstring(convert_blob_length, maybe_null,
item->name, table,
item->collation.collation);
else
new_field= new Field_blob(item->max_length, maybe_null, item->name,
table, item->collation.collation);
}
else else
new_field= new Field_string(item->max_length, maybe_null, new_field= new Field_string(item->max_length, maybe_null, item->name,
item->name, table, table, item->collation.collation);
item->collation.collation);
break; break;
case ROW_RESULT: case ROW_RESULT:
default: default:
...@@ -4697,6 +4709,8 @@ static Field* create_tmp_field_from_item(THD *thd, ...@@ -4697,6 +4709,8 @@ static Field* create_tmp_field_from_item(THD *thd,
the record in the original table. the record in the original table.
If modify_item is 0 then fill_record() will update If modify_item is 0 then fill_record() will update
the temporary table the temporary table
convert_blob_length If >0 create a varstring(convert_blob_length) field
instead of blob.
RETURN RETURN
0 on error 0 on error
...@@ -4704,8 +4718,8 @@ static Field* create_tmp_field_from_item(THD *thd, ...@@ -4704,8 +4718,8 @@ static Field* create_tmp_field_from_item(THD *thd,
*/ */
Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item ***copy_func, Field **from_field, Item ***copy_func, Field **from_field,
bool group, bool modify_item) bool group, bool modify_item, uint convert_blob_length)
{ {
switch (type) { switch (type) {
case Item::SUM_FUNC_ITEM: case Item::SUM_FUNC_ITEM:
...@@ -4740,8 +4754,15 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, ...@@ -4740,8 +4754,15 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
item->name,table,item->unsigned_flag); item->name,table,item->unsigned_flag);
case STRING_RESULT: case STRING_RESULT:
if (item_sum->max_length > 255) if (item_sum->max_length > 255)
return new Field_blob(item_sum->max_length,maybe_null, {
item->name,table,item->collation.collation); if (convert_blob_length)
return new Field_varstring(convert_blob_length, maybe_null,
item->name, table,
item->collation.collation);
else
return new Field_blob(item_sum->max_length, maybe_null, item->name,
table, item->collation.collation);
}
return new Field_string(item_sum->max_length,maybe_null, return new Field_string(item_sum->max_length,maybe_null,
item->name,table,item->collation.collation); item->name,table,item->collation.collation);
case ROW_RESULT: case ROW_RESULT:
...@@ -4758,8 +4779,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, ...@@ -4758,8 +4779,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
case Item::DEFAULT_VALUE_ITEM: case Item::DEFAULT_VALUE_ITEM:
{ {
Item_field *field= (Item_field*) item; Item_field *field= (Item_field*) item;
return create_tmp_field_from_field(thd, (*from_field= field->field), return create_tmp_field_from_field(thd, (*from_field= field->field), item,
item, table, modify_item); table, modify_item, convert_blob_length);
} }
case Item::FUNC_ITEM: case Item::FUNC_ITEM:
case Item::COND_ITEM: case Item::COND_ITEM:
...@@ -4774,14 +4795,16 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, ...@@ -4774,14 +4795,16 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
case Item::REF_ITEM: case Item::REF_ITEM:
case Item::NULL_ITEM: case Item::NULL_ITEM:
case Item::VARBIN_ITEM: case Item::VARBIN_ITEM:
return create_tmp_field_from_item(thd, item, table, return create_tmp_field_from_item(thd, item, table, copy_func, modify_item,
copy_func, modify_item); convert_blob_length);
case Item::TYPE_HOLDER: case Item::TYPE_HOLDER:
{ {
Field *example= ((Item_type_holder *)item)->example(); Field *example= ((Item_type_holder *)item)->example();
if (example) if (example)
return create_tmp_field_from_field(thd, example, item, table, 0); return create_tmp_field_from_field(thd, example, item, table, 0,
return create_tmp_field_from_item(thd, item, table, copy_func, 0); convert_blob_length);
return create_tmp_field_from_item(thd, item, table, copy_func, 0,
convert_blob_length);
} }
default: // Dosen't have to be stored default: // Dosen't have to be stored
return 0; return 0;
...@@ -4944,8 +4967,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -4944,8 +4967,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (!arg->const_item()) if (!arg->const_item())
{ {
Field *new_field= Field *new_field=
create_tmp_field(thd, table,arg,arg->type(),&copy_func, create_tmp_field(thd, table, arg, arg->type(), &copy_func,
tmp_from_field, group != 0,not_all_columns); tmp_from_field, group != 0,not_all_columns,
param->convert_blob_length);
if (!new_field) if (!new_field)
goto err; // Should be OOM goto err; // Should be OOM
tmp_from_field++; tmp_from_field++;
...@@ -4981,9 +5005,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ...@@ -4981,9 +5005,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
We here distinguish between UNION and multi-table-updates by the fact We here distinguish between UNION and multi-table-updates by the fact
that in the later case group is set to the row pointer. that in the later case group is set to the row pointer.
*/ */
Field *new_field=create_tmp_field(thd, table, item,type, &copy_func, Field *new_field= create_tmp_field(thd, table, item, type, &copy_func,
tmp_from_field, group != 0, tmp_from_field, group != 0,
not_all_columns || group !=0); not_all_columns || group !=0,
param->convert_blob_length);
if (!new_field) if (!new_field)
{ {
if (thd->is_fatal_error) if (thd->is_fatal_error)
......
...@@ -1378,7 +1378,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -1378,7 +1378,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
field=item->tmp_table_field(&tmp_table); field=item->tmp_table_field(&tmp_table);
else else
field=create_tmp_field(thd, &tmp_table, item, item->type(), field=create_tmp_field(thd, &tmp_table, item, item->type(),
(Item ***) 0, &tmp_field,0,0); (Item ***) 0, &tmp_field, 0, 0, 0);
if (!field || if (!field ||
!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
((Item_field *)item)->field : ((Item_field *)item)->field :
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment