Skip to content

Skip map_expressions rebuild for Extension nodes with empty expressions #21700

@zhuqi-lucas

Description

@zhuqi-lucas

Is your feature request related to a problem or challenge?

When an Extension node (e.g. view matching OneOf) has many children but no expressions, LogicalPlan::map_expressions still clones all inputs and calls with_exprs_and_inputs to reconstruct the node — wasted work since there are no expressions to transform.

In our production system with view matching (5 MV candidates × ~15 optimizer rules), this unnecessary rebuild is the dominant cost of the optimizer pass for plans containing Extension nodes.

Describe the solution you'd like

Add an early return in map_expressions when node.expressions() is empty:

LogicalPlan::Extension(Extension { node }) => {
    let raw_exprs = node.expressions();
    if raw_exprs.is_empty() {
        Transformed::no(LogicalPlan::Extension(Extension { node }))
    } else {
        // existing clone + rebuild path
    }
}

Describe alternatives you've considered

A more general optimization would be to change UserDefinedLogicalNode::expressions() to return references (&[Expr]) instead of cloned Vec<Expr>, and only clone + rebuild when the transform actually modifies an expression. This would avoid the clone + with_exprs_and_inputs rebuild even for non-empty expression lists when the transform is a no-op. This would be a larger API change affecting all UserDefinedLogicalNode implementations, so the empty-expressions shortcut is a pragmatic first step.

Additional context

Benchmark results (criterion micro-benchmark):

Children no_expr (optimized) with_expr (rebuild) Speedup
1 24 ns 167 ns 7x
3 23 ns 192 ns 8x
5 23 ns 181 ns 8x
10 24 ns 216 ns 9x

The no_expr path is constant time regardless of children count.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions