MDEV-21102: Server crashes in JOIN_CACHE::write_record_data upon EXPLAIN with subqueries
JOIN_CACHE has a light-weight initialization mode that's targeted at EXPLAINs. In that mode, JOIN_CACHE objects are not able to execute. Light-weight mode was used whenever the statement was an EXPLAIN. However the EXPLAIN can execute subqueries, provided they enumerate less than @@expensive_subquery_limit rows. Make sure we use light-weight initialization mode only when the select is more expensive @@expensive_subquery_limit. Also add an assert into JOIN_CACHE::put_record() which prevents its use if it was initialized for EXPLAIN only.
This commit is contained in:
parent
a618ff2b1c
commit
8cc36fb743
@ -6396,5 +6396,27 @@ b b d c c
|
|||||||
10 NULL NULL NULL NULL
|
10 NULL NULL NULL NULL
|
||||||
DROP TABLE t1,t2,t3,t4;
|
DROP TABLE t1,t2,t3,t4;
|
||||||
#
|
#
|
||||||
|
# MDEV-21102: Server crashes in JOIN_CACHE::write_record_data upon EXPLAIN with subqueries and constant tables
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a int, b int) ENGINE=MyISAM;
|
||||||
|
CREATE TABLE t2 (c int, d int) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t2 VALUES (1,10);
|
||||||
|
CREATE TABLE t3 (e int, key (e)) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t3 VALUES (2),(3);
|
||||||
|
# Must not crash, must use join buffer in subquery
|
||||||
|
EXPLAIN
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE a > b OR a IN (
|
||||||
|
SELECT c FROM t2 WHERE EXISTS (
|
||||||
|
SELECT * FROM t3 t3a JOIN t3 t3b WHERE t3a.e < d
|
||||||
|
)
|
||||||
|
);
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||||
|
2 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 1
|
||||||
|
3 SUBQUERY t3a index e e 5 NULL 2 Using where; Using index
|
||||||
|
3 SUBQUERY t3b index NULL e 5 NULL 2 Using index; Using join buffer (flat, BNL join)
|
||||||
|
DROP TABLE t1,t2,t3;
|
||||||
|
#
|
||||||
# End of 10.4 tests
|
# End of 10.4 tests
|
||||||
#
|
#
|
||||||
|
@ -4305,6 +4305,27 @@ eval $q2;
|
|||||||
|
|
||||||
DROP TABLE t1,t2,t3,t4;
|
DROP TABLE t1,t2,t3,t4;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-21102: Server crashes in JOIN_CACHE::write_record_data upon EXPLAIN with subqueries and constant tables
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t1 (a int, b int) ENGINE=MyISAM;
|
||||||
|
|
||||||
|
CREATE TABLE t2 (c int, d int) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t2 VALUES (1,10);
|
||||||
|
|
||||||
|
CREATE TABLE t3 (e int, key (e)) ENGINE=MyISAM;
|
||||||
|
INSERT INTO t3 VALUES (2),(3);
|
||||||
|
|
||||||
|
--echo # Must not crash, must use join buffer in subquery
|
||||||
|
EXPLAIN
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE a > b OR a IN (
|
||||||
|
SELECT c FROM t2 WHERE EXISTS (
|
||||||
|
SELECT * FROM t3 t3a JOIN t3 t3b WHERE t3a.e < d
|
||||||
|
)
|
||||||
|
);
|
||||||
|
DROP TABLE t1,t2,t3;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.4 tests
|
--echo # End of 10.4 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -562,6 +562,8 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
|
|||||||
This measure is used instead of JOIN::read_time, because it is considered
|
This measure is used instead of JOIN::read_time, because it is considered
|
||||||
to be much more reliable than the cost estimate.
|
to be much more reliable than the cost estimate.
|
||||||
|
|
||||||
|
Note: the logic in this function must agree with JOIN::init_join_caches().
|
||||||
|
|
||||||
@return true if the subquery is expensive
|
@return true if the subquery is expensive
|
||||||
@return false otherwise
|
@return false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -1589,6 +1589,7 @@ bool JOIN_CACHE::put_record()
|
|||||||
{
|
{
|
||||||
bool is_full;
|
bool is_full;
|
||||||
uchar *link= 0;
|
uchar *link= 0;
|
||||||
|
DBUG_ASSERT(!for_explain_only);
|
||||||
if (prev_cache)
|
if (prev_cache)
|
||||||
link= prev_cache->get_curr_rec_link();
|
link= prev_cache->get_curr_rec_link();
|
||||||
write_record_data(link, &is_full);
|
write_record_data(link, &is_full);
|
||||||
|
@ -1876,6 +1876,26 @@ JOIN::init_range_rowid_filters()
|
|||||||
|
|
||||||
int JOIN::init_join_caches()
|
int JOIN::init_join_caches()
|
||||||
{
|
{
|
||||||
|
bool init_for_explain= false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Can we use lightweight initalization mode just for EXPLAINs? We can if
|
||||||
|
we're certain that the optimizer will not execute the subquery.
|
||||||
|
The optimzier will not execute the subquery if it's too expensive. For
|
||||||
|
the exact criteria, see Item_subselect::is_expensive().
|
||||||
|
Note that the subquery might be a UNION and we might not yet know if it is
|
||||||
|
expensive.
|
||||||
|
What we do know is that if this SELECT is too expensive, then the whole
|
||||||
|
subquery will be too expensive as well.
|
||||||
|
So, we can use lightweight initialization (init_for_explain=true) if this
|
||||||
|
SELECT examines more than @@expensive_subquery_limit rows.
|
||||||
|
*/
|
||||||
|
if ((select_options & SELECT_DESCRIBE) &&
|
||||||
|
get_examined_rows() >= thd->variables.expensive_subquery_limit)
|
||||||
|
{
|
||||||
|
init_for_explain= true;
|
||||||
|
}
|
||||||
|
|
||||||
JOIN_TAB *tab;
|
JOIN_TAB *tab;
|
||||||
|
|
||||||
for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
|
for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
|
||||||
@ -1895,7 +1915,8 @@ int JOIN::init_join_caches()
|
|||||||
{
|
{
|
||||||
table->prepare_for_keyread(tab->index, table->read_set);
|
table->prepare_for_keyread(tab->index, table->read_set);
|
||||||
}
|
}
|
||||||
if (tab->cache && tab->cache->init(select_options & SELECT_DESCRIBE))
|
|
||||||
|
if (tab->cache && tab->cache->init(init_for_explain))
|
||||||
revise_cache_usage(tab);
|
revise_cache_usage(tab);
|
||||||
else
|
else
|
||||||
tab->remove_redundant_bnl_scan_conds();
|
tab->remove_redundant_bnl_scan_conds();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user