risingwavelabs/risingwave · error
should already bail out after subquery unnesting
Error message
should already bail out after subquery unnesting
What it means
`LogicalMaxOneRow::to_stream` is an `unreachable!("should already bail out after subquery unnesting")`. A `MaxOneRow` node (scalar-subquery wrapper, e.g. from `(SELECT max(x) ...)` used as an expression) must have been eliminated by subquery unnesting before the stream planner runs, per `LogicalOptimizer::gen_optimized_logical_plan_for_stream`. Reaching this node means an unnested scalar subquery survived into the to-stream phase.
Source
Thrown at src/frontend/src/optimizer/plan_node/logical_max_one_row.rs:112
gen_filter_and_pushdown(self, predicate, Condition::true_cond(), ctx)
}
}
impl ToBatch for LogicalMaxOneRow {
fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
let input = self.input().to_batch()?;
let core = generic::MaxOneRow { input };
Ok(BatchMaxOneRow::new(core).into())
}
}
impl ToStream for LogicalMaxOneRow {
fn to_stream(
&self,
_ctx: &mut ToStreamContext,
) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
// Check `LogicalOptimizer::gen_optimized_logical_plan_for_stream`.
unreachable!("should already bail out after subquery unnesting")
}
fn logical_rewrite_for_stream(
&self,
ctx: &mut RewriteStreamContext,
) -> Result<(PlanRef, ColIndexMapping)> {
let (input, input_col_change) = self.input().logical_rewrite_for_stream(ctx)?;
let (this, out_col_change) = self.rewrite_with_input(input, input_col_change);
Ok((this.into(), out_col_change))
}
}
View on GitHub (pinned to 6469eb736d)
Solutions
- Rewrite the query to avoid the scalar subquery, e.g. convert it to a GROUP BY join or window function that unnesting supports.
- Check `gen_optimized_logical_plan_for_stream` for a missing bail-out: it should reject the query with a clear error instead of reaching the panic; fix/extend the unnesting pass.
- Test whether the same query works as a batch query to confirm it is the scalar-subquery/stream limitation, then report the query upstream.
Example fix
-- before: scalar subquery left MaxOneRow in the stream plan CREATE MATERIALIZED VIEW mv AS SELECT a, (SELECT max(b) FROM t2) AS m FROM t1; -- after: use a join + group by instead CREATE MATERIALIZED VIEW mv AS SELECT t1.a, t2.m FROM t1 JOIN (SELECT max(b) AS m FROM t2) t2 ON true;
Defensive patterns
Strategy: validation
Validate before calling
// before CREATE MATERIALIZED VIEW, check the query for scalar subqueries: // SELECT ... WHERE x = (SELECT max(y) FROM t2) -- must be unnestable, else rewrite it
Try / catch
match create_result {
Err(e) if e.to_string().contains("subquery unnesting") => {
eprintln!("Rewrite scalar subqueries as joins/group-bys for streaming");
}
r => r?,
} Prevention
- Avoid scalar subqueries in materialized view definitions; prefer explicit JOIN with an aggregated subquery.
- Confirm `gen_optimized_logical_plan_for_stream` still bails with a clear error for unnestable subqueries after upgrades.
When it happens
Trigger: Creating a streaming plan (e.g. `CREATE MATERIALIZED VIEW`) whose query contains a scalar subquery that the unnesting pass failed to rewrite away, leaving `LogicalMaxOneRow` in the tree.
Common situations: Scalar subqueries in SELECT/WHERE of a materialized view definition using an unsupported placement or correlation pattern that unnesting does not handle; regressions in the unnesting rule after version upgrades.
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
- unreachable
- Method not available for `LogicalMultiJoin` which is a place
- IcebergSinkWriter should be initialized before barrier
- BatchPosixFsReader should not hit this branch. refer to `bat
- duplicate path: {:?}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/9c89752809cd9466.
Report an issue: GitHub.