Skip to content

sql: Refactor MapFilterProject, MfpPlan, and SafeMfpPlan to have parameter with trait#36759

Merged
mgree merged 2 commits into
MaterializeInc:mainfrom
mgree:stable-lir-mfp-refactor
Jun 1, 2026
Merged

sql: Refactor MapFilterProject, MfpPlan, and SafeMfpPlan to have parameter with trait#36759
mgree merged 2 commits into
MaterializeInc:mainfrom
mgree:stable-lir-mfp-refactor

Conversation

@mgree
Copy link
Copy Markdown
Contributor

@mgree mgree commented May 27, 2026

Motivation

Second PR pulled from #36544.

Description

MapFilterProject (and its friends, MfpPlan and SafeMfpPlan---the "MFP" family) are used at both the MIR and LIR level. To separate LIR cleanly from MIR, we need to be able to work with MFPs at both levels.

This change parameterizes these types and introduces a trait OptimizableExpr that allows us to work independently of the MIR type. It is not the prettiest trait, but I don't see how to work around this (without a much more intensive overhaul of how we process MFP-like structures in LIR). See also: https://linear.app/materializeinc/issue/SQL-319/mfp-like-structures-in-lir-are-optimized-late.

Verification

Pure refactor, CI green.

@mgree mgree changed the title refactor: Parameterize MapFilterProject, MfpPlan, and SafeMfpPlan; add trait sql: Refactor MapFilterProject, MfpPlan, and SafeMfpPlan to have parameter with trait May 27, 2026
@mgree mgree marked this pull request as ready for review May 27, 2026 21:15
@mgree mgree requested a review from a team as a code owner May 27, 2026 21:15
@ggevay
Copy link
Copy Markdown
Contributor

ggevay commented May 28, 2026

Triggered the random query subset of Nightly: https://buildkite.com/materialize/nightly/builds/16607

Copy link
Copy Markdown
Contributor

@ggevay ggevay left a comment

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

