risingwavelabs/risingwave · error

unimplemented

Error message

unimplemented

What it means

LogicalExcept::to_batch unconditionally calls unimplemented!(): EXCEPT has no batch physical implementation in the optimizer, so planning EXCEPT as a batch query always panics. Fires whenever a batch plan conversion reaches a LogicalExcept node.

Source

Thrown at src/frontend/src/optimizer/plan_node/logical_except.rs:101

impl PredicatePushdown for LogicalExcept {
    fn predicate_pushdown(
        &self,
        predicate: Condition,
        ctx: &mut PredicatePushdownContext,
    ) -> PlanRef {
        let new_inputs = self
            .inputs()
            .iter()
            .map(|input| input.predicate_pushdown(predicate.clone(), ctx))
            .collect_vec();
        self.clone_with_inputs(&new_inputs)
    }
}

impl ToBatch for LogicalExcept {
    fn to_batch(&self) -> Result<BatchPlanRef> {
        unimplemented!()
    }
}

impl ToStream for LogicalExcept {
    fn to_stream(&self, _ctx: &mut ToStreamContext) -> Result<StreamPlanRef> {
        unimplemented!()
    }

    fn logical_rewrite_for_stream(
        &self,
        _ctx: &mut RewriteStreamContext,
    ) -> Result<(PlanRef, ColIndexMapping)> {
        unimplemented!()
    }
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite EXCEPT using NOT EXISTS or LEFT JOIN ... WHERE rhs IS NULL with DISTINCT.
  2. Use EXCEPT ALL semantics via an anti-join with grouping and count if needed.
  3. Check RisingWave release notes for EXCEPT support before using it.

Example fix

// before
SELECT id FROM t1 EXCEPT SELECT id FROM t2;
// after
SELECT DISTINCT t1.id FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
Defensive patterns

Strategy: fallback

Validate before calling

// Detect EXCEPT before sending to RisingWave
if (/\bEXCEPT\b/i.test(sql)) {
  throw new Error('EXCEPT is not supported by this RisingWave planner path; rewrite with NOT EXISTS');
}

Try / catch

try {
  return await rw.query(sql);
} catch (e) {
  if (e instanceof Error && /unimplemented/i.test(e.message)) {
    return await rw.query(rewriteExceptToAntiJoin(sql));
  }
  throw e;
}

Prevention

When it happens

Trigger: Running a query using the EXCEPT set operator, e.g. SELECT a FROM t1 EXCEPT SELECT a FROM t2, which reaches LogicalExcept::to_batch (or to_stream).

Common situations: Migrating Postgres queries containing EXCEPT; test scripts using EXCEPT for diffing results.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/286cc31b33184eb5. Report an issue: GitHub.