risingwavelabs/risingwave · error

shared node should be handled specially in PlanRef::clone_wi

Error message

shared node should be handled specially in PlanRef::clone_with_input

What it means

`LogicalShare::clone_with_input` unconditionally panics via `unreachable!()`. LogicalShare is a shared (common-subplan) node; cloning it with a fresh input would break the share semantics, so the framework is supposed to handle shared nodes specially in `PlanRef::clone_with_input` rather than call this method. Reaching it means a PlanTreeNode code path bypassed the share-aware handling.

Source

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

        Self::with_core(ctx.logical_share(share_id))
    }

    pub fn create(input: PlanRef) -> PlanRef {
        LogicalShare::new(input).into()
    }

    pub(super) fn pretty_fields(base: impl GenericPlanRef, name: &str) -> XmlNode<'_> {
        childless_record(name, vec![("id", Pretty::debug(&base.id().0))])
    }
}

impl PlanTreeNodeUnary<Logical> for LogicalShare {
    fn input(&self) -> PlanRef {
        self.core.input()
    }

    fn clone_with_input(&self, _input: PlanRef) -> Self {
        unreachable!("shared node should be handled specially in PlanRef::clone_with_input")
    }

    fn rewrite_with_input(
        &self,
        input: PlanRef,
        input_col_change: ColIndexMapping,
    ) -> (Self, ColIndexMapping) {
        (Self::new(input), input_col_change)
    }
}

impl_plan_tree_node_for_unary! { Logical, LogicalShare}

impl ShareNode<Logical> for LogicalShare {
    fn share_id(&self) -> ShareId {
        self.core.share_id()
    }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Do not call `clone_with_input` on a shared node; use the share-aware `PlanRef::clone_with_input` entry point which special-cases LogicalShare.
  2. If you are writing an optimizer rule, clone the LogicalShare's underlying `LogicalPlanRef` core and construct a new LogicalShare instead of treating it as a plain unary node.
  3. If this fires from stock rules, file a RisingWave bug with the explain output of the query.

Example fix

// before
let cloned = shared_node.clone_with_input(new_input);
// after
let cloned = PlanRef::from(shared_node.clone()).clone_with_input(new_input); // share-aware path
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_share(plan: &PlanRef) -> bool { plan.as_logical_share().is_some() }

Type guard

fn as_non_share(plan: PlanRef) -> Option<PlanRef> {
    if plan.as_logical_share().is_some() { None } else { Some(plan) }
}

Try / catch

// Panic cannot be caught in Rust; guard the call site instead.
assert!(!is_share(&node), "use share-aware PlanRef::clone_with_input");

Prevention

When it happens

Trigger: Any optimizer rule or rewrite that calls `clone_with_input` on a `PlanRef` wrapping a LogicalShare directly (e.g. a generic unary-node transformation) instead of routing through the share-aware `PlanRef::clone_with_input`.

Common situations: Seen by RisingWave contributors while writing new optimizer rules (column pruning, predicate pushdown, merge rules) that clone/rebuild plan trees and forgot that common-subplan elimination inserts LogicalShare nodes.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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