Skip to content

Commit 9e8d646

Browse files
committed
Bug#36421727 mysql server 8.3.0 failed at Query_expression::optimize
In the repro, we have a query which has a deeply nested aggregate function (AVG) containing a subquery. The aggregate function aggregates in the outermost query block - it contains no outer reference, but all intervening query blocks forbid aggregation (the aggregate function is contained in their WHERE clauses). Now, transformation to derived table merge, transformation to semi-join, and elimination of an impossible where clause subsequently removed parts of the query tree, including the subquery which is an argument of the aggregate function. But the subquery is still needed for the evaluation of the aggregate function in the outermost query block, so we end up trying to evaluate a destroyed query expression, hence the server exit. This happens because the cleanup procedure for the aggregate function is never called. The culprit is the implementation of Query_block::build_sj_cond(), which is called as part of subquery transformation. It is called with a Table_ref object containing two equality predicates, a const one and a predicate involving the aggregate function. Since the first predicate is false and it evaluates to true, the entire condition is abandoned and replaced with FALSE. However, no cleanup code is invoked for the aggregate function, hence the problems. Solution: change build_sj_cond() so that it generates a full condition, and then apply Item::clean_up_after_removal on the condition when all predicates have been processed. Change-Id: Iaa51de16017179bfbc1224798beaa321ba767166
1 parent 0140f79 commit 9e8d646

File tree

2 files changed

+45
-32
lines changed

2 files changed

+45
-32
lines changed

sql/sql_resolver.cc

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,23 +2265,22 @@ void Query_block::clear_sj_expressions(NESTED_JOIN *nested_join) {
22652265
}
22662266

22672267
/**
2268-
Build equality conditions using outer expressions and inner
2269-
expressions. If the equality condition is not constant, add
2270-
it to the semi-join condition. Otherwise, evaluate it and
2271-
remove the constant expressions from the
2272-
outer/inner expressions list if the result is true. If the
2273-
result is false, remove all the expressions in outer/inner
2274-
expression list and attach an always false condition
2275-
to semijoin condition.
2276-
2277-
@param thd Thread context
2278-
@param nested_join Join nest
2279-
@param subq_query_block Query block for the subquery
2280-
@param outer_tables_map Map of tables from original outer query block
2268+
Build equality predicates using outer expressions and inner expressions.
2269+
If an equality predicate is not constant, add it to the semi-join condition.
2270+
Otherwise, evaluate the predicate. If the result of the predicate is true,
2271+
remove the expressions of the constant predicate from the outer/inner
2272+
expressions list. If the result is false, remove all the expressions in
2273+
outer/inner expression list and attach an always false condition to
2274+
semijoin condition.
2275+
2276+
@param thd Thread context
2277+
@param nested_join Join nest
2278+
@param subq_query_block Query block for the subquery
2279+
@param outer_tables_map Map of tables from original outer query block
22812280
@param[in,out] sj_cond Semi-join condition to be constructed
22822281
Contains non-equalities on input.
2283-
@param[out] simple_const true if the returned semi-join condition is
2284-
a simple true or false predicate, false otherwise.
2282+
@param[out] simple_const true if the returned semi-join condition is
2283+
a simple true or false predicate, false otherwise.
22852284
22862285
@return false if success, true if error
22872286
*/
@@ -2292,12 +2291,13 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
22922291
*simple_const = false;
22932292

22942293
Item *new_cond = nullptr;
2294+
bool remove_condition = false;
22952295

22962296
auto ii = nested_join->sj_inner_exprs.begin();
22972297
auto oi = nested_join->sj_outer_exprs.begin();
22982298
while (ii != nested_join->sj_inner_exprs.end() &&
22992299
oi != nested_join->sj_outer_exprs.end()) {
2300-
bool should_remove = false;
2300+
bool remove_predicate = false;
23012301
Item *inner = *ii;
23022302
Item *outer = *oi;
23032303
/*
@@ -2312,7 +2312,7 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
23122312
Item *predicate = item_eq;
23132313
if (!item_eq->fixed && item_eq->fix_fields(thd, &predicate)) return true;
23142314

2315-
// Evaluate if the condition is on const expressions
2315+
// Evaluate if the predicate is a const value:
23162316
if (predicate->const_item() &&
23172317
!(predicate)->walk(&Item::is_non_const_over_literals,
23182318
enum_walk::POSTFIX, nullptr)) {
@@ -2339,20 +2339,14 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
23392339
const condition evaluates to true as Item_cond::fix_fields will
23402340
remove the condition later.
23412341
*/
2342-
should_remove = true;
2342+
remove_predicate = true;
23432343
} else {
23442344
/*
2345-
Remove all the expressions in inner/outer expression list if
2346-
one of condition evaluates to always false. Add an always false
2347-
condition to semi-join condition.
2345+
Predicate is false, and thus condition is false. However, generate
2346+
the full condition so that it can be removed completely when all
2347+
predicates have been processed.
23482348
*/
2349-
nested_join->sj_inner_exprs.clear();
2350-
nested_join->sj_outer_exprs.clear();
2351-
Item *new_item = new Item_func_false();
2352-
if (new_item == nullptr) return true;
2353-
(*sj_cond) = new_item;
2354-
*simple_const = true;
2355-
return false;
2349+
remove_condition = true;
23562350
}
23572351
}
23582352
/*
@@ -2370,7 +2364,7 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
23702364
*/
23712365
nested_join->sj_corr_tables |= inner->used_tables() & outer_tables_map;
23722366

2373-
if (should_remove) {
2367+
if (remove_predicate) {
23742368
ii = nested_join->sj_inner_exprs.erase(ii);
23752369
oi = nested_join->sj_outer_exprs.erase(oi);
23762370
} else {
@@ -2380,6 +2374,25 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
23802374
++ii, ++oi;
23812375
}
23822376
}
2377+
if (remove_condition) {
2378+
/*
2379+
Condition is false.
2380+
Clean up the synthesized condition.
2381+
Remove all the expressions in inner/outer expression list.
2382+
Add an always false predicate to semi-join condition.
2383+
*/
2384+
Item::Cleanup_after_removal_context ctx(this);
2385+
new_cond->walk(&Item::clean_up_after_removal, walk_options,
2386+
pointer_cast<uchar *>(&ctx));
2387+
2388+
nested_join->sj_inner_exprs.clear();
2389+
nested_join->sj_outer_exprs.clear();
2390+
Item *new_item = new Item_func_false();
2391+
if (new_item == nullptr) return true;
2392+
(*sj_cond) = new_item;
2393+
*simple_const = true;
2394+
return false;
2395+
}
23832396
/*
23842397
Semijoin processing expects at least one inner/outer expression
23852398
in the list if there is a sj_nest present. This is required for semi-join

sql/sql_union.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,11 +1484,11 @@ void Query_block::destroy() {
14841484
unit->destroy();
14851485
unit = next;
14861486
}
1487-
14881487
List_iterator<Window> li(m_windows);
14891488
Window *w;
1490-
while ((w = li++)) w->destroy();
1491-
1489+
while ((w = li++)) {
1490+
w->destroy();
1491+
}
14921492
// Destroy allocated derived tables
14931493
destroy_tmp_tables(get_table_list());
14941494

0 commit comments

Comments
 (0)