fix: optimize_projections failure with struct-field join keys - #22903
Conversation
|
A subquery that survives decorrelation, such as an inequality-correlated scalar subquery over struct-field join keys, can still hit the stale-schema issue on the subquery traversal path. That plan shape cannot reach physical execution today, so I’m leaving it for a follow-up rather than expanding this PR. Any feedback would be great. |
There was a problem hiding this comment.
@kumarUjjawal
Looks good overall. I left one small performance-related suggestion, but nothing blocking.
| // traversal in the same optimizer pass. This is also | ||
| // correctness-sensitive: the in-place path refreshes parent | ||
| // schemas after child schemas change. | ||
| let has_subqueries = plan_has_subqueries(&new_plan); |
There was a problem hiding this comment.
plan_has_subqueries now runs once per rule, even after it has already returned false. Correctness still looks fine, but for the common case where there are no subqueries, this changes the old once-per-pass scan into roughly one full plan scan per optimizer rule.
Could we cache the flag for the pass and refresh it only when a rule actually changes the plan? For example: let mut has_subqueries = plan_has_subqueries(&new_plan); and then if transformed { has_subqueries = plan_has_subqueries(&new_plan); }. That should keep the behavior where subqueries removed mid-pass are noticed, while still staying safe if a rule introduces a subquery.
There was a problem hiding this comment.
Thank you! I should have caught this.
| // Recurse into children using Arc::make_mut (zero-cost when refcount == 1) | ||
| changed |= map_children_mut(plan, |child| { | ||
| rewrite_plan_in_place(child, apply_order, rule, config) | ||
| let mut child_schema_changed = false; |
There was a problem hiding this comment.
This seems like it is potentially treating a symptom rather than the root cause 🤔
I worry this change may be ignoring the root cause elsewhere (an incorrectly reported "transofrmed" flag)
It seems like if the plan returns transformed = false the schema should not have changed.
There was a problem hiding this comment.
I worry this change may be ignoring the root cause elsewhere (an incorrectly reported "transofrmed" flag)
I think validating Transformed accuracy is worthwhile, but probably separate from this fix.
This PR intentionally continues to trust transformed; it recomputes parent schemas when a child rewrite reports changed = true and the child schema actually differs.
|
run benchmark sql_planner |
|
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing fix/no-field-name-join (144d0e9) to d77a02d (merge-base) diff using: sql_planner File an issue against this benchmark runner |
|
🤖 Benchmark completed (GKE) | trigger Instance: CPU Details (lscpu)Details
Resource Usagesql_planner — base (merge-base)
sql_planner — branch
File an issue against this benchmark runner |
I think @alamb's nulability fix interacted with apache#22903 reverting the optimization
…#22903) ## Which issue does this PR close? <!-- We generally require a GitHub issue to be filed for all bug fixes and enhancements and this helps us generate change logs for our releases. You can link an issue to this PR using the GitHub syntax. For example `Closes apache#123` indicates that this PR will close issue apache#123. --> - Closes apache#22895. ## Rationale for this change Join-key extraction can add helper projections and change a child plan's output schema. Some parent nodes cache their schema, so after the child rewrite they can still expose stale fields. Later projection pruning then uses the stale schema and fails with missing-column errors. <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. --> ## What changes are included in this PR? - Refresh a parent schema only when a rewritten child reports an actual schema change. - Re-check whether the plan has subqueries before each rule, so plans that are decorrelated by earlier rules can use the in-place path in the same optimizer pass. - Add regression coverage for the reported struct-field join-key failures. - Add a regression case for union output labels while union-to-filter rewriting is enabled. - Add a focused optimizer unit test for parent schema refresh after child schema changes. - Update one existing optimized-plan expectation. The new plan removes a redundant dedup step; the surrounding semi joins already preserve distinctness. <!-- There is no need to duplicate the description in the issue here but it is sometimes worth providing a summary of the individual changes in this PR. --> ## Are these changes tested? Yes <!-- We typically require tests for all PRs in order to: 1. Prevent the code from being accidentally broken by subsequent changes 2. Serve as another way to document the expected behavior of the code If tests are not included in your PR, please explain why (for example, are they covered by existing tests)? --> ## Are there any user-facing changes? No <!-- If there are user-facing changes then we may require documentation to be updated before approving the PR. --> <!-- If there are any breaking changes to public APIs, please add the `api change` label. --> --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
Which issue does this PR close?
optimize_projectionsfails with "No field named ..." when join keys containget_field(ExtractLeafExpressions) #22895.Rationale for this change
Join-key extraction can add helper projections and change a child plan's output schema. Some parent nodes cache their schema, so after the child rewrite they can still expose stale fields. Later projection pruning then uses the stale schema and fails with missing-column errors.
What changes are included in this PR?
Are these changes tested?
Yes
Are there any user-facing changes?
No