{"record":{"id":"9c89752809cd9466","repo":"risingwavelabs/risingwave","slug":"should-already-bail-out-after-subquery-unnesting","errorCode":null,"errorMessage":"should already bail out after subquery unnesting","messagePattern":"should already bail out after subquery unnesting","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_max_one_row.rs","lineNumber":112,"sourceCode":"        gen_filter_and_pushdown(self, predicate, Condition::true_cond(), ctx)\n    }\n}\n\nimpl ToBatch for LogicalMaxOneRow {\n    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {\n        let input = self.input().to_batch()?;\n        let core = generic::MaxOneRow { input };\n        Ok(BatchMaxOneRow::new(core).into())\n    }\n}\n\nimpl ToStream for LogicalMaxOneRow {\n    fn to_stream(\n        &self,\n        _ctx: &mut ToStreamContext,\n    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {\n        // Check `LogicalOptimizer::gen_optimized_logical_plan_for_stream`.\n        unreachable!(\"should already bail out after subquery unnesting\")\n    }\n\n    fn logical_rewrite_for_stream(\n        &self,\n        ctx: &mut RewriteStreamContext,\n    ) -> Result<(PlanRef, ColIndexMapping)> {\n        let (input, input_col_change) = self.input().logical_rewrite_for_stream(ctx)?;\n        let (this, out_col_change) = self.rewrite_with_input(input, input_col_change);\n        Ok((this.into(), out_col_change))\n    }\n}\n","sourceCodeStart":94,"sourceCodeEnd":124,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_max_one_row.rs#L94-L124","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"-- before: scalar subquery left MaxOneRow in the stream plan\nCREATE MATERIALIZED VIEW mv AS SELECT a, (SELECT max(b) FROM t2) AS m FROM t1;\n-- after: use a join + group by instead\nCREATE MATERIALIZED VIEW mv AS\nSELECT t1.a, t2.m FROM t1 JOIN (SELECT max(b) AS m FROM t2) t2 ON true;","handlingStrategy":"validation","validationCode":"// before CREATE MATERIALIZED VIEW, check the query for scalar subqueries:\n// SELECT ... WHERE x = (SELECT max(y) FROM t2) -- must be unnestable, else rewrite it","typeGuard":null,"tryCatchPattern":"match create_result {\n    Err(e) if e.to_string().contains(\"subquery unnesting\") => {\n        eprintln!(\"Rewrite scalar subqueries as joins/group-bys for streaming\");\n    }\n    r => r?,\n}","preventionTips":["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."],"tags":["rust","stream-optimizer","subquery","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"}