{"record":{"id":"65b6f9ec09d638f0","repo":"risingwavelabs/risingwave","slug":"shared-node-should-be-handled-specially-in-planref","errorCode":null,"errorMessage":"shared node should be handled specially in PlanRef::clone_with_input","messagePattern":"shared node should be handled specially in PlanRef::clone_with_input","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_share.rs","lineNumber":87,"sourceCode":"        Self::with_core(ctx.logical_share(share_id))\n    }\n\n    pub fn create(input: PlanRef) -> PlanRef {\n        LogicalShare::new(input).into()\n    }\n\n    pub(super) fn pretty_fields(base: impl GenericPlanRef, name: &str) -> XmlNode<'_> {\n        childless_record(name, vec![(\"id\", Pretty::debug(&base.id().0))])\n    }\n}\n\nimpl PlanTreeNodeUnary<Logical> for LogicalShare {\n    fn input(&self) -> PlanRef {\n        self.core.input()\n    }\n\n    fn clone_with_input(&self, _input: PlanRef) -> Self {\n        unreachable!(\"shared node should be handled specially in PlanRef::clone_with_input\")\n    }\n\n    fn rewrite_with_input(\n        &self,\n        input: PlanRef,\n        input_col_change: ColIndexMapping,\n    ) -> (Self, ColIndexMapping) {\n        (Self::new(input), input_col_change)\n    }\n}\n\nimpl_plan_tree_node_for_unary! { Logical, LogicalShare}\n\nimpl ShareNode<Logical> for LogicalShare {\n    fn share_id(&self) -> ShareId {\n        self.core.share_id()\n    }\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_share.rs#L69-L105","documentation":"`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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Do not call `clone_with_input` on a shared node; use the share-aware `PlanRef::clone_with_input` entry point which special-cases LogicalShare.","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.","If this fires from stock rules, file a RisingWave bug with the explain output of the query."],"exampleFix":"// before\nlet cloned = shared_node.clone_with_input(new_input);\n// after\nlet cloned = PlanRef::from(shared_node.clone()).clone_with_input(new_input); // share-aware path\n","handlingStrategy":"type-guard","validationCode":"fn is_share(plan: &PlanRef) -> bool { plan.as_logical_share().is_some() }","typeGuard":"fn as_non_share(plan: PlanRef) -> Option<PlanRef> {\n    if plan.as_logical_share().is_some() { None } else { Some(plan) }\n}","tryCatchPattern":"// Panic cannot be caught in Rust; guard the call site instead.\nassert!(!is_share(&node), \"use share-aware PlanRef::clone_with_input\");","preventionTips":["Never downcast to LogicalShare before calling generic unary-node helpers.","Route all clone/rebuild operations through `PlanRef::clone_with_input`.","Add unit tests with common-subplan-eliminated plans when writing optimizer rules.","Read PlanTreeNodeUnary docs: shared nodes are excluded from the standard contract."],"tags":["rust","optimizer","unreachable","logical-share","common-subplan"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}