Commit 2b734bbe authored by Martin Hansson's avatar Martin Hansson

Bug#41660: Sort-index_merge for non-first join table may

require O(#scans) memory

When an index merge operation was restarted, it would
re-allocate the Unique object controlling the duplicate row
ID elimination. Fixed by making the Unique object a member
of QUICK_INDEX_MERGE_SELECT and thus reusing it throughout
the lifetime of this object.
parent f4420a3b
...@@ -48,5 +48,40 @@ Got one of the listed errors ...@@ -48,5 +48,40 @@ Got one of the listed errors
SET SESSION debug=DEFAULT; SET SESSION debug=DEFAULT;
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#41660: Sort-index_merge for non-first join table may require
# O(#scans) memory
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
CREATE TABLE t2 (a INT, b INT, filler CHAR(100), KEY(a), KEY(b));
INSERT INTO t2 SELECT 1000, 1000, 'filler' FROM t1 A, t1 B, t1 C;
INSERT INTO t2 VALUES (1, 1, 'data');
# the example query uses LEFT JOIN only for the sake of being able to
# demonstrate the issue with a very small dataset. (left outer join
# disables the use of join buffering, so we get the second table
# re-scanned for every record in the outer table. if we used inner join,
# we would need to have thousands of records and/or more columns in both
# tables so that the join buffer is filled and re-scans are triggered).
SET SESSION debug = '+d,only_one_Unique_may_be_created';
EXPLAIN
SELECT * FROM t1 LEFT JOIN t2 ON ( t2.a < 10 OR t2.b < 10 );
id select_type table type possible_keys key key_len ref rows Extra
x x x x x x x x x
x x x x x x x x x Using sort_union(a,b); Using where
SELECT * FROM t1 LEFT JOIN t2 ON ( t2.a < 10 OR t2.b < 10 );
a a b filler
0 1 1 data
1 1 1 data
2 1 1 data
3 1 1 data
4 1 1 data
5 1 1 data
6 1 1 data
7 1 1 data
8 1 1 data
9 1 1 data
SET SESSION debug = DEFAULT;
DROP TABLE t1, t2;
#
# End of 5.1 tests # End of 5.1 tests
# #
...@@ -45,6 +45,35 @@ SHOW CREATE TABLE t1; ...@@ -45,6 +45,35 @@ SHOW CREATE TABLE t1;
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
-- echo #
-- echo # Bug#41660: Sort-index_merge for non-first join table may require
-- echo # O(#scans) memory
-- echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
CREATE TABLE t2 (a INT, b INT, filler CHAR(100), KEY(a), KEY(b));
INSERT INTO t2 SELECT 1000, 1000, 'filler' FROM t1 A, t1 B, t1 C;
INSERT INTO t2 VALUES (1, 1, 'data');
--echo # the example query uses LEFT JOIN only for the sake of being able to
--echo # demonstrate the issue with a very small dataset. (left outer join
--echo # disables the use of join buffering, so we get the second table
--echo # re-scanned for every record in the outer table. if we used inner join,
--echo # we would need to have thousands of records and/or more columns in both
--echo # tables so that the join buffer is filled and re-scans are triggered).
SET SESSION debug = '+d,only_one_Unique_may_be_created';
--replace_column 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x
EXPLAIN
SELECT * FROM t1 LEFT JOIN t2 ON ( t2.a < 10 OR t2.b < 10 );
SELECT * FROM t1 LEFT JOIN t2 ON ( t2.a < 10 OR t2.b < 10 );
SET SESSION debug = DEFAULT;
DROP TABLE t1, t2;
--echo # --echo #
--echo # Bug#42064: low memory crash when importing hex strings, in Item_hex_string::Item_hex_string --echo # Bug#42064: low memory crash when importing hex strings, in Item_hex_string::Item_hex_string
......
...@@ -1194,7 +1194,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() ...@@ -1194,7 +1194,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param,
TABLE *table) TABLE *table)
:pk_quick_select(NULL), thd(thd_param) :unique(NULL), pk_quick_select(NULL), thd(thd_param)
{ {
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT"); DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT");
index= MAX_KEY; index= MAX_KEY;
...@@ -1236,6 +1236,7 @@ QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT() ...@@ -1236,6 +1236,7 @@ QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects); List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects);
QUICK_RANGE_SELECT* quick; QUICK_RANGE_SELECT* quick;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT"); DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT");
delete unique;
quick_it.rewind(); quick_it.rewind();
while ((quick= quick_it++)) while ((quick= quick_it++))
quick->file= NULL; quick->file= NULL;
...@@ -8153,7 +8154,6 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8153,7 +8154,6 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it(quick_selects); List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it(quick_selects);
QUICK_RANGE_SELECT* cur_quick; QUICK_RANGE_SELECT* cur_quick;
int result; int result;
Unique *unique;
handler *file= head->file; handler *file= head->file;
DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::read_keys_and_merge"); DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::read_keys_and_merge");
...@@ -8172,9 +8172,22 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8172,9 +8172,22 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
if (cur_quick->init() || cur_quick->reset()) if (cur_quick->init() || cur_quick->reset())
DBUG_RETURN(1); DBUG_RETURN(1);
unique= new Unique(refpos_order_cmp, (void *)file, if (unique == NULL)
file->ref_length, {
thd->variables.sortbuff_size); DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", abort(); );
DBUG_EXECUTE_IF("only_one_Unique_may_be_created",
DBUG_SET("+d,index_merge_may_not_create_a_Unique"); );
unique= new Unique(refpos_order_cmp, (void *)file,
file->ref_length,
thd->variables.sortbuff_size);
}
else
unique->reset();
DBUG_ASSERT(file->ref_length == unique->get_size());
DBUG_ASSERT(thd->variables.sortbuff_size == unique->get_max_in_memory_size());
if (!unique) if (!unique)
DBUG_RETURN(1); DBUG_RETURN(1);
for (;;) for (;;)
...@@ -8189,10 +8202,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8189,10 +8202,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
if (cur_quick->file->inited != handler::NONE) if (cur_quick->file->inited != handler::NONE)
cur_quick->file->ha_index_end(); cur_quick->file->ha_index_end();
if (cur_quick->init() || cur_quick->reset()) if (cur_quick->init() || cur_quick->reset())
{
delete unique;
DBUG_RETURN(1); DBUG_RETURN(1);
}
} }
if (result) if (result)
...@@ -8200,17 +8210,13 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8200,17 +8210,13 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
if (result != HA_ERR_END_OF_FILE) if (result != HA_ERR_END_OF_FILE)
{ {
cur_quick->range_end(); cur_quick->range_end();
delete unique;
DBUG_RETURN(result); DBUG_RETURN(result);
} }
break; break;
} }
if (thd->killed) if (thd->killed)
{
delete unique;
DBUG_RETURN(1); DBUG_RETURN(1);
}
/* skip row if it will be retrieved by clustered PK scan */ /* skip row if it will be retrieved by clustered PK scan */
if (pk_quick_select && pk_quick_select->row_in_ranges()) if (pk_quick_select && pk_quick_select->row_in_ranges())
...@@ -8219,10 +8225,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8219,10 +8225,7 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
cur_quick->file->position(cur_quick->record); cur_quick->file->position(cur_quick->record);
result= unique->unique_add((char*)cur_quick->file->ref); result= unique->unique_add((char*)cur_quick->file->ref);
if (result) if (result)
{
delete unique;
DBUG_RETURN(1); DBUG_RETURN(1);
}
} }
/* /*
...@@ -8231,7 +8234,6 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() ...@@ -8231,7 +8234,6 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge()
sequence. sequence.
*/ */
result= unique->get(head); result= unique->get(head);
delete unique;
doing_pk_scan= FALSE; doing_pk_scan= FALSE;
/* index_merge currently doesn't support "using index" at all */ /* index_merge currently doesn't support "using index" at all */
head->set_keyread(FALSE); head->set_keyread(FALSE);
...@@ -10277,7 +10279,7 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg, ...@@ -10277,7 +10279,7 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
uint use_index, double read_cost_arg, uint use_index, double read_cost_arg,
ha_rows records_arg, uint key_infix_len_arg, ha_rows records_arg, uint key_infix_len_arg,
uchar *key_infix_arg, MEM_ROOT *parent_alloc) uchar *key_infix_arg, MEM_ROOT *parent_alloc)
:join(join_arg), index_info(index_info_arg), :file(table->file), join(join_arg), index_info(index_info_arg),
group_prefix_len(group_prefix_len_arg), group_prefix_len(group_prefix_len_arg),
group_key_parts(group_key_parts_arg), have_min(have_min_arg), group_key_parts(group_key_parts_arg), have_min(have_min_arg),
have_max(have_max_arg), seen_first_key(FALSE), have_max(have_max_arg), seen_first_key(FALSE),
...@@ -10286,7 +10288,6 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg, ...@@ -10286,7 +10288,6 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
max_functions_it(NULL) max_functions_it(NULL)
{ {
head= table; head= table;
file= head->file;
index= use_index; index= use_index;
record= head->record[0]; record= head->record[0];
tmp_record= head->record[1]; tmp_record= head->record[1];
......
...@@ -500,6 +500,7 @@ class QUICK_RANGE_SELECT_GEOM: public QUICK_RANGE_SELECT ...@@ -500,6 +500,7 @@ class QUICK_RANGE_SELECT_GEOM: public QUICK_RANGE_SELECT
class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
{ {
Unique *unique;
public: public:
QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table); QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
~QUICK_INDEX_MERGE_SELECT(); ~QUICK_INDEX_MERGE_SELECT();
...@@ -684,7 +685,7 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I ...@@ -684,7 +685,7 @@ class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I class QUICK_GROUP_MIN_MAX_SELECT : public QUICK_SELECT_I
{ {
private: private:
handler *file; /* The handler used to get data. */ handler * const file; /* The handler used to get data. */
JOIN *join; /* Descriptor of the current query */ JOIN *join; /* Descriptor of the current query */
KEY *index_info; /* The index chosen for data access */ KEY *index_info; /* The index chosen for data access */
uchar *record; /* Buffer where the next record is returned. */ uchar *record; /* Buffer where the next record is returned. */
......
...@@ -2935,6 +2935,9 @@ class Unique :public Sql_alloc ...@@ -2935,6 +2935,9 @@ class Unique :public Sql_alloc
void reset(); void reset();
bool walk(tree_walk_action action, void *walk_action_arg); bool walk(tree_walk_action action, void *walk_action_arg);
uint get_size() const { return size; }
ulonglong get_max_in_memory_size() const { return max_in_memory_size; }
friend int unique_write_to_file(uchar* key, element_count count, Unique *unique); friend int unique_write_to_file(uchar* key, element_count count, Unique *unique);
friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique); friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique);
}; };
......
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