Fix IllegalStateException in UnifiedQueryPlanner.preserveCollation on composite-collation plans - #5650
Conversation
UnifiedQueryPlanner.preserveCollation reads the collation via RelTraitSet.getCollation(), which casts to a single RelCollation. When a plan derives several collations (Calcite stores them as a RelCompositeTrait) the cast throws ClassCastException and the query fails to plan. A multi-column literal-row plan (e.g. makeresults data=, which lowers to a multi-column LogicalValues) derives such composite collations and hits this; a single-column plan derives one collation and survives. Read the collation through the composite-safe getTraits(RelCollationTraitDef.INSTANCE) and materialize a LogicalSort only when there is exactly one genuine non-empty sort collation. A composite (several incidental derived collations) is not a user sort, so the plan is left unwrapped. Single-collation behavior is unchanged. Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
PR Reviewer Guide 🔍(Review updated until commit cb87a7e)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to cb87a7e Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit f860d87
Suggestions up to commit 3f39114
|
|
Persistent review updated to latest commit f860d87 |
Cover UnifiedQueryPlanner#preserveCollation with tests and remove the now-redundant inline comment (the rationale lives in the commit/PR). Unit tests (UnifiedQueryPlannerTest) exercise all four branches: - an existing top-level Sort is returned unchanged (no double-wrap); - a composite collation is left unwrapped instead of crashing (the regression: `sort age | eval x = age` makes x mirror age, so the top LogicalProject derives multiple collations stored as a RelCompositeTrait; the old getCollation() read threw "Trait index N has multiple values", surfacing as a 500); - a single genuine collation is materialized as a LogicalSort; - an unsorted plan is returned unwrapped. Execution coverage: UnifiedQueryCompilerTest compiles and runs the previously-crashing plan in-memory, and UnifiedQueryOpenSearchIT runs the same sort+eval-copy shape end to end against a real OpenSearch index. The composite-collation tests were confirmed to fail against the pre-fix implementation. Signed-off-by: Louis Chu <lingzhichu.clz@gmail.com>
f860d87 to
cb87a7e
Compare
|
Persistent review updated to latest commit cb87a7e |
Description
UnifiedQueryPlanner.preserveCollationfails to plan any query whose topRelNodecarries more than one collation in its trait set. It surfaces as a 500Failed to plan query.Root cause. The method reads the collation via:
RelTraitSet.getCollation()delegates togetTrait(RelCollationTraitDef.INSTANCE), which assumes a single collation. When a plan derives several collations, Calcite stores them as aRelCompositeTraitandgetTraitrefuses to return it, throwing:Such a composite arises whenever a non-
Sorttop node is ordered by more than one equivalent key — for example... | sort age | eval x = age:xmirrorsage, so the topLogicalProjectis sorted on bothageandxand derives two collations. A plan with a single derived collation reads back fine, which is why this was not caught earlier. (A single multi-column literal-row plan such asmakeresults format=csv data=...derives one multi-field collation, not a composite, so it does not trigger this.)Fix. Read the collation through the composite-safe
getTraits(RelCollationTraitDef.INSTANCE)and materialize aLogicalSortonly when there is exactly one genuine (non-empty) sort collation. A composite (several incidental derived collations) is not a user sort, so the plan is left unwrapped instead of crashing. Behavior for the single-collation case is unchanged.Related Issues
N/A
Check List
--signoff.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.