Skip to content

Commit 72a3d3f

Browse files
committed
Bug#35730982: GROUP_CONCAT results were displayed as NULL before 8.0.23
This is a regression from changes made in the fix for bug#32053547. In this fix, a check for NULL value in group concat evaluation was moved from Item_func_group_concat::setup() to fix_fields(). The change made sense, however when a table was subject to const table elimination, it was no longer optimized early. It would still have been OK, except that the NULL value check in Item_func_group_concat::add() does not cover all possible cases and thus a NULL value in an underlying argument to group_concat might not be detected. The fix is to reintroduce a NULL value check in the setup() function, while still keeping the check in fix_fields(). The flag always_null is replaced by two flags m_null_resolved which is set in fix_fields() and m_null_executed which is set in setup(). The former handles expressions that are always NULL, whereas the latter handles expressions that can be determined to be NULL at start of each execution. The two flags are also moved to the base class Item_sum so that the same optimization may easily be added for other aggregate functions. The flag m_null_executed is reset in Item_sum::cleanup() so that it can be evaluated properly in start of next execution, when used with prepared statements. A specific test case using a prepared statement is added to verify that the flag is properly cleared for subsequent executions. Change-Id: I42967ee5908d5e288854463b6b761a049dc7a7ee
1 parent 4f55249 commit 72a3d3f

File tree

4 files changed

+118
-15
lines changed

4 files changed

+118
-15
lines changed

mysql-test/r/func_gconcat.result

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,3 +1374,43 @@ SET SESSION group_concat_max_len=default;
13741374
# Bug#34703610: Create view with subquery assertion error
13751375
CREATE VIEW v(c0) AS SELECT group_concat((SELECT 1), 1);
13761376
DROP VIEW v;
1377+
# Bug#35730982: GROUP_CONCAT results were displayed as NULL before 8.0.23
1378+
CREATE TABLE customer (
1379+
c_id int NOT NULL,
1380+
c_name varchar(255) NOT NULL,
1381+
PRIMARY KEY (c_id)
1382+
);
1383+
INSERT INTO customer VALUES (1,'Messi'),(2,'Pele'),(3,'Maradona');
1384+
CREATE TABLE employee (
1385+
seq_id bigint NOT NULL,
1386+
c_id int NOT NULL,
1387+
emp_id int NOT NULL,
1388+
emp_name varchar(30) DEFAULT NULL,
1389+
PRIMARY KEY (seq_id)
1390+
);
1391+
INSERT INTO employee VALUES (1,1,10,'Zico'),(2,3,30,'Ronaldo');
1392+
SELECT mc.c_id,
1393+
GROUP_CONCAT(e.emp_name order by e.emp_id separator ',') AS gc
1394+
FROM customer AS mc LEFT JOIN employee AS e
1395+
ON mc.c_id = e.c_id AND e.seq_id = 2
1396+
WHERE mc.c_id = 2
1397+
GROUP BY mc.c_id;
1398+
c_id gc
1399+
2 NULL
1400+
PREPARE ps FROM
1401+
"SELECT mc.c_id,
1402+
GROUP_CONCAT(e.emp_name order by e.emp_id separator ',') AS gc
1403+
FROM customer AS mc LEFT JOIN employee AS e
1404+
ON mc.c_id = e.c_id AND e.seq_id = ?
1405+
WHERE mc.c_id = ?
1406+
GROUP BY mc.c_id";
1407+
SET @seq_id = 2;
1408+
SET @c_id = 2;
1409+
EXECUTE ps USING @seq_id, @c_id;
1410+
c_id gc
1411+
2 NULL
1412+
SET @c_id = 3;
1413+
EXECUTE ps USING @seq_id, @c_id;
1414+
c_id gc
1415+
3 Ronaldo
1416+
DROP TABLE customer, employee;

mysql-test/t/func_gconcat.test

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,3 +1023,49 @@ SET SESSION group_concat_max_len=default;
10231023
CREATE VIEW v(c0) AS SELECT group_concat((SELECT 1), 1);
10241024

10251025
DROP VIEW v;
1026+
1027+
--echo # Bug#35730982: GROUP_CONCAT results were displayed as NULL before 8.0.23
1028+
1029+
CREATE TABLE customer (
1030+
c_id int NOT NULL,
1031+
c_name varchar(255) NOT NULL,
1032+
PRIMARY KEY (c_id)
1033+
);
1034+
1035+
INSERT INTO customer VALUES (1,'Messi'),(2,'Pele'),(3,'Maradona');
1036+
1037+
CREATE TABLE employee (
1038+
seq_id bigint NOT NULL,
1039+
c_id int NOT NULL,
1040+
emp_id int NOT NULL,
1041+
emp_name varchar(30) DEFAULT NULL,
1042+
PRIMARY KEY (seq_id)
1043+
);
1044+
1045+
INSERT INTO employee VALUES (1,1,10,'Zico'),(2,3,30,'Ronaldo');
1046+
1047+
SELECT mc.c_id,
1048+
GROUP_CONCAT(e.emp_name order by e.emp_id separator ',') AS gc
1049+
FROM customer AS mc LEFT JOIN employee AS e
1050+
ON mc.c_id = e.c_id AND e.seq_id = 2
1051+
WHERE mc.c_id = 2
1052+
GROUP BY mc.c_id;
1053+
1054+
PREPARE ps FROM
1055+
"SELECT mc.c_id,
1056+
GROUP_CONCAT(e.emp_name order by e.emp_id separator ',') AS gc
1057+
FROM customer AS mc LEFT JOIN employee AS e
1058+
ON mc.c_id = e.c_id AND e.seq_id = ?
1059+
WHERE mc.c_id = ?
1060+
GROUP BY mc.c_id";
1061+
1062+
SET @seq_id = 2;
1063+
SET @c_id = 2;
1064+
1065+
EXECUTE ps USING @seq_id, @c_id;
1066+
1067+
SET @c_id = 3;
1068+
1069+
EXECUTE ps USING @seq_id, @c_id;
1070+
1071+
DROP TABLE customer, employee;

