MDEV-41225 Parse the ORDER BY tail of a wrapped parenthesized query … - #5795
Open
mariadb-RexJohnston wants to merge 1 commit into
Open
mariadb-RexJohnston wants to merge 1 commit into
mariadb-RexJohnston wants to merge 1 commit into
Conversation
mariadb-RexJohnston
force-pushed
the
10.11-MDEV-41225
branch
from
September 30, 2026 01:21
86f43d5 to
e5191ba
Compare
…n the wrapping select
A parenthesized query expression that already has its own ORDER BY or
LIMIT and is followed by another ORDER BY, e.g.
(SELECT c1 FROM t1 ORDER BY c1 LIMIT 2) ORDER BY <expr> LIMIT 1
is wrapped into a derived table, and the outer ORDER BY/LIMIT is attached
to the wrapping select. The grammar pushed the inner select before
parsing the tail, and add_tail_to_query_expression_body_ext_parens()
created the wrapper only after the tail had been parsed. Anything that
is attached to the current select while the tail is being parsed was
therefore attached to the inner select, while the ORDER BY list itself
was moved to the wrapper:
- Window functions and their window specs were added to the inner
select's window_funcs/window_specs. The same Item_window_func was then
in the wrapper's ORDER BY and in the inner select's window function
list, which triggers the original assertion reported in MDEV-41225.
(SELECT 1 FROM t1 LIMIT 1)
ORDER BY PERCENTILE_DISC(1) WITHIN GROUP(ORDER BY TIME'0') OVER();
The function is computed by the inner select over the rows before its
LIMIT, so the outer ORDER BY sorts on wrong values
(e.g. COUNT(*) OVER () returned 3 for a 2-row result).
- Subqueries were registered as units of the inner select. An
uncorrelated subquery in the tail crashed the server with SIGSEGV when
it was executed by the outer filesort. A correlated one resolved its
outer references in the inner select (t1.c1) instead of the derived
table.
The decision to wrap depends only on whether the inner select already
has a tail and on whether the outer tail has ORDER BY. LIMIT and locking
clauses cannot contain window functions or subqueries.
We replace query_expression_tail with
query_expression_tail_with_order and query_expression_tail_no_order
so the mid-rule action knows whether an ORDER BY follows.
The new LEX::push_select_for_ext_parens_tail() creates the wrapper before
an ORDER BY tail is parsed when wrapping is required, and pushes it instead
of the inner select. The tail's items are then created in the wrapper's
context, and window functions and subqueries are attached to the wrapper.
add_tail_to_query_expression_body_ext_parens() takes the pushed select and
skips wrapping when it has already happened; the other cases keep the
previous logic. The grammar has the same number of conflicts as before.
A subquery in such a tail can no longer refer to the tables of the wrapped
query expression (e.g. t1.c1 above) and gets ER_BAD_FIELD_ERROR,
as those tables are not visible outside the derived table.
This commit was prepared with Claude Code (Opus 5.5). It traced the
wrong-result and crash cases to the parse-time registration of window
functions and subqueries in the inner select, tested the grammar split
with bison, wrote the code change and the brackets test.
mariadb-RexJohnston
force-pushed
the
10.11-MDEV-41225
branch
from
September 30, 2026 01:23
e5191ba to
55ff0a4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…in the wrapping select
A parenthesized query expression that already has its own ORDER BY or LIMIT and is followed by another ORDER BY, e.g.
(SELECT c1 FROM t1 ORDER BY c1 LIMIT 2) ORDER BY LIMIT 1
is wrapped into a derived table, and the outer ORDER BY/LIMIT is attached to the wrapping select. The grammar pushed the inner select before parsing the tail, and add_tail_to_query_expression_body_ext_parens() created the wrapper only after the tail had been parsed. Anything that is attached to the current select while the tail is being parsed was therefore attached to the inner select, while the ORDER BY list itself was moved to the wrapper:
Window functions and their window specs were added to the inner select's window_funcs/window_specs. The same Item_window_func was then in the wrapper's ORDER BY and in the inner select's window function list, which triggers the original assertion reported in MDEV-41225.
(SELECT 1 FROM t1 LIMIT 1) ORDER BY PERCENTILE_DISC(1) WITHIN GROUP(ORDER BY TIME'0') OVER();
The function is computed by the inner select over the rows before its LIMIT, so the outer ORDER BY sorts on wrong values (e.g. COUNT(*) OVER () returned 3 for a 2-row result).
Subqueries were registered as units of the inner select. An uncorrelated subquery in the tail crashed the server with SIGSEGV when it was executed by the outer filesort. A correlated one resolved its outer references in the inner select (t1.c1) instead of the derived table.
The decision to wrap depends only on whether the inner select already has a tail and on whether the outer tail has ORDER BY. LIMIT and locking clauses cannot contain window functions or subqueries. We replace query_expression_tail with
query_expression_tail_with_order and query_expression_tail_no_order so the mid-rule action knows whether an ORDER BY follows. The new LEX::push_select_for_ext_parens_tail() creates the wrapper before an ORDER BY tail is parsed when wrapping is required, and pushes it instead of the inner select. The tail's items are then created in the wrapper's context, and window functions and subqueries are attached to the wrapper. add_tail_to_query_expression_body_ext_parens() takes the pushed select and skips wrapping when it has already happened; the other cases keep the previous logic. The grammar has the same number of conflicts as before.
A subquery in such a tail can no longer refer to the tables of the wrapped query expression (e.g. t1.c1 above) and gets ER_BAD_FIELD_ERROR, as those tables are not visible outside the derived table.
This commit was prepared with Claude Code (Opus 5.5). It traced the wrong-result and crash cases to the parse-time registration of window functions and subqueries in the inner select, tested the grammar split with bison, wrote the code change and the brackets test.