risingwavelabs/risingwave · critical

only inner join without non-equal condition is supported for

Error message

only inner join without non-equal condition is supported for delta joins

What it means

This is a panic (not a returned error) in the stream fragmenter's DeltaIndexJoin handling. Delta joins only support inner joins with pure equality conditions (no non-equal predicates); any other delta join shape hits an unreachable else branch and panics. It signals a planner/frontend invariant violation rather than a user-recoverable error.

Source

Thrown at src/frontend/src/stream_fragmenter/mod.rs:593

            NodeBody::LocalityProvider(_) => {
                current_fragment
                    .fragment_type_mask
                    .add(FragmentTypeFlag::LocalityProvider);
            }

            _ => {}
        };

        // handle join logic
        if let NodeBody::DeltaIndexJoin(delta_index_join) = stream_node.node_body.as_mut().unwrap()
        {
            if delta_index_join.get_join_type()? == JoinType::Inner
                && delta_index_join.condition.is_none()
            {
                return build_delta_join_without_arrange(state, current_fragment, stream_node);
            } else {
                panic!("only inner join without non-equal condition is supported for delta joins");
            }
        }

        // Usually we do not expect exchange node to be visited here, which should be handled by the
        // following logic of "visit children" instead. If it does happen (for example, `Share` will be
        // transformed to an `Exchange`), it means we have an empty fragment and we need to add a no-op
        // node to it, so that the meta service can handle it correctly.
        if let NodeBody::Exchange(_) = stream_node.node_body.as_ref().unwrap() {
            stream_node = state.gen_no_op_stream_node(stream_node);
        }

        // Visit plan children.
        stream_node.input = stream_node
            .input
            .into_iter()
            .map(|mut child_node| {
                match child_node.get_node_body()? {
                    // When exchange node is generated when doing rewrites, it could be having

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite the query/MV join to a pure inner equi-join, or move non-equal predicates to WHERE on a single side.
  2. Drop the index that triggers the delta join path so the join uses a regular hash join.
  3. File a bug with the query plan (EXPLAIN) — this panic indicates a missing planner validation.
  4. Use a plain materialized view (without index) for the join and filter on read.

Example fix

-- before
CREATE INDEX ON mv_delta (a_id);
SELECT * FROM mv_delta JOIN other ON mv_delta.a_id = other.id AND mv_delta.ts > other.ts;

-- after
SELECT * FROM mv_delta JOIN other ON mv_delta.a_id = other.id
WHERE mv_delta.ts > other.ts; -- non-equi predicate outside join condition
Defensive patterns

Strategy: validation

Validate before calling

-- ensure joins feeding indexed MVs are pure equi inner joins
-- EXPLAIN the plan and check for DeltaIndexJoin nodes with
-- non-inner join types or non-equi ON conditions before CREATE INDEX.

Try / catch

// This is a panic, not an error — guard at plan construction:
let join_type = delta_index_join.get_join_type()?;
let has_non_equi = delta_index_join.condition
    .as_ref().map(|c| c.has_non_equi_condition()).unwrap_or(false);
if join_type != JoinType::Inner || has_non_equi {
    return build_regular_join(state, current_fragment, stream_node);
}

Prevention

When it happens

Trigger: Building a streaming plan whose fragment contains a DeltaIndexJoin node with a join type other than Inner, or an Inner join that also carries a non-equi condition, when build_fragment() fragments the plan.

Common situations: Creating indexes/MVs over joins with mixed ON conditions (e.g. ON a.id = b.id AND a.ts > b.ts) that the delta-join rewrite incorrectly routes through the delta join path; using join types (LEFT/SEMI) on indexed MVs that hit this path.

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/c1a43845764bd0e8. Report an issue: GitHub.