sql/item_sum.cc

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,9 @@ Item_sum::Item_sum(THD *thd, const Item_sum *item)
440440
base_query_block(item->base_query_block),
441441
aggr_query_block(item->aggr_query_block),
442442
allow_group_via_temp_table(item->allow_group_via_temp_table),
443-
forced_const(item->forced_const) {
443+
forced_const(item->forced_const),
444+
m_null_resolved(item->m_null_resolved),
445+
m_null_executed(item->m_null_executed) {
444446
assert(arg_count == item->arg_count);
445447
with_distinct = item->with_distinct;
446448
if (item->aggr) {
@@ -867,6 +869,8 @@ void Item_sum::cleanup() {
867869
Item_result_field::cleanup();
868870
// forced_const may have been set during optimization, reset it:
869871
forced_const = false;
872+
873+
m_null_executed = false;
870874
}
871875

872876
bool Item_sum::fix_fields(THD *thd, Item **ref [[maybe_unused]]) {
@@ -4227,7 +4231,6 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
42274231
Item_func_group_concat *item)
42284232
: Item_sum(thd, item),
42294233
distinct(item->distinct),
4230-
always_null(item->always_null),
42314234
m_order_arg_count(item->m_order_arg_count),
42324235
m_field_arg_count(item->m_field_arg_count),
42334236
context(item->context),
@@ -4355,17 +4358,19 @@ void Item_func_group_concat::clear() {
43554358
}
43564359

43574360
bool Item_func_group_concat::add() {
4358-
if (always_null) return false;
4361+
if (m_null_executed) return false;
43594362
THD *thd = current_thd;
43604363
if (copy_funcs(tmp_table_param, thd)) return true;
43614364

43624365
for (uint i = 0; i < m_field_arg_count; i++) {
4363-
Item *show_item = args[i];
4364-
if (show_item->const_item()) continue;
4365-
4366-
Field *field = show_item->get_tmp_table_field();
4367-
if (field && field->is_null_in_record((const uchar *)table->record[0]))
4366+
Item *item = args[i];
4367+
if (item->const_for_execution()) {
4368+
continue;
4369+
}
4370+
Field *field = item->get_tmp_table_field();
4371+
if (field && field->is_null_in_record((const uchar *)table->record[0])) {
43684372
return false; // Skip row if it contains null
4373+
}
43694374
}
43704375

43714376
null_value = false;
@@ -4473,7 +4478,7 @@ bool Item_func_group_concat::fix_fields(THD *thd, Item **ref) {
44734478
item->is_null()) {
44744479
// "is_null()" may cause error:
44754480
if (thd->is_error()) return true;
4476-
always_null = true;
4481+
m_null_resolved = true;
44774482
}
44784483
}
44794484

@@ -4483,7 +4488,7 @@ bool Item_func_group_concat::fix_fields(THD *thd, Item **ref) {
44834488
The "fields" list is not used after the call to setup_order(), however it
44844489
must be recreated during optimization to create tmp table columns.
44854490
*/
4486-
if (m_order_arg_count > 0 && !always_null &&
4491+
if (m_order_arg_count > 0 && !m_null_resolved &&
44874492
setup_order(thd, Ref_item_array(args, arg_count), context->table_list,
44884493
&fields, order_array.begin()))
44894494
return true;
@@ -4503,8 +4508,10 @@ bool Item_func_group_concat::setup(THD *thd) {
45034508
*/
45044509
if (table != nullptr || tree != nullptr) return false;
45054510

4506-
// Nothing to set up if always NULL:
4507-
if (always_null) return false;
4511+
// If resolved as NULL, execution is always NULL
4512+
m_null_executed = m_null_resolved;
4513+
// Nothing to set up if value is NULL:
4514+
if (m_null_executed) return false;
45084515

45094516
assert(thd->lex->current_query_block() == aggr_query_block);
45104517

@@ -4535,7 +4542,13 @@ bool Item_func_group_concat::setup(THD *thd) {
45354542

45364543
// First add the fields from the concat field list
45374544
for (uint i = 0; i < m_field_arg_count; i++) {
4538-
fields.push_back(args[i]);
4545+
Item *item = args[i];
4546+
fields.push_back(item);
4547+
if (item->const_for_execution()) {
4548+
if (item->is_null()) m_null_executed = true;
4549+
if (thd->is_error()) return true;
4550+
if (m_null_executed) return false;
4551+
}
45394552
}
45404553
// Then prepend the ordered fields not already in the "fields" list
45414554
for (uint i = 0; i < m_order_arg_count; i++) {

sql/item_sum.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,12 @@ class Item_sum : public Item_func {
505505
The value must be reset to false after execution.
506506
*/
507507
bool forced_const{false};
508+
509+
/// true if the function is resolved to be always NULL
510+
bool m_null_resolved{false};
511+
/// true if the function is determined to be NULL at start of execution
512+
bool m_null_executed{false};
513+
508514
static ulonglong ram_limitation(THD *thd);
509515

510516
public:
@@ -2095,8 +2101,6 @@ class Item_func_group_concat final : public Item_sum {
20952101

20962102
/// True if GROUP CONCAT has the DISTINCT attribute
20972103
bool distinct;
2098-
/// True if the result is always NULL
2099-
bool always_null{false};
21002104
/// The number of ORDER BY items.
21012105
uint m_order_arg_count;
21022106
/// The number of selected items, aka the concat field list

0 commit comments

Comments
 (0)