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.
This commit is contained in:
parent
a08780df98
commit
dac59fa9c3
@ -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;
|
|||||||
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()
|
|||||||
|
|
||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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()
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@ public:
|
|||||||
|
|
||||||
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 @@ private:
|
|||||||
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 @@ public:
|
|||||||
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);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user