Skip to content

PHOENIX-7891 Explain the query optimizer's index selection rationale - #2513

Merged
apurtell merged 2 commits into
apache:PHOENIX-7876-featurefrom
apurtell:PHOENIX-7891
Jun 11, 2026
Merged

PHOENIX-7891 Explain the query optimizer's index selection rationale#2513
apurtell merged 2 commits into
apache:PHOENIX-7876-featurefrom
apurtell:PHOENIX-7891

Conversation

@apurtell

@apurtellapurtell commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Capture the query optimizer's index selection rationale with a new data model and a closed set of RULE_* and REASON_* string constants plumbed through QueryPlan/BaseQueryPlan/DelegateQueryPlan. AddPlanResult returns from the two addPlan overloads, and a DecisionState accumulator is threaded through. A new helper method assigns the winning rule and collected rejections. New ExplainPlanAttributes fields indexRule and indexRejected are set in BaseQueryPlan from getOptimizerDecision(), with matching ExplainPlanTestUtil fluent assertions indexRule, indexRuleStartsWith, indexRejectedCount, indexRejected, and indexRejectedNone.

EXPLAIN output gains INDEX <name> [<kind>] [/* <rule> */] for chosen index and one /* !INDEX <name> -- <reason> */ line per rejected index.

Co-authored-by: Claude Opus 4.8[1m] noreply@anthropic.com

Capture the query optimizer's index selection rationale with a new data model
and a closed set of RULE_* and REASON_* string constants plumbed through
QueryPlan/BaseQueryPlan/DelegateQueryPlan. AddPlanResult returns from the two
addPlan overloads, and a DecisionState accumulator is threaded through. A new
helper method assigns the winning rule and collected rejections. New
ExplainPlanAttributes fields indexRule and indexRejected are set in
BaseQueryPlan from getOptimizerDecision(), with matching ExplainPlanTestUtil
fluent assertions indexRule, indexRuleStartsWith, indexRejectedCount,
indexRejected, indexRejectedNone. EXPLAIN output gains
'INDEX <name> [<kind>] [/* <rule> */]' for chosen index and one
'/* !INDEX <name> -- <reason> */' line per rejected index.
Co-authored-by: Claude Opus 4.8[1m] <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a structured “optimizer decision” model to capture why the query optimizer chose a particular index (or the data table), and wires that rationale through QueryPlan into both structured explain attributes and EXPLAIN text output.

Changes:

  • Introduces OptimizerDecision, OptimizerReasons (RULE_* / REASON_*), and RejectedIndexEntry, and records these during plan selection in QueryOptimizer.
  • Extends explain plumbing so EXPLAIN can render the chosen rule as a comment on the INDEX line and render /* !INDEX ... -- reason */ lines for rejected candidates; also exposes indexRule / indexRejected via ExplainPlanAttributes.
  • Updates unit and integration tests to assert optimizer rule/rejection metadata and updated EXPLAIN output formatting.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
FileDescription
phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.javaUpdates plan assertions to include indexRule / rejected-index expectations.
phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTestUtil.javaAdds fluent assertions for indexRule and rejected-index lists.
phoenix-core/src/test/java/org/apache/phoenix/query/explain/ExplainPlanTest.javaUpdates expected EXPLAIN text/JSON and adds helper assertions for new INDEX comments and !INDEX lines.
phoenix-core/src/test/java/org/apache/phoenix/compile/TenantSpecificViewIndexCompileTest.javaAdds optimizer rule/rejection assertions to compile-time plan tests.
phoenix-core/src/test/java/org/apache/phoenix/compile/StatementHintsCompilationTest.javaAdds optimizer rule/rejection assertions for hint-related compilation paths.
phoenix-core/src/test/java/org/apache/phoenix/compile/QueryOptimizerTest.javaAsserts chosen rule and rejected index reason in optimizer selection tests.
phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.javaAdds optimizer decision assertions to regression tests.
phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.javaAdds optimizer decision assertions to end-to-end view/index scenarios.
phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.javaAdds optimizer decision assertions to reverse scan scenarios.
phoenix-core/src/it/java/org/apache/phoenix/end2end/json/JsonFunctionsIT.javaAdds optimizer decision assertions for JSON/index-related plans.
phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinLocalIndexIT.javaAdds optimizer decision assertions for join subplans and rejection reasons.
phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinGlobalIndexIT.javaAdds optimizer decision assertions for join subplans and rejection reasons.
phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.javaAdds optimizer decision assertions for point-lookup and range-scan IN-list plans.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ViewIndexIT.javaAdds optimizer decision assertions to view-index usage tests.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SingleCellIndexIT.javaAsserts rule and explicit rejection reasons (e.g., NO_INDEX hint).
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.javaAdds optimizer decision assertions across salted index/table variants.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/PartialIndexIT.javaAdds optimizer rule assertions for partial index + hint behavior.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.javaAdds optimizer decision assertions to mutable index selection tests.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalIndexIT.javaAdds optimizer decision assertions and explicit local-vs-global rejection reasons.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexUsageIT.javaAdds optimizer rule/rejection assertions across several index-usage patterns.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexOptimizationIT.javaAdds optimizer rule/rejection assertions for optimization flows and subplans.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalIndexCheckerIT.javaAdds overload to assert expected rule and asserts rule/rejections in checks.
phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.javaAdds optimizer rule assertions for child-view index selection.
phoenix-core/src/it/java/org/apache/phoenix/end2end/CostBasedDecisionIT.javaAdds optimizer rule/rejection assertions for cost-based winner/loser decisions.
phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.javaAdds optimizer rule assertions to shared base IT helpers.
phoenix-core-client/src/main/java/org/apache/phoenix/optimize/RejectedIndexEntry.javaNew value type for a rejected index + reason.
phoenix-core-client/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.javaRecords optimizer decisions, threads DecisionState, and tags rejection reasons.
phoenix-core-client/src/main/java/org/apache/phoenix/optimize/OptimizerReasons.javaNew closed-set string vocabulary for rules and rejection reasons.
phoenix-core-client/src/main/java/org/apache/phoenix/optimize/OptimizerDecision.javaNew model capturing chosen index, rule, and rejected index entries.
phoenix-core-client/src/main/java/org/apache/phoenix/iterate/ExplainTable.javaRenders rule comments on INDEX and prints !INDEX rejection lines.
phoenix-core-client/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.javaExposes optimizer decision to explain rendering.
phoenix-core-client/src/main/java/org/apache/phoenix/execute/DelegateQueryPlan.javaDelegates optimizer decision get/set to wrapped plan.
phoenix-core-client/src/main/java/org/apache/phoenix/execute/BaseQueryPlan.javaStores optimizer decision and propagates it into structured explain attributes.
phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryPlan.javaAdds default get/set methods for optimizer decisions.
phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExplainPlanAttributes.javaAdds indexRule and indexRejected fields + builder plumbing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@apurtell

Copy link
Copy Markdown
ContributorAuthor

Test Results

LayerSuitesPassedErrorsSkippedNotes
Connectionless unit tests634103
Integration tests (minicluster)3312044144 pre-existing, unrelated (see below)
Total391545417green except documented pre-existing flake

Unit tests

SuiteTestsFailuresErrorsSkipped
query.explain.ExplainPlanTest63000
compile.QueryCompilerTest215002
compile.QueryOptimizerTest49001
compile.TenantSpecificViewIndexCompileTest6000
compile.StatementHintsCompilationTest4000
query.QueryPlanTest4000
Subtotal341003

Integration tests

Index suites

SuiteTestsFailuresErrorsSkipped
end2end.index.GlobalIndexCheckerIT112006
end2end.index.MutableIndexIT90000
end2end.index.PartialIndexIT60000
end2end.index.IndexUsageIT39000
end2end.index.SingleCellIndexIT14000
end2end.index.GlobalIndexOptimizationIT7000
end2end.index.SaltedIndexIT2000
end2end.index.LocalIndexIT6004 ⚠0

Join suites

SuiteTestsFailuresErrorsSkipped
end2end.join.HashJoinGlobalIndexIT33000
end2end.join.HashJoinLocalIndexIT34000
end2end.join.HashJoinNoIndexIT33000
end2end.join.SortMergeJoinGlobalIndexIT35000
end2end.join.SortMergeJoinLocalIndexIT35000
end2end.join.SortMergeJoinNoIndexIT35000

Cost / optimizer

SuiteTestsFailuresErrorsSkipped
end2end.CostBasedDecisionIT20000

View / tenant suites

SuiteTestsFailuresErrorsSkipped
end2end.index.ViewIndexIT32002
end2end.TenantSpecificViewIndexIT (covers BaseTenantSpecificViewIndexIT)11000
end2end.TenantSpecificViewIndexSaltedIT12000
end2end.index.ChildViewsUseParentViewIndexIT2000
end2end.ReadOnlyViewOnReadOnlyIT2000

Other EXPLAIN-asserting suites

SuiteTestsFailuresErrorsSkippedNotes
end2end.InListIT228000point lookup, data table, only candidate
end2end.ExplainPlanWithStatsEnabledIT28000NO_INDEX hint paths; estimate parsing
end2end.DeleteIT74000
end2end.SequenceIT56000
end2end.Bson4IT32006range scan / point lookup attributes
end2end.join.SubqueryIT36000audited — data-table-only (not annotated)
end2end.join.SubqueryUsingSortMergeJoinIT21000audited — data-table-only (not annotated)
end2end.UnionAllIT19000
end2end.CursorWithRowValueConstructorIT18000first-line scan assertion only
end2end.json.JsonFunctionsIT15000more bound PK columns, data table
end2end.QueryLoggerIT7000logged vs. fresh EXPLAIN (self-consistent)
end2end.ReverseScanIT4000more bound PK columns, data table
end2end.UpgradeNamespaceIT2000

Pre-existing failures (not introduced by this change)

end2end.index.LocalIndexIT reports 4 errors in testLocalIndexReverseScanShouldReturnAllRows and testLocalIndexUsedForUncoveredOrderBy (each ×isNamespaceMapped), all StackOverflowError. Tracked by PHOENIX-7893.

@apurtell
apurtell merged commit d43231c into apache:PHOENIX-7876-featureJun 11, 2026
@apurtell
apurtell deleted the PHOENIX-7891 branch June 11, 2026 00:21
apurtell added a commit to apurtell/phoenix that referenced this pull request Jun 17, 2026
…pache#2513)
Co-authored-by: Claude Opus 4.8[1m] <noreply@anthropic.com>
asf-gitbox-commits pushed a commit that referenced this pull request Jul 24, 2026
…2513)
Co-authored-by: Claude Opus 4.8[1m] <noreply@anthropic.com>
apurtell added a commit to apurtell/phoenix that referenced this pull request Aug 4, 2026
…pache#2513)
Co-authored-by: Claude Opus 4.8[1m] <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@apurtell