risingwavelabs/risingwave · error
logical join should store predicate as Condition
Error message
logical join should store predicate as Condition
What it means
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.
Source
Thrown at src/frontend/src/optimizer/plan_node/logical_join.rs:133
pub fn internal_column_num(&self) -> usize {
self.core.internal_column_num()
}
pub fn i2l_col_mapping_ignore_join_type(&self) -> ColIndexMapping {
self.core.i2l_col_mapping_ignore_join_type()
}
pub fn i2r_col_mapping_ignore_join_type(&self) -> ColIndexMapping {
self.core.i2r_col_mapping_ignore_join_type()
}
/// Get a reference to the logical join's on.
pub fn on(&self) -> &Condition {
self.core
.on
.as_condition_ref()
.expect("logical join should store predicate as Condition")
}
pub fn core(&self) -> &generic::Join<PlanRef> {
&self.core
}
/// Collect all input ref in the on condition. And separate them into left and right.
pub fn input_idx_on_condition(&self) -> (Vec<usize>, Vec<usize>) {
let input_refs = self
.core
.on
.as_condition_ref()
.expect("logical join should store predicate as Condition")
.collect_input_refs(self.core.left.schema().len() + self.core.right.schema().len());
let index_group = input_refs
.ones()
.chunk_by(|i| *i < self.core.left.schema().len());
let left_index = index_groupView on GitHub (pinned to 6469eb736d)
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
Example fix
// before
let core = generic::Join { on: generic::JoinOn::EqPredicate(pred), .. };
let cond = join.on();
// after
let core = generic::Join { on: generic::JoinOn::Condition(Condition::with_exprs(pred.into_iter().collect())), .. };
let cond = join.on(); Defensive patterns
Strategy: type-guard
Validate before calling
if join.core().on.as_condition_ref().is_none() { /* build or convert to Condition first */ } Type guard
fn on_condition(join: &LogicalJoin) -> Option<&Condition> { join.core().on.as_condition_ref() } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- expect fn
- core predicate must exist
- checked above
- Unprocessed shared node.
- internal error: entered unreachable code
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/ba99d5ca7436a9f3.
Report an issue: GitHub.