Repository navigation
refactor(substrait): pass immutable scope context through consumers - #26890
Conversation
|
@gabotechs This PR is fairly straightforward, even if the diff is scary. The main change is in If we agree that structure makes sense, the 2 remaining question are
|
gabotechs
left a comment
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
| pub(super) state: &'a SessionState, | ||
| outer_schemas: RwLock<Vec<Arc<DFSchema>>>, | ||
| lambda_consumer: DefaultSubstraitLambdaConsumer, | ||
| } | ||
|
|
| 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() { |
There was a problem hiding this comment.
🤔 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.
| @@ -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 | |||
There was a problem hiding this comment.
🤔 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Also, branch-56 was already cut, and this PR is not there, so this might belong to the 57 upgrade guide instead.
There was a problem hiding this comment.
I think humans also screw this up 😆
c889abf to
861b014
Compare
There was a problem hiding this comment.
All good here, thanks @alexanderbianchi and @LiaCastaneda!
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?
SubstraitConsumerContextand pass it through relation and expression callbacks and conversion helpers.with_extension/get_extension, using DataFusion’s typedExtensionscontainer. Child scopes inherit values and can override them without changing parents or siblings.DefaultSubstraitLambdaConsumer.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.