Comment thread src/expr/src/linear.rs
expr.visit_pre(|e| {
if let MirScalarExpr::Column(i, _name) = e {
reference_count[*i] += 1;
expr.visit_pre(&mut |e| {
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.

Minor thing: originally, this was MSE's visit_pre, which is written iteratively (the worklist stuff) to avoid recursion limit issues, while the new code is calling the Visit trait's visit_pre, which is Stacker-based. So, we now could newly panic on a recursion limit error. I'm wondering if the Visit trait's implementation could also be changed to be iterative.

(And the same at the below visit_pre call.)

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.

There are a bunch of deprecated things here... is there any reason not to move everything to iterative work?

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.

#36852 takes a cut at iterative visitors. This took me longer than I expected, mostly because post-order mutable traversal requires unsafe rust.

In the meantime, I made it so we can override visit_pre. It should go away once we have the iterative refactor, though.

@mgree mgree force-pushed the stable-lir-mfp-refactor branch from 88e78df to 14f9369 Compare June 1, 2026 21:08
@mgree mgree enabled auto-merge (squash) June 1, 2026 21:09
@mgree mgree merged commit 40e5dd1 into MaterializeInc:main Jun 1, 2026
119 checks passed
@def-
Copy link
Copy Markdown
Contributor

def- commented Jun 2, 2026

I'm not sure this is important enough to be considered a bug, but it's definitely a regression, the query worked fine before, now errors:

mfp-deep-expr-recursion-limit.td:27:1: error: preparing query failed: db error: ERROR: exceeded recursion limit of 1024

with this .td file:

> CREATE TABLE t (a int4);

> EXPLAIN OPTIMIZED PLAN FOR SELECT a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a + a FROM t;

@ggevay
Copy link
Copy Markdown
Contributor

ggevay commented Jun 2, 2026

Thanks, @def- ! We'll fix this as a followup, as part of some more visitor changes.

@mgree
Copy link
Copy Markdown
Contributor Author

mgree commented Jun 3, 2026

Unless things are different in SLT from TD, I think this is a bug in the parser.

    Bail:test/sqllogictest/scalar_deep.slt:14 PlanFailure:test/sqllogictest/scalar_deep.slt:14:
    db error: ERROR: exceeded recursion limit of 1024   
    backtrace:
      0: std::backtrace_rs::backtrace::libunwind::trace
                 at /rustc/ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9
       1: std::backtrace_rs::backtrace::trace_unsynchronized::<<std::backtrace::Backtrace>::create::{closure#0}>
                 at /rustc/ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14
       2: <std::backtrace::Backtrace>::create
                 at /rustc/ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96/library/std/src/backtrace.rs:331:13
       3: mz_ore::stack::RecursionGuard::descend
                 at ./src/ore/src/stack.rs:243:29
       4: mz_ore::stack::CheckedRecursion::checked_recur_mut
                 at ./src/ore/src/stack.rs:205:32
       5: mz_sql::plan::transform_ast::Desugarer::visit_internal
                 at ./src/sql/src/plan/transform_ast.rs:580:31
       6: <mz_sql::plan::transform_ast::Desugarer as mz_sql_parser::ast::visit_mut::VisitMut<mz_sql::names::Aug>>::visit_expr_mut
                 at ./src/sql/src/plan/transform_ast.rs:568:14
       7: mz_sql_parser::ast::visit_mut::visit_expr_mut
                 at ./target/debug/build/mz-sql-parser-fb536d7038cbfd96/out/visit_mut.rs:4413:21
       8: mz_sql::plan::transform_ast::Desugarer::visit_expr_mut_internal
                 at ./src/sql/src/plan/transform_ast.rs:869:9
       9: core::ops::function::Fn::call
                 at /Users/michaelgreenberg/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
      10: mz_sql::plan::transform_ast::Desugarer::visit_internal::{{closure}}
                 at ./src/sql/src/plan/transform_ast.rs:580:53
      11: mz_ore::stack::CheckedRecursion::checked_recur_mut::{{closure}}
                 at ./src/ore/src/stack.rs:206:33
      12: stacker::maybe_grow
                 at /Users/michaelgreenberg/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stacker-0.1.24/src/lib.rs:57:9
      13: mz_ore::stack::maybe_grow
                 at ./src/ore/src/stack.rs:101:5
      14: mz_ore::stack::CheckedRecursion::checked_recur_mut
                 at ./src/ore/src/stack.rs:206:19
      15: mz_sql::plan::transform_ast::Desugarer::visit_internal
                 at ./src/sql/src/plan/transform_ast.rs:580:31
      16: <mz_sql::plan::transform_ast::Desugarer as mz_sql_parser::ast::visit_mut::VisitMut<mz_sql::names::Aug>>::visit_expr_mut
                 at ./src/sql/src/plan/transform_ast.rs:568:14
      17: mz_sql_parser::ast::visit_mut::visit_expr_mut
                 at ./target/debug/build/mz-sql-parser-fb536d7038cbfd96/out/visit_mut.rs:4413:21
      18: mz_sql::plan::transform_ast::Desugarer::visit_expr_mut_internal
                 at ./src/sql/src/plan/transform_ast.rs:869:9
[snip 10818 frames]                 

Stack frame courtesy #36888.

@def-
Copy link
Copy Markdown
Contributor

def- commented Jun 3, 2026

Oops, you're right, this already failed before! My bad.

antiguru pushed a commit that referenced this pull request Jun 3, 2026
…meter with trait (#36759)

### Motivation

Second PR pulled from
#36544.

### Description

`MapFilterProject` (and its friends, `MfpPlan` and `SafeMfpPlan`---the
"MFP" family) are used at both the MIR and LIR level. To separate LIR
cleanly from MIR, we need to be able to work with MFPs at both levels.

This change parameterizes these types and introduces a trait
`OptimizableExpr` that allows us to work independently of the MIR type.
It is not the _prettiest_ trait, but I don't see how to work around this
(without a much more intensive overhaul of how we process MFP-like
structures in LIR). See also:
https://linear.app/materializeinc/issue/SQL-319/mfp-like-structures-in-lir-are-optimized-late.

### Verification

Pure refactor, CI green.
@mgree
Copy link
Copy Markdown
Contributor Author

mgree commented Jun 3, 2026

No worries! Absolutely could have been me. 😁

If this is something we'd like to keep track of, would it make sense to keep a file of known failing (but interesting and maybe eventually shouldn't fail) tests?

@def-
Copy link
Copy Markdown
Contributor

def- commented Jun 4, 2026

We have bugs for a bunch of them on github's database-issues. No one seems to have cared much, even when they regressed in the past.

antiguru pushed a commit that referenced this pull request Jun 4, 2026
### Motivation

Closes MaterializeInc/database-issues#3733.
Closes MaterializeInc/database-issues#3516.


#36759 (comment)
MaterializeInc/database-issues#9996

### Description

Changes `VisitChildren` to have `children()` and `children_mut()`
functions.

Changes `Visitor` to use these for iterative traversals.

NB that mutable post-traversal requires unsafety: we need an `&mut` for
the children and an `&mut` for the parent when we're done. This is
sound---Rust allows it when your stack is the call stack, but not your
own data structure. So: this is somewhat unsavory code.


### Verification

Green CI.

NB maintaing safety in `VisitChildren` means slightly changing visit
order: if we're going to work via `children_mut()`, we can no longer
yield all subqueries before the term itself. This seems to have affected
precisely one SLT, which I've rewritten.
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