Skip to content

refactor(substrait): pass immutable scope context through consumers - #26890

Merged
gabotechs merged 9 commits into
apache:mainfrom
alexanderbianchi:codex/substrait-explicit-context
Nov 10, 2026
Merged

gabotechs merged 9 commits into
apache:mainfrom
alexanderbianchi:codex/substrait-explicit-context

Conversation

@alexanderbianchi

@alexanderbianchi alexanderbianchi commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Rationale for this change

Lambda and outer-query scopes currently live in mutable stacks on the Substrait consumer. Passing an explicit context makes each conversion's scope visible and removes the need to push and pop state around nested conversions.

This PR keeps input consumption sequential. Parallel consumption and its performance evaluation can be handled separately.

What changes are included in this PR?

  • Add SubstraitConsumerContext and pass it through relation and expression callbacks and conversion helpers.
  • Derive child contexts when entering lambda or outer-query scopes; the parent stays unchanged on success, error, or cancellation.
  • Support custom scoped values through with_extension / get_extension, using DataFusion’s typed Extensions container. Child scopes inherit values and can override them without changing parents or siblings.
  • Remove scope stacks and their push/pop methods from the consumer, including DefaultSubstraitLambdaConsumer.
  • Preserve existing sequential input consumption.
  • Keep plan-level entrypoint signatures unchanged and document custom-consumer migration in the DataFusion 57.0.0 upgrade guide.

Testing

Existing Substrait tests cover relation conversion, correlated subqueries, and lambda behavior. Direct context tests cover scope inheritance and sibling isolation; a roundtrip execution test covers differently typed sibling lambdas and nested captures. Concurrency-only tests and their fixture have been removed.

Validation passed: Rust and Markdown formatting, workspace Clippy with all targets and features (warnings denied), 68 Substrait unit tests, 230 integration tests (6 ignored), and 3 doctests.

Are there any user-facing changes?

Breaking API change: custom relation/expression callbacks and direct conversion-helper calls must accept and forward &SubstraitConsumerContext. The upgrade guide explains how to replace the removed scope methods.

Query results and the default sequential scheduling policy are preserved. Generated lambda parameter names can differ in plan displays because independent lexical scopes may reuse names.

Custom consumers can keep their scoped values in the context using typed extensions. Values are shared through Arc; child-local changes replace a value. Interior mutable state is still shared, so opting into concurrency remains the custom consumer’s responsibility.

@github-actions github-actions Bot added substrait Changes to the substrait crate auto detected api change Auto detected API change labels Oct 29, 2026
@alexanderbianchi
alexanderbianchi marked this pull request as ready for review November 3, 2026 17:28
@alexanderbianchi

Copy link
Copy Markdown
Contributor Author

@gabotechs This PR is fairly straightforward, even if the diff is scary. The main change is in datafusion/substrait/src/logical_plan/consumer/substrait_consumer.rs and the rest are updating function signatures + callers.

If we agree that structure makes sense, the 2 remaining question are

  1. Should we do parallelization here or leave that to the user?
  2. Do we still want the multi-input consume function from feat(substrait): add a hook for consuming relation inputs #26603?

Comment thread datafusion/substrait/README.md Resolved
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Nov 6, 2026

@gabotechs gabotechs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good! for this PR, I'd just remove the consume_rel_inputs hook, and I think it should be ready to go.

/// The default consumes inputs sequentially, stopping at the first error.
/// Overrides can provide a different scheduling policy, but must account for
/// any additional mutable traversal state in the custom consumer.
async fn consume_rel_inputs(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm still not convinced about this change. For the sake of this PR, I'd remove it, and we can discuss in a follow up

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment on lines 572 to 574
pub(super) state: &'a SessionState,
outer_schemas: RwLock<Vec<Arc<DFSchema>>>,
lambda_consumer: DefaultSubstraitLambdaConsumer,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice!

assert!(consumer.get_outer_schema(0).is_none());
assert!(consumer.get_outer_schema(1).is_none());
assert!(consumer.get_outer_schema(2).is_none());
fn extensions_inherit_and_override_without_changing_other_scopes() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤔 these tests were pretty coupled to implementation details, and now they are getting in the way rather than helping.

No action required in this PR, but if you find yourself in the position of adding new test, rather than contributing to these ones, I'd try to just test through the public API.

Comment on lines 561 to 562
@@ -545,7 +558,7 @@ mod tests {
assert_snapshot!(
plan,
@"
Projection: array_transform2(make_array(make_array(data3.p1)), (p0, p2) -> array_concat(array_transform2(p0, (p3, p4) -> p3 * p2 * p4), array_transform2(p0, (p5, p6) -> p5 * p2 * p6))) AS array_transform2(make_array(make_array(data3.p1)),(v, i) -> array_concat(array_transform2(v,(v, j) -> v * i * j),array_transform2(v,(v, j) -> v * i * j)))
Projection: array_transform2(make_array(make_array(data3.p1)), (p0, p2) -> array_concat(array_transform2(p0, (p3, p4) -> p3 * p2 * p4), array_transform2(p0, (p3, p4) -> p3 * p2 * p4))) AS array_transform2(make_array(make_array(data3.p1)),(v, i) -> array_concat(array_transform2(v,(v, j) -> v * i * j),array_transform2(v,(v, j) -> v * i * j)))
TableScan: data3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤔 at first I thought there was something wrong with this snapshot change, but it does look correct, just that the argument numeration is not global to whole plan, but to the specific plan branch in which that array_transform happened to fall.

nested conversions. There is no matching pop operation; the parent context stays
unchanged on success, error, and cancellation.

Input consumption remains sequential. Custom consumers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks like the classical LLM correction over a previous state of this PR that is not going to be shipped. I'd revisit the upgrade guide.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, branch-56 was already cut, and this PR is not there, so this might belong to the 57 upgrade guide instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think humans also screw this up 😆

@alexanderbianchi
alexanderbianchi force-pushed the codex/substrait-explicit-context branch from c889abf to 861b014 Compare November 10, 2026 14:52

@gabotechs gabotechs left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All good here, thanks @alexanderbianchi and @LiaCastaneda!

@github-actions github-actions Bot removed the auto detected api change Auto detected API change label Nov 10, 2026
@gabotechs
gabotechs added this pull request to the merge queue Nov 10, 2026
Merged via the queue into apache:main with commit 8fbcc00 Nov 10, 2026
45 of 46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation substrait Changes to the substrait crate v57.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants