fix(aggregate): show aliased expr in explain#21739
fix(aggregate): show aliased expr in explain#21739kumarUjjawal wants to merge 6 commits intoapache:mainfrom
Conversation
|
cc @pepijnve |
|
It has much more cases than I realized initially 😢 |
|
I updated the code with new changes along with pr body |
|
I tried two approaches for this. The first approach was by hiding the bit in alias metadata. It fixed the display problem, but it also mixed planner-only state with user metadata. That made the behavior harder to reason about and forced extra handling in places like equality, hashing, serialization, and rewrite logic. In practice, a simple display fix startedaffecting alias identity and metadata flow in too many places. The current approach adds an explicit internal flag to alias. This is a small API break, but it makes the model much clearer: whether an alias is user written or planner generated is now part of the type itself, not hidden in metadata. That keeps the display logic direct, avoids hidden state leaking into unrelated code paths, and makes future maintenance safer because the intent is visible and compiler checked. Would love to hear your thoughts @pepijnve |
|
@kumarUjjawal could you give an example of where/when the internal flag is necessary? |
| .map(|expr| expr.human_display()) | ||
| .map(|expr| { | ||
| let human_display = expr.human_display(); | ||
| if human_display.is_empty() { |
There was a problem hiding this comment.
Should human_display be Option<String>?
There was a problem hiding this comment.
Yeah I could do that.
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_aggregate_explain_shows_aliased_expression() -> Result<()> { |
There was a problem hiding this comment.
These might be more concise as SLTs
I think the current behavior is intentional. My thinking:
count() is in the second group, so keeping aggr=[count()] is expected. The bug here is about user aliases like sum(...) as agg disappearing in physical explain, not about exposing every internal rewrite. So while the physical plan does execute the lowered count(1) form, showing count(1) as count() in explain would expose planner internals and would regress the compact count() output we already preserve elsewhere. If we want physical explain to show lowered forms more generally, I think that should be doable but will require some changes. |
A good example could be: Internally, COUNT() is lowered to count(1) so it can use the normal aggregate path. But the user did not write count(1), they wrote count(). So the planner needs a way to preserve that user-facing name without treating it like a real user alias. That is what the internal flag is for: it marks aliases that exist only because the planner rewrote the expression. Without that bit, physical explain cannot tell these two cases apart:
Those should display differently. For example, with: SELECT COUNT(*) AS total_rows FROM t there are really two alias layers:
The internal flag lets explain show count() as total_rows, instead of either exposing the lowered form count(1) as count() as total_rows or collapsing everything to just total_rows. |
|
This might just be personal preference speaking, but in a physical explain plan I'm looking for what the engine is actually doing (how is it being executed), not what I wrote as query. It doesn't make sense for me that the logical plan (which is more declarative in nature) shows the lowered version, while the physical plan (which is more imperative) does not. If anything it should be the other way around that logical hides this detail, and physical shows it. So if I got to choose I would prefer the left side plan of the diff image above rather than the right one. |
I think I like this approach too, showing lowered expressions in physical explain can be more useful. Let me see how the code looks for this. |
21c1690 to
2580bc5
Compare

Which issue does this PR close?
Rationale for this change
Physical explain output only showed the alias for aliased aggregates. That made it hard to understand the plan, especially when the aggregate had a filter, explicit RESPECT NULLS, or a custom UDAF display.
What changes are included in this PR?
Are these changes tested?
Yes
Are there any user-facing changes?
Yes.
This is a public API change for users who build or pattern match Alias directly. The upgrade guide has been updated with the needed changes.