Skip to content

datafusion-physical-expr: add DynamicFilterPhysicalExpr::update_from(Self) method - #26534

Open
jayshrivastava wants to merge 6 commits into
apache:mainfrom
jayshrivastava:js/dynamic-filter-update-from
Open

jayshrivastava wants to merge 6 commits into
apache:mainfrom
jayshrivastava:js/dynamic-filter-update-from

Conversation

@jayshrivastava

@jayshrivastava jayshrivastava commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

What changes are included in this PR?

This PR adds a helper similar to DynamicFilterPhysicalExpr::update() which takes a different DynamicFilterPhysicalExpr.

pub fn update_from(&self, source: &Self) -> Result<()> {

Rationale for this change

The use case I have is that I want to update a Arc<DynamicFilterPhysicalExpr> from a different Arc<DynamicFilterPhysicalExpr> (as opposed to the normal update() API which takes a non dynamic filter Arc<dyn PhysicalExpr>).

This requires accessing private fields like Inner. Rather than exposing private fields, I thought that adding an update_from(Self) API is cleaner.

Note that you cannot call

dynamic_filter_1.update(dynamic_filter_2.current())

because
a) it doesn't copy is_complete
b) it doesn't copy inner.expr directly. current() returns the expression with remapped columns and calling dynamic_filter_expr.update(remapped_expression); dynamic_filter_expr.current() breaks because current() cannot remap the remapped expression.

Alternatives Considered

  1. As a hack to get the private internal state, we currently incur a proto roundtrip, which makes fields like inner.expr and is_complete public. This is undesirable because it adds overhead
  2. Replace a dynamic filter entirely rather than mutate it. Currently datafusion has no API to "replace" a dynamic filter with another one in an ExecutionPlan. In a way, this is what update_from does, taking advantage of interior mutability.

What is the testing strategy for this PR?

One unit test

Are there any user-facing changes?

No.

@github-actions github-actions Bot added the physical-expr Changes to the physical-expr crates label Oct 20, 2026
@jayshrivastava
jayshrivastava marked this pull request as ready for review October 20, 2026 21:04
@jayshrivastava jayshrivastava changed the title wip datafusion-physical-expr: add DynamicFilterPhysicalExpr::update_from(Self) method Oct 20, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.94%. Comparing base (63af08d) to head (9f8db33).
⚠️ Report is 36 commits behind head on main.

Files with missing lines Patch % Lines
...ysical-expr/src/expressions/dynamic_filters/mod.rs 85.71% 2 Missing and 22 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #26534      +/-   ##
==========================================
+ Coverage   82.91%   82.94%   +0.02%     
==========================================
  Files        1155     1156       +1     
  Lines      458058   458717     +659     
  Branches   458058   458717     +659     
==========================================
+ Hits       379813   380489     +676     
+ Misses      55202    55148      -54     
- Partials    23043    23080      +37     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread datafusion/physical-expr/src/expressions/dynamic_filters/mod.rs Outdated

@rgbuilds rgbuilds 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.

Re-reviewed the latest changes locally. The completion-semantics concern is resolved, and the focused dynamic-filter, physical-plan, and proto round-trip tests pass, along with formatting and strict Clippy. LGTM.

@LiaCastaneda LiaCastaneda 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.

the solution makes sense to me, just had a few questions

let _ = self.state_watch.send(FilterState::Complete {
generation: current_generation,
});
self.state_watch.send_replace(current.state());

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.

Is there a reason to change send with send_replace? if its fixing something can we add a test?

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 just didn't like that we were throwing away the error. I figured there's no reason to not use send_replace instead. This way we buffer the update and let potentially late-arriving watchers to read it.

I'm happy to go back to send though if you feel strongly.


// Release the source lock before acquiring the target lock via clone().
let source_state = source.inner.read().clone();
if self.children != source.children {

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.

Why not just check the ID?

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.

Yeah that makes sense. The children should not be different. Deleted in favor of just the ID check.

lyne7-sc pushed a commit to lyne7-sc/datafusion that referenced this pull request Oct 23, 2026
## Which issue does this PR close?

- N/A

## Rationale for this change

The semver check previously compared the PR against the current state of
`apache/main`, and did so by doing a full `git` checkout, including the
entire repository history. This approach has two downsides:

1. It is expensive: cloning the entire repo history takes ~9 sec
locally, versus ~0.7 for a shallow checkout.
2. There's a race condition which results in comparing the PR against
the wrong base when assessing whether the PR breaks semantic versioning.

On (2): when a PR is opened or updated, GitHub creates a test merge
commit that merges the PR into main as of that moment (T0). Now suppose
that there is a delay between the CI jobs being queued and the semver
job actually running, so that when the semver job runs, `HEAD` is now at
T1. The previous version of this check would compare the PR with T1, not
T0. For example, if a commit between T0 and T1 added a new API, the
semver check would claim that the PR _removes_ that API.

Fix this by comparing the merge commit with its first parent instead,
which is T0 in the notation above.

## What changes are included in this PR?

See above.

## What is the testing strategy for this PR?

CI / dev tooling change only. Tested locally on six open PRs (apache#26565,
apache#26000, apache#26582, apache#25874, apache#26534, apache#26576): fetched each PR's merge ref at
depth 2 and ran both steps against `HEAD^1`. The crate lists and semver
results match CI, including two PRs that branched 66 and 154 commits
behind main.

## Are there any user-facing changes?

No.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-expr Changes to the physical-expr crates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants