feat!: id-based outer-reference resolution (Substrait 0.89.0)#982
Open
nielspardon wants to merge 4 commits into
Open
feat!: id-based outer-reference resolution (Substrait 0.89.0)#982nielspardon wants to merge 4 commits into
nielspardon wants to merge 4 commits into
Conversation
08eae97 to
4e98b39
Compare
Update the substrait submodule to v0.89.0 and model its new id-based outer-reference mechanism alongside the existing offset-based one: - RelCommon.rel_anchor -> Rel.getRelAnchor() / Rel.withRelAnchor(...) - OuterReference.rel_reference -> FieldReference.outerReferenceRelReference() steps_out and rel_reference share a single OuterReference oneof, so they are mutually exclusive: a FieldReference carries at most one (enforced by FieldReference#check()), and both proto converters emit/read whichever form is set. Add OuterReferenceConverter to rewrite a plan's outer references from one form to the other (toIdBased / toStepsOut) so an integration can work in a single encoding regardless of which form a plan arrives in. Assigning plan-wide unique rel_anchors requires whole-plan context, so this lives in core rather than being duplicated per integration. BREAKING CHANGE: Rel gains an abstract getRelAnchor(); non-Immutables Rel implementations must implement it. withRelAnchor(...) is added as a throwing default method that the generated Immutables override. Signed-off-by: Niels Pardon <par@zurich.ibm.com>
Wrap isthmus conversion with the core OuterReferenceConverter: - Producing: convert offset-based outer references (steps_out) emitted by the depth-based visitor into the id-based form (rel_anchor / rel_reference). - Consuming: normalize id-based plans back to steps_out before the existing depth-based correlation logic runs, so both encodings are accepted while that logic stays unchanged. Offset-based plans pass through untouched. Calcite's correlation model is itself id-based (CorrelationId), so this aligns isthmus' external interface with the direction Substrait is migrating towards without touching its correlation wiring. SubstraitRepeatRel, a non-Immutables test Rel, now implements the new Rel.getRelAnchor() accessor. Signed-off-by: Niels Pardon <par@zurich.ibm.com>
4e98b39 to
a46f546
Compare
Replace the steps_out bridge with native id-based correlation handling so isthmus no longer works on offset-based outer references internally. Producing: OuterReferenceResolver now maps each Calcite CorrelationId to a plan-wide rel_anchor and its binding relation. SubstraitRelVisitor stamps that anchor on the binding relation and RexExpressionConverter emits rel_reference directly. The offset-based depth computation and the OuterReferenceConverter.toIdBased post-pass are removed. Consuming: SubstraitRelNodeConverter.Context resolves rel_reference by anchor, dropping the subquery-depth arithmetic. Incoming offset-based plans are normalized to the id-based form once at the boundary (OuterReferenceConverter.toIdBased), so both encodings are still accepted while the internal machinery is purely id-based. Calcite's CorrelationId is itself an identity bound to a single relation, so id-based resolution is a closer and simpler fit than the depth model it replaces. BREAKING CHANGE: OuterReferenceResolver no longer computes a RexFieldAccess->depth map; its apply(RelNode) is replaced by resolve(RelNode) plus anchorForCorrelationId/anchorForTarget. SubstraitRelVisitor.popFieldAccessDepthMap/getFieldAccessDepth are replaced by resolveOuterReferences/getOuterReferenceAnchor, and the SubstraitRelNodeConverter.Context correlation API is now anchor-based. Signed-off-by: Niels Pardon <par@zurich.ibm.com>
3d42703 to
3760af7
Compare
Address review feedback on the id-based outer-reference work: - core: OuterReferenceConverter clears currentInput inside a subquery scope so a correlated reference reached through a multi-input host (join/set) is rejected as unsupported instead of silently binding to the wrong relation; share one anchor counter across all plan roots so assigned rel_anchors are unique plan-wide. - core: simplify FieldReference#check() to an int sum and drop fully-qualified names in OuterReferenceConverter. - isthmus: reject duplicate rel_anchors on consume, surface an unresolvable rel_reference as UnsupportedOperationException (consistent with core), and report an actionable error when a custom Rel binding target cannot carry an anchor. Add regression tests for multi-root anchor uniqueness, multi-input scope rejection, duplicate-anchor rejection, and custom-Rel anchor support.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Updates the
substraitsubmodule to v0.89.0 and adds support for its new id-based outer-reference resolution (RelCommon.rel_anchor+OuterReference.rel_reference) alongside the existing offset-basedsteps_outmechanism, then makes the isthmus integration resolve correlations natively by id.Implements #869.
Core (
feat(core)!)Rel.getRelAnchor()/Rel.withRelAnchor(...)andFieldReference.outerReferenceRelReference().steps_outandrel_referenceare a single protobufoneof, so they are mutually exclusive — aFieldReferencecarries at most one (enforced byFieldReference#check()), and both proto converters emit/read whichever form is set. (Unlike the URI→URN case in the breaking-change policy, these are not parallel fields, so there is no "dual write / prefer" behavior.)OuterReferenceConverterrewrites a plan's outer references from one form to the other (toIdBased/toStepsOut). Assigning plan-wide-uniquerel_anchors needs whole-plan context, so it lives in core.Isthmus (
feat(isthmus)→feat(isthmus)!)Calcite's correlation model is itself id-based (
CorrelationIdis an identity bound to one relation), so isthmus now resolves outer references natively by anchor rather than by subquery depth:OuterReferenceResolvermaps eachCorrelationIdto a plan-widerel_anchorand its binding relation;SubstraitRelVisitorstamps the anchor on that relation andRexExpressionConverteremitsrel_referencedirectly. The offset-based depth computation is gone.SubstraitRelNodeConverter.Contextresolvesrel_referenceby anchor (no subquery-depth arithmetic). Offset-based (steps_out) plans are still accepted — they are normalized to the id-based form once at the conversion boundary, so the internal machinery is purely id-based.This replaces the earlier depth/
steps_outbridge with a closer, simpler fit to Calcite's own model.Breaking changes
Relgains an abstractgetRelAnchor(); non-ImmutablesRelimplementations must implement it (withRelAnchor(...)is a throwing default the Immutables override).OuterReferenceResolver.apply→resolve+anchorForCorrelationId/anchorForTarget;SubstraitRelVisitor.popFieldAccessDepthMap/getFieldAccessDepth→resolveOuterReferences/getOuterReferenceAnchor; theSubstraitRelNodeConverter.Contextcorrelation API is now anchor-based.Known limitation
OuterReferenceConvertersupports outer references whose binding relation is the input of a single-input host (Filter/Project), covering correlated scalar/IN/EXISTS subqueries. A correlated reference into a multi-input (join/set) scope, or a shared subtree (ReferenceRel), throwsUnsupportedOperationException. isthmus does not produce those shapes.Testing
rel_anchor,OuterReferenceConverter(round-trip identity, nesting, shared-scope dedup, unsupported cases), plus a mutual-exclusivity test.SubqueryConversionTestcovers native id-based consume and offset-based (boundary-normalized) consume;SubqueryPlanTestcovers id-based produce;OuterReferenceResolverTestcovers anchor assignment and the dangling/partially-decorrelated case.:core:test,:isthmus:test(incl. TPC-H / TPC-DS correlated-subquery round-trips),:core:javadoc,:isthmus:javadoc,spotlessCheck, and Spark compilation all pass.🤖 Generated with AI