Repository navigation
[DISCUSSION] Sorts being removed from subqueries #15886
Copy link
Copy link
Closed
Labels
discussionProject DiscussionProject Discussion
Description
Describe the bug
Referenced discussion: https://the-asf.slack.com/archives/C01QUFS30TD/p1745875862723149
Given this table:
> create table d1 (ul_node_id string);
It appears that sorts are being removed from inner expressions. For example, here is a query with a sort expression:
> explain SELECT "ul_node_id", encode(ul_node_id, 'base64') FROM d1 ORDER BY encode(ul_node_id, 'base64') ASC NULLS LAST;
+---------------+-------------------------------+
| plan_type | plan |
+---------------+-------------------------------+
| physical_plan | ┌───────────────────────────┐ |
| | │ SortExec │ |
| | │ -------------------- │ |
| | │ encode(d1.ul_node_id,Utf8(│ |
| | │ "base64"))@1 ASC NULLS │ |
| | │ LAST │ |
| | └─────────────┬─────────────┘ |
| | ┌─────────────┴─────────────┐ |
| | │ ProjectionExec │ |
| | │ -------------------- │ |
| | │ encode(d1.ul_node_id,Utf8(│ |
| | │ "base64")): │ |
| | │ encode(ul_node_id, base64)│ |
| | │ │ |
| | │ ul_node_id: │ |
| | │ ul_node_id │ |
| | └─────────────┬─────────────┘ |
| | ┌─────────────┴─────────────┐ |
| | │ DataSourceExec │ |
| | │ -------------------- │ |
| | │ bytes: 0 │ |
| | │ format: memory │ |
| | │ rows: 0 │ |
| | └───────────────────────────┘ |
| | |
+---------------+-------------------------------+
1 row(s) fetched.
If I wrap it in a no-op CTE the SortExec stage disappears:
> explain WITH t0 AS (SELECT "ul_node_id", encode(ul_node_id, 'base64') FROM d1 ORDER BY encode(ul_node_id, 'base64') ASC NULLS LAST) SELECT * FROM t0;
+---------------+-------------------------------+
| plan_type | plan |
+---------------+-------------------------------+
| physical_plan | ┌───────────────────────────┐ |
| | │ ProjectionExec │ |
| | │ -------------------- │ |
| | │ encode(d1.ul_node_id,Utf8(│ |
| | │ "base64")): │ |
| | │ encode(ul_node_id, base64)│ |
| | │ │ |
| | │ ul_node_id: │ |
| | │ ul_node_id │ |
| | └─────────────┬─────────────┘ |
| | ┌─────────────┴─────────────┐ |
| | │ DataSourceExec │ |
| | │ -------------------- │ |
| | │ bytes: 0 │ |
| | │ format: memory │ |
| | │ rows: 0 │ |
| | └───────────────────────────┘ |
| | |
+---------------+-------------------------------+
1 row(s) fetched.
Manually hoisting out the ORDER BY from the CTE causes it to appear in the plan:
> explain WITH t0 AS (SELECT "ul_node_id", encode(ul_node_id, 'base64') FROM d1) SELECT * from t0 ORDER BY encode(ul_node_id, 'base64') ASC NULLS LAST;
+---------------+-------------------------------+
| plan_type | plan |
+---------------+-------------------------------+
| physical_plan | ┌───────────────────────────┐ |
| | │ SortExec │ |
| | │ -------------------- │ |
| | │ encode(ul_node_id@0, │ |
| | │ base64) ASC NULLS │ |
| | │ LAST │ |
| | └─────────────┬─────────────┘ |
| | ┌─────────────┴─────────────┐ |
| | │ ProjectionExec │ |
| | │ -------------------- │ |
| | │ encode(d1.ul_node_id,Utf8(│ |
| | │ "base64")): │ |
| | │ encode(ul_node_id, base64)│ |
| | │ │ |
| | │ ul_node_id: │ |
| | │ ul_node_id │ |
| | └─────────────┬─────────────┘ |
| | ┌─────────────┴─────────────┐ |
| | │ DataSourceExec │ |
| | │ -------------------- │ |
| | │ bytes: 0 │ |
| | │ format: memory │ |
| | │ rows: 0 │ |
| | └───────────────────────────┘ |
| | |
+---------------+-------------------------------+
1 row(s) fetched.
To Reproduce
No response
Expected behavior
No response
Additional context
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
discussionProject DiscussionProject Discussion
Activity
comphead commentedon Apr 28, 2025
Simpler test case
ORDER BY in outer query
should be the same as ORDER BY in CTE
But it is not. Sort is gone
[-]Sorts being removed from inner expressions[/-][+][REGRESSION] Sorts being removed from inner expressions[/+]maxburke commentedon Apr 28, 2025
I bisected this to commit 02eab80
maxburke commentedon Apr 28, 2025
I'm getting the results I expect if I revert the changes from that commit ^ in this file: datafusion/sql/src/relation/mod.rs (ie: remove the call to
optimize_subquery_sort)regarding apache#15886, comment out problematic query transformation
comphead commentedon Apr 28, 2025
Thanks for narrowing down
ozankabak commentedon Apr 29, 2025
I remember that we discussed this before and the conclusion that this is actually valid behavior. Quoting from Wikipedia:
IIRC the SQL standard defines things way. Granted, a lot of people do not like this behavior and find it surprising, but it is not wrong AFAICT -- and a lot of DBs behave this way.
Personally, I think the behavior is not surprising in @comphead's example, but it is somewhat surprising in @maxburke's example. For users that don't like the standard behavior, maybe we can add a configuration flag to DF that they can switch, which would make the
optimize_subquery_sortfunction respect subqueries.I am removing the
bugandregressiontags for now -- we can reclassify as such if we find out I'm mistaken.[-][REGRESSION] Sorts being removed from inner expressions[/-][+][DISCUSSION] Sorts being removed from inner expressions[/+]suibianwanwank commentedon Apr 29, 2025
I agree that, to my knowledge, quite a few engines remove sorting in subqueries without LIMIT/OFFSET. Adding a configuration option is a good solution.
alamb commentedon Apr 29, 2025
I also agree that the current behavior on main is expected but confusing. It would be great to improve it somehow (maybe a config setting)
4 remaining items
regarding apache#15886, comment out problematic query transformation
disable subquery sort optimization, see apache#15886
disable subquery sort optimization, see apache#15886
disable subquery sort optimization, see apache#15886
adriangb commentedon Dec 24, 2025
I hit this issue as well. I can confirm that Postgres, DuckDB, BigQuery, ClickHouse and SQLite do preserve the order from the subquery / CTE. I understand that technically this may be acceptable behavior, but I have not been able to find any system that has this behavior. @ozankabak @suibianwanwank could you share some of the database systems have this behavior?
I would like to propose that we (1) make this configurable and (2) change the default since (a) the majority of popular systems seem to behave in the same way and (b) the safer behavior is to preserve the order by even if it has some performance cost.
adriangb commentedon Dec 24, 2025
I found that this causes what I would call a bug with
Unparser: #19483.Essentially the following query unparses into a subquery which then when re-parsed and optimized looses the order by, i.e. parsing/unparsing SQL changes the query semantics:
We could try to fix it in the
Unparserbut the the point is that even our own codebase assumes the opposite behavior.ozankabak commentedon Dec 25, 2025
Let's make this configurable. It is an optimization the other systems don't do. the users can then turn it on/off depending on their use case.
Preserve ORDER BY in Unparser for projection -> order by pattern (#19483
Preserve ORDER BY in Unparser for projection -> order by pattern (apa…
disable subquery sort optimization, see apache#15886
fix: impl `handle_child_pushdown_result` for `SortExec` (#21527)
feat: add a config to disable subquery_sort_elimination (#21614)
fix: impl `handle_child_pushdown_result` for `SortExec` (apache#21527)
feat: add a config to disable subquery_sort_elimination (apache#21614)