{"record":{"id":"dbcfe71d9b72b4e5","repo":"risingwavelabs/risingwave","slug":"method-not-available-for-logicalmultijoin-which","errorCode":null,"errorMessage":"Method not available for `LogicalMultiJoin` which is a placeholder node with a temporary lifetime. It only facilitates join reordering during logical planning.","messagePattern":"Method not available for `LogicalMultiJoin` which is a placeholder node with a temporary lifetime\\. It only facilitates join reordering during logical planning\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_multi_join.rs","lineNumber":814,"sourceCode":"    left: Option<Box<JoinTreeNode>>,\n    right: Option<Box<JoinTreeNode>>,\n    height: usize,\n}\n\n// join graph internal representation\n#[derive(Clone, Debug)]\nstruct GraphNode {\n    id: usize,\n    join_tree: JoinTreeNode,\n    relations: BTreeSet<usize>,\n}\n\nimpl ToStream for LogicalMultiJoin {\n    fn logical_rewrite_for_stream(\n        &self,\n        _ctx: &mut RewriteStreamContext,\n    ) -> Result<(PlanRef, ColIndexMapping)> {\n        panic!(\n            \"Method not available for `LogicalMultiJoin` which is a placeholder node with \\\n             a temporary lifetime. It only facilitates join reordering during logical planning.\"\n        )\n    }\n\n    fn to_stream(\n        &self,\n        _ctx: &mut ToStreamContext,\n    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {\n        panic!(\n            \"Method not available for `LogicalMultiJoin` which is a placeholder node with \\\n             a temporary lifetime. It only facilitates join reordering during logical planning.\"\n        )\n    }\n}\n\nimpl ToBatch for LogicalMultiJoin {\n    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {","sourceCodeStart":796,"sourceCodeEnd":832,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_multi_join.rs#L796-L832","documentation":"`LogicalMultiJoin::logical_rewrite_for_stream` panics because `LogicalMultiJoin` is only a temporary placeholder used during join-order enumeration in logical planning; it is always replaced by concrete join nodes before the to-stream rewrite phase. Any call means a `LogicalMultiJoin` escaped the join-reordering stage.","triggerScenarios":"Calling `logical_rewrite_for_stream` on a plan tree still containing `LogicalMultiJoin`, i.e. join reordering (`dphyp`/greedy join ordering) did not run or did not replace the node.","commonSituations":"Optimizer configuration or feature flags that disable join reordering; tests or tools building `LogicalMultiJoin` directly and running stream conversion; regressions in the join-order pass.","solutions":["Ensure join reordering runs and replaces `LogicalMultiJoin` with concrete joins before stream rewriting (check optimizer pass ordering/flags).","Do not construct or persist `LogicalMultiJoin` outside the join-reordering stage in custom code or tests.","If a normal multi-way JOIN query triggers it, capture the SQL and report an optimizer bug upstream."],"exampleFix":"// before: placeholder node survives into stream rewrite\nlet (plan, _) = multi_join.logical_rewrite_for_stream(ctx)?; // panics\n// after: run join reordering first to replace the placeholder\nlet plan = reorder_joins(multi_join)?; // produces LogicalJoin tree\nlet (plan, _) = plan.logical_rewrite_for_stream(ctx)?;","handlingStrategy":"type-guard","validationCode":"debug_assert!(!plan_contains::<LogicalMultiJoin>(&plan), \"LogicalMultiJoin placeholder must be consumed by join reordering\");","typeGuard":"fn is_multi_join(node: &PlanNode) -> bool { matches!(node, PlanNode::LogicalMultiJoin(_)) }","tryCatchPattern":"let (plan, _) = node.logical_rewrite_for_stream(ctx)\n    .unwrap_or_else(|e| panic!(\"MultiJoin placeholder survived; run join reordering first: {e}\"));","preventionTips":["Never disable the join-reordering pass before stream planning of multi-way joins.","Treat `LogicalMultiJoin` as private to the enumeration stage; emit concrete join nodes in any custom plan-building code."],"tags":["rust","stream-optimizer","join-reordering","panic"],"backgroundTag":"method-not-implemented","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"}