Skip to content

Commit ee97274

Browse files
committed
DEV-10595 MariaDB daemon leaks memory with specific query
The issue was that in some extreme cases when doing GROUP BY, buffers for temporary blobs where not properly cleared.
1 parent a92a8cc commit ee97274

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

mysql-test/r/group_min_max_innodb.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,19 @@ F 28 28
286286
F 29 29
287287
F 30 30
288288
DROP TABLE t0,t1,t2;
289+
#
290+
# MDEV-MariaDB daemon leaks memory with specific query
291+
#
292+
CREATE TABLE t1 (`voter_id` int(11) unsigned NOT NULL,
293+
`language_id` int(11) unsigned NOT NULL DEFAULT '1'
294+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
295+
CREATE TABLE t2 (`voter_id` int(10) unsigned NOT NULL DEFAULT '0',
296+
`serialized_c` mediumblob) ENGINE=InnoDB DEFAULT CHARSET=utf8;
297+
insert into t2 values (1,repeat("a",1000)),(2,repeat("a",1000)),(3,repeat("b",1000)),(4,repeat("c",1000)),(4,repeat("b",1000));
298+
SELECT GROUP_CONCAT(t1.language_id SEPARATOR ',') AS `translation_resources`, `d`.`serialized_c` FROM t2 AS `d` LEFT JOIN t1 ON `d`.`voter_id` = t1.`voter_id` GROUP BY `d`.`voter_id` ORDER BY 10-d.voter_id+RAND()*0;
299+
translation_resources serialized_c
300+
NULL cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
301+
NULL bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
302+
NULL aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
303+
NULL aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
304+
drop table t1,t2;

mysql-test/t/group_min_max_innodb.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,16 @@ eval EXPLAIN $query;
230230
eval $query;
231231

232232
DROP TABLE t0,t1,t2;
233+
234+
--echo #
235+
--echo # MDEV-MariaDB daemon leaks memory with specific query
236+
--echo #
237+
238+
CREATE TABLE t1 (`voter_id` int(11) unsigned NOT NULL,
239+
`language_id` int(11) unsigned NOT NULL DEFAULT '1'
240+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
241+
CREATE TABLE t2 (`voter_id` int(10) unsigned NOT NULL DEFAULT '0',
242+
`serialized_c` mediumblob) ENGINE=InnoDB DEFAULT CHARSET=utf8;
243+
insert into t2 values (1,repeat("a",1000)),(2,repeat("a",1000)),(3,repeat("b",1000)),(4,repeat("c",1000)),(4,repeat("b",1000));
244+
SELECT GROUP_CONCAT(t1.language_id SEPARATOR ',') AS `translation_resources`, `d`.`serialized_c` FROM t2 AS `d` LEFT JOIN t1 ON `d`.`voter_id` = t1.`voter_id` GROUP BY `d`.`voter_id` ORDER BY 10-d.voter_id+RAND()*0;
245+
drop table t1,t2;

sql/sql_class.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3607,6 +3607,11 @@ class TMP_TABLE_PARAM :public Sql_alloc
36073607
save_copy_field_end= copy_field_end= NULL;
36083608
}
36093609
}
3610+
void free_copy_field_data()
3611+
{
3612+
for (Copy_field *ptr= copy_field ; ptr != copy_field_end ; ptr++)
3613+
ptr->tmp.free();
3614+
}
36103615
};
36113616

36123617
class select_union :public select_result_interceptor

sql/sql_select.cc

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8275,9 +8275,26 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
82758275
We need to destruct the copy_field (allocated in create_tmp_table())
82768276
before setting it to 0 if the join is not "reusable".
82778277
*/
8278-
if (!tmp_join || tmp_join != this)
8279-
tmp_table_param.cleanup();
8280-
tmp_table_param.copy_field= tmp_table_param.copy_field_end=0;
8278+
if (!tmp_join || tmp_join != this)
8279+
tmp_table_param.cleanup();
8280+
else
8281+
{
8282+
/*
8283+
Free data buffered in copy_fields, but keep data pointed by copy_field
8284+
around for next iteration (possibly stored in save_copy_fields).
8285+
8286+
It would be logically simpler to not clear copy_field
8287+
below, but as we have loops that runs over copy_field to
8288+
copy_field_end that should not be done anymore, it's simpler to
8289+
just clear the pointers.
8290+
8291+
Another option would be to just clear copy_field_end and not run
8292+
the loops if this is not set or to have tmp_table_param.cleanup()
8293+
to run cleanup on save_copy_field if copy_field is not set.
8294+
*/
8295+
tmp_table_param.free_copy_field_data();
8296+
tmp_table_param.copy_field= tmp_table_param.copy_field_end=0;
8297+
}
82818298
first_record= sort_and_group=0;
82828299
send_records= (ha_rows) 0;
82838300

@@ -10866,7 +10883,7 @@ void JOIN::join_free()
1086610883
/**
1086710884
Free resources of given join.
1086810885

10869-
@param fill true if we should free all resources, call with full==1
10886+
@param full true if we should free all resources, call with full==1
1087010887
should be last, before it this function can be called with
1087110888
full==0
1087210889

@@ -10982,7 +10999,7 @@ void JOIN::cleanup(bool full)
1098210999
/*
1098311000
If we have tmp_join and 'this' JOIN is not tmp_join and
1098411001
tmp_table_param.copy_field's of them are equal then we have to remove
10985-
pointer to tmp_table_param.copy_field from tmp_join, because it qill
11002+
pointer to tmp_table_param.copy_field from tmp_join, because it will
1098611003
be removed in tmp_table_param.cleanup().
1098711004
*/
1098811005
if (tmp_join &&
@@ -21397,7 +21414,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
2139721414
err:
2139821415
if (copy)
2139921416
delete [] param->copy_field; // This is never 0
21400-
param->copy_field=0;
21417+
param->copy_field= 0;
2140121418
err2:
2140221419
DBUG_RETURN(TRUE);
2140321420
}

0 commit comments

Comments
 (0)