risingwavelabs/risingwave · error

call predicate_pushdown of the PlanRef instead of calling di

Error message

call predicate_pushdown of the PlanRef instead of calling directly on LogicalShare

What it means

`LogicalShare::predicate_pushdown` is deliberately `unimplemented!()`. Predicate pushdown into a shared subplan must be routed through `PlanRef::predicate_pushdown`, which special-cases LogicalShare so predicates are duplicated/filtered above the share rather than pushed incorrectly into it. Calling the trait method directly breaks the API contract.

Source

Thrown at src/frontend/src/optimizer/plan_node/logical_share.rs:147

}

impl ColPrunable for LogicalShare {
    fn prune_col(&self, _required_cols: &[usize], _ctx: &mut ColumnPruningContext) -> PlanRef {
        unimplemented!("call prune_col of the PlanRef instead of calling directly on LogicalShare")
    }
}

impl ExprRewritable<Logical> for LogicalShare {}

impl ExprVisitable for LogicalShare {}

impl PredicatePushdown for LogicalShare {
    fn predicate_pushdown(
        &self,
        _predicate: Condition,
        _ctx: &mut PredicatePushdownContext,
    ) -> PlanRef {
        unimplemented!(
            "call predicate_pushdown of the PlanRef instead of calling directly on LogicalShare"
        )
    }
}

impl ToBatch for LogicalShare {
    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
        bail_not_implemented!("batch query doesn't support share operator for now");
    }
}

impl ToStream for LogicalShare {
    fn to_stream(
        &self,
        ctx: &mut ToStreamContext,
    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
        match ctx.get_to_stream_result(self.share_id()) {
            None => {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Call `PlanRef::from(share.clone()).predicate_pushdown(cond, ctx)` so the share-aware implementation runs.
  2. Keep predicate-pushdown code generic over `PlanRef` and avoid downcasting to LogicalShare before pushing predicates.
  3. If a built-in rule triggers this, file a RisingWave optimizer bug.

Example fix

// before
let pushed = logical_share.predicate_pushdown(cond, ctx);
// after
let pushed = PlanRef::from(logical_share.clone()).predicate_pushdown(cond, ctx);
Defensive patterns

Strategy: type-guard

Validate before calling

if node.as_logical_share().is_some() {
    let node = PlanRef::from(node.clone());
    // call node.predicate_pushdown(cond, ctx)
}

Type guard

fn pushable_ref(plan: PlanRef) -> PlanRef {
    assert!(plan.as_logical_share().is_none(), "predicate_pushdown via PlanRef only");
    plan
}

Prevention

When it happens

Trigger: Calling `predicate_pushdown(cond, ctx)` directly on a `LogicalShare` value instead of on a `PlanRef` wrapping it.

Common situations: Encountered by developers extending the predicate pushdown rules who downcast to LogicalShare and invoke the trait method, bypassing the share-aware dispatch.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/2dd8c1fd8b3237ba. Report an issue: GitHub.