Skip to content

Commit 665045f

Browse files
committed
Fixed bug mdev-11081.
The idea of this fix was taken from the patch by Roy Lyseng for mysql-5.6 bug iBug#14740889: "Wrong result for aggregate functions when executing query through cursor". Here's Roy's comment for his patch: " The problem was that a grouped query did not behave properly when executed using a cursor. On further inspection, the query used one intermediate temporary table for the grouping. Then, Select_materialize::send_result_set_metadata created a temporary table for storing the query result. Notice that get_unit_column_types() is used to retrieve column meta-data for the query. The items contained in this list are later modified so that their result_field points to the row buffer of the materialized temporary table for the cursor. But prior to this, these result_field objects have been prepared for use in the grouping operation, by JOIN::make_tmp_tables_info(), hence the grouping operation operates on wrong column buffers. The problem is solved by using the list JOIN::fields when copying data to the materialized table. This list is set by JOIN::make_tmp_tables_info() and points to the columns of the last intermediate temporary table of the executed query. For a UNION, it points to the temporary table that is the result of the UNION query. Notice that we have to assign a value to ::fields early in JOIN::optimize() in case the optimization shortcuts due to a const plan detection. A more optimal solution might be to avoid creating the final temporary table when the query result is already stored in a temporary table. " The patch does not contain a test case, but the description of the problem corresponds exactly what could be observed in the test case for mdev-11081.
1 parent 022aeda commit 665045f

File tree

8 files changed

+92
-10
lines changed

8 files changed

+92
-10
lines changed

mysql-test/r/sp.result

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8001,3 +8001,42 @@ return 1;
80018001
end |
80028002
ERROR 0A000: Not allowed to return a result set from a function
80038003
drop table t1,t2;
8004+
#
8005+
# MDEV-11081: CURSOR for query with GROUP BY
8006+
#
8007+
CREATE TABLE t1 (name VARCHAR(10), value INT);
8008+
INSERT INTO t1 VALUES ('b',1);
8009+
INSERT INTO t1 VALUES ('b',1);
8010+
INSERT INTO t1 VALUES ('c',1);
8011+
INSERT INTO t1 VALUES ('a',1);
8012+
INSERT INTO t1 VALUES ('a',1);
8013+
INSERT INTO t1 VALUES ('a',1);
8014+
CREATE PROCEDURE p1 ()
8015+
BEGIN
8016+
DECLARE done INT DEFAULT FALSE;
8017+
DECLARE v_name VARCHAR(10);
8018+
DECLARE v_total INT;
8019+
DECLARE c CURSOR FOR
8020+
SELECT name, SUM(value) AS total FROM t1 GROUP BY name;
8021+
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
8022+
OPEN c;
8023+
read_loop:
8024+
LOOP
8025+
FETCH c INTO v_name, v_total;
8026+
IF done THEN
8027+
LEAVE read_loop;
8028+
END IF;
8029+
SELECT v_name, v_total;
8030+
END LOOP;
8031+
CLOSE c;
8032+
END;
8033+
|
8034+
CALL p1();
8035+
v_name v_total
8036+
a 3
8037+
v_name v_total
8038+
b 2
8039+
v_name v_total
8040+
c 1
8041+
DROP PROCEDURE p1;
8042+
DROP TABLE t1;

mysql-test/t/sp.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9446,3 +9446,41 @@ end |
94469446
--delimiter ;
94479447

94489448
drop table t1,t2;
9449+
9450+
--echo #
9451+
--echo # MDEV-11081: CURSOR for query with GROUP BY
9452+
--echo #
9453+
9454+
CREATE TABLE t1 (name VARCHAR(10), value INT);
9455+
INSERT INTO t1 VALUES ('b',1);
9456+
INSERT INTO t1 VALUES ('b',1);
9457+
INSERT INTO t1 VALUES ('c',1);
9458+
INSERT INTO t1 VALUES ('a',1);
9459+
INSERT INTO t1 VALUES ('a',1);
9460+
INSERT INTO t1 VALUES ('a',1);
9461+
DELIMITER |;
9462+
CREATE PROCEDURE p1 ()
9463+
BEGIN
9464+
DECLARE done INT DEFAULT FALSE;
9465+
DECLARE v_name VARCHAR(10);
9466+
DECLARE v_total INT;
9467+
DECLARE c CURSOR FOR
9468+
SELECT name, SUM(value) AS total FROM t1 GROUP BY name;
9469+
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
9470+
OPEN c;
9471+
read_loop:
9472+
LOOP
9473+
FETCH c INTO v_name, v_total;
9474+
IF done THEN
9475+
LEAVE read_loop;
9476+
END IF;
9477+
SELECT v_name, v_total;
9478+
END LOOP;
9479+
CLOSE c;
9480+
END;
9481+
|
9482+
DELIMITER ;|
9483+
CALL p1();
9484+
DROP PROCEDURE p1;
9485+
DROP TABLE t1;
9486+

sql/item_subselect.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4572,7 +4572,7 @@ subselect_hash_sj_engine::get_strategy_using_schema()
45724572
return COMPLETE_MATCH;
45734573
else
45744574
{
4575-
List_iterator<Item> inner_col_it(*item_in->unit->get_unit_column_types());
4575+
List_iterator<Item> inner_col_it(*item_in->unit->get_column_types(false));
45764576
Item *outer_col, *inner_col;
45774577

45784578
for (uint i= 0; i < item_in->left_expr->cols(); i++)

sql/sql_class.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4392,7 +4392,7 @@ class select_result :public select_result_sink
43924392
/*
43934393
All descendant classes have their send_data() skip the first
43944394
unit->offset_limit_cnt rows sent. Select_materialize
4395-
also uses unit->get_unit_column_types().
4395+
also uses unit->get_column_types().
43964396
*/
43974397
SELECT_LEX_UNIT *unit;
43984398
/* Something used only by the parser: */

sql/sql_cursor.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ void Materialized_cursor::on_table_fill_finished()
433433
bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
434434
{
435435
DBUG_ASSERT(table == 0);
436-
if (create_result_table(unit->thd, unit->get_unit_column_types(),
436+
if (create_result_table(unit->thd, unit->get_column_types(true),
437437
FALSE,
438438
thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
439439
"", FALSE, TRUE, TRUE))

sql/sql_lex.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ class st_select_lex_unit: public st_select_lex_node {
732732
friend void lex_start(THD *thd);
733733
friend int subselect_union_engine::exec();
734734

735-
List<Item> *get_unit_column_types();
735+
List<Item> *get_column_types(bool for_cursor);
736736

737737
select_union *get_union_result() { return union_result; }
738738
int save_union_explain(Explain_query *output);

sql/sql_select.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,9 +1121,6 @@ int JOIN::optimize()
11211121
int
11221122
JOIN::optimize_inner()
11231123
{
1124-
/*
1125-
if (conds) { Item *it_clone= conds->build_clone(thd,thd->mem_root); }
1126-
*/
11271124
ulonglong select_opts_for_readinfo;
11281125
uint no_jbuf_after;
11291126
JOIN_TAB *tab;
@@ -1137,6 +1134,12 @@ JOIN::optimize_inner()
11371134
set_allowed_join_cache_types();
11381135
need_distinct= TRUE;
11391136

1137+
/*
1138+
Needed in case optimizer short-cuts,
1139+
set properly in make_tmp_tables_info()
1140+
*/
1141+
fields= &select_lex->item_list;
1142+
11401143
if (select_lex->first_cond_optimization)
11411144
{
11421145
//Do it only for the first execution

sql/sql_union.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,9 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
13821382
Get column type information for this unit.
13831383
13841384
SYNOPSIS
1385-
st_select_lex_unit::get_unit_column_types()
1385+
st_select_lex_unit::get_column_types()
1386+
@param for_cursor if true return the list the fields
1387+
retrieved by the cursor
13861388
13871389
DESCRIPTION
13881390
For a single-select the column types are taken
@@ -1396,7 +1398,7 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
13961398
st_select_lex_unit::prepare()
13971399
*/
13981400

1399-
List<Item> *st_select_lex_unit::get_unit_column_types()
1401+
List<Item> *st_select_lex_unit::get_column_types(bool for_cursor)
14001402
{
14011403
SELECT_LEX *sl= first_select();
14021404
bool is_procedure= MY_TEST(sl->join->procedure);
@@ -1416,7 +1418,7 @@ List<Item> *st_select_lex_unit::get_unit_column_types()
14161418
return &types;
14171419
}
14181420

1419-
return &sl->item_list;
1421+
return for_cursor ? sl->join->fields : &sl->item_list;
14201422
}
14211423

14221424

0 commit comments

Comments
 (0)