{"record":{"id":"ba99d5ca7436a9f3","repo":"risingwavelabs/risingwave","slug":"logical-join-should-store-predicate-as-condition","errorCode":null,"errorMessage":"logical join should store predicate as Condition","messagePattern":"logical join should store predicate as Condition","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_join.rs","lineNumber":133,"sourceCode":"\n    pub fn internal_column_num(&self) -> usize {\n        self.core.internal_column_num()\n    }\n\n    pub fn i2l_col_mapping_ignore_join_type(&self) -> ColIndexMapping {\n        self.core.i2l_col_mapping_ignore_join_type()\n    }\n\n    pub fn i2r_col_mapping_ignore_join_type(&self) -> ColIndexMapping {\n        self.core.i2r_col_mapping_ignore_join_type()\n    }\n\n    /// Get a reference to the logical join's on.\n    pub fn on(&self) -> &Condition {\n        self.core\n            .on\n            .as_condition_ref()\n            .expect(\"logical join should store predicate as Condition\")\n    }\n\n    pub fn core(&self) -> &generic::Join<PlanRef> {\n        &self.core\n    }\n\n    /// Collect all input ref in the on condition. And separate them into left and right.\n    pub fn input_idx_on_condition(&self) -> (Vec<usize>, Vec<usize>) {\n        let input_refs = self\n            .core\n            .on\n            .as_condition_ref()\n            .expect(\"logical join should store predicate as Condition\")\n            .collect_input_refs(self.core.left.schema().len() + self.core.right.schema().len());\n        let index_group = input_refs\n            .ones()\n            .chunk_by(|i| *i < self.core.left.schema().len());\n        let left_index = index_group","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_join.rs#L115-L151","documentation":"LogicalJoin::on() expects the generic JoinOn core to hold a full Condition and panics with expect() if it does not (e.g. it holds only an EqPredicate representation). The invariant is that after planning, a logical join's predicate is stored as a Condition.","triggerScenarios":"Calling on() on a LogicalJoin whose core.on was constructed as generic::JoinOn::EqPredicate (e.g. joins created internally by temporal/asof join paths) rather than Condition.","commonSituations":"Optimizer rule authors or index-lookup/temporal rewrite passes building joins with JoinOn::EqPredicate and later calling .on(); version changes where JoinOn representation was refactored.","solutions":["Construct the join core with generic::JoinOn::Condition(cond) instead of JoinOn::EqPredicate","Before calling .on(), check as_condition_ref() and fall back to converting the eq-predicate to a Condition","Use the appropriate accessor (e.g. as_eq_predicate_ref) when you know the representation","Report as an optimizer bug if the join came from the standard planner path"],"exampleFix":"// before\nlet core = generic::Join { on: generic::JoinOn::EqPredicate(pred), .. };\nlet cond = join.on();\n// after\nlet core = generic::Join { on: generic::JoinOn::Condition(Condition::with_exprs(pred.into_iter().collect())), .. };\nlet cond = join.on();","handlingStrategy":"type-guard","validationCode":"if join.core().on.as_condition_ref().is_none() { /* build or convert to Condition first */ }","typeGuard":"fn on_condition(join: &LogicalJoin) -> Option<&Condition> { join.core().on.as_condition_ref() }","tryCatchPattern":null,"preventionTips":["Always construct generic::JoinOn::Condition when creating logical joins","Use accessor helpers instead of reaching into core.on directly","Add debug assertions in rule code verifying the Condition representation"],"tags":["optimizer","join","invariant","panic"],"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"}