Skip to content

Reapply iterative Visitor refactor (#36852) with SQL-332 fix#36903

Merged
antiguru merged 3 commits into
mainfrom
claude/sharp-pascal-JgbAV
Jun 8, 2026
Merged

Reapply iterative Visitor refactor (#36852) with SQL-332 fix#36903
antiguru merged 3 commits into
mainfrom
claude/sharp-pascal-JgbAV

Conversation

@antiguru

@antiguru antiguru commented Jun 4, 2026

Copy link
Copy Markdown
Member

Motivation

The iterative, child-based Visitor refactor (#36852) was reverted in #36905 because it introduced a regression: SQL-332, where mz_now() used as a direct argument to a value window function (e.g. first_value, last_value) or an aggregate window function (e.g. max) was no longer recognized as temporal, so the query failed to receive a timestamp near the current time. For example, SELECT first_value(mz_now()) OVER () < '3000-01-01' returned false instead of true.

This PR re-lands the refactor with that regression fixed, so it doesn't ship again.

Description

This PR consists of two commits:

  1. Reapply "refactor: Change all Visitors to be iterative, child-based" (refactor: Change all Visitors to be iterative, child-based #36852) — reverts the revert (Revert "refactor: Change all Visitors to be iterative, child-based" #36905), restoring the iterative traversal.

  2. Fix temporal detection (SQL-332) — the root cause of the regression. The iterative Visit traversals walk expressions via the new VisitChildren::children() / children_mut() iterators. For ValueWindowExpr and AggregateWindowExpr, the new children() impls descended into the argument expression and yielded its children, rather than yielding the argument expression itself — which is what the canonical visit_children does, and what the impl-level doc comments promise ("Yields args; does not descend into it").

    contains_temporal() runs over this traversal (via visit_post). When the argument is a leaf like mz_now() (a CallUnmaterializable with no children), descending skipped it entirely, so the mz_now() node was never visited and the query was planned as a constant.

    The fix yields the argument expression itself via std::iter::once(...), matching visit_children.

Verification

Green CI. Added regression tests in test/sqllogictest/temporal.slt covering mz_now() as the direct argument of first_value, last_value, and max window functions — each asserts the value is < '3000-01-01', confirming the query receives a current timestamp rather than u64::MAX.

https://claude.ai/code/session_017Xe6H3p7ZETe4LW5bqPiWZ

claude added 2 commits June 5, 2026 07:22
`first_value(mz_now()) OVER ()` (and `last_value`, aggregate window
functions) returned the wrong result because the query was not recognized
as time-dependent: `contains_temporal()` failed to find the `mz_now()` call.

The iterative `Visit` traversal introduced in #36852 walks expressions via
`VisitChildren::children()`. For `ValueWindowExpr` and `AggregateWindowExpr`
the new `children()`/`children_mut()` impls descended *into* the argument
expression and yielded its children, rather than yielding the argument
expression itself (as `visit_children` does, and as the impl doc comments
promise). When the argument is a leaf like `mz_now()` it has no children, so
it was never visited and temporal detection missed it.

Yield the argument expression itself, matching `visit_children`.

https://claude.ai/code/session_017Xe6H3p7ZETe4LW5bqPiWZ
@antiguru antiguru force-pushed the claude/sharp-pascal-JgbAV branch from a0b4de0 to f4ffcfb Compare June 5, 2026 07:24
@antiguru antiguru requested review from ggevay and mgree June 5, 2026 07:57
@antiguru antiguru marked this pull request as ready for review June 5, 2026 07:57
@antiguru antiguru requested review from a team as code owners June 5, 2026 07:58
@antiguru antiguru changed the title Fix temporal detection in window function arguments Reapply iterative Visitor refactor (#36852) with SQL-332 fix Jun 5, 2026
@ggevay

ggevay commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Triggered a Nightly subset: https://buildkite.com/materialize/nightly/builds/16708

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

LGTM if Nightly is green, just minor comments.

Comment thread src/expr/src/visit.rs Outdated
/// (default is to visit all children).
#[deprecated = "Use `visit_mut_pre_post` instead."]
fn visit_mut_pre_post_nolimit<F1, F2>(&mut self, pre: &mut F1, post: &mut F2)
/// It is improtant for safety that `pre` is (a) safe code and (b) returns children only.

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.

Typo in "improtant".

Comment thread src/sql/src/plan/hir.rs
{
f(&mut self.args)
}

@ggevay ggevay Jun 8, 2026

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.

Could the above visit_children, visit_mut_children, try_visit_children, try_visit_mut_children be deleted? Their trait defaults call children/children_mut, which should have the same behavior.

And the same applies for impl VisitChildren<HirScalarExpr> for AggregateWindowExpr {.

(But this doesn't apply to other things, e.g., impl VisitChildren<HirScalarExpr> for WindowExprType {, because there the explicit implementations are saving Vec allocations.)

- Fix "improtant" typo in expr/visit.rs.
- Drop the now-redundant explicit `visit_children`/`try_visit_*` impls for
  `ValueWindowExpr` and `AggregateWindowExpr`; the trait defaults delegate to
  the (now-correct) `children`/`children_mut`. `WindowExprType` keeps its
  explicit impls since those avoid Vec allocations.

https://claude.ai/code/session_017Xe6H3p7ZETe4LW5bqPiWZ
@antiguru

antiguru commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

Thanks for the review! Let's try again.

@antiguru antiguru merged commit fc2aaf0 into main Jun 8, 2026
117 checks passed
@antiguru antiguru deleted the claude/sharp-pascal-JgbAV branch June 8, 2026 13:53
Sign up for free to 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.

3 participants