{"record":{"id":"7e896bf9ba49f896","repo":"risingwavelabs/risingwave","slug":"insert-should-always-be-converted-to-batch-plan","errorCode":null,"errorMessage":"insert should always be converted to batch plan","messagePattern":"insert should always be converted to batch plan","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_insert.rs","lineNumber":169,"sourceCode":"    ) -> PlanRef {\n        gen_filter_and_pushdown(self, predicate, Condition::true_cond(), ctx)\n    }\n}\n\nimpl ToBatch for LogicalInsert {\n    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {\n        let new_input = self.input().to_batch()?;\n        let core = self.core.clone_with_input(new_input);\n        Ok(BatchInsert::new(core).into())\n    }\n}\n\nimpl ToStream for LogicalInsert {\n    fn to_stream(\n        &self,\n        _ctx: &mut ToStreamContext,\n    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {\n        unreachable!(\"insert should always be converted to batch plan\");\n    }\n\n    fn logical_rewrite_for_stream(\n        &self,\n        _ctx: &mut RewriteStreamContext,\n    ) -> Result<(PlanRef, ColIndexMapping)> {\n        unreachable!(\"insert should always be converted to batch plan\");\n    }\n}\n","sourceCodeStart":151,"sourceCodeEnd":179,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_insert.rs#L151-L179","documentation":"LogicalInsert represents SQL INSERT into a table. In RisingWave, inserts are always planned as batch DML jobs, never converted to streaming operators, so to_stream panics via unreachable!() with this message. Reaching it means an INSERT node survived into the stream conversion phase, violating the planner's contract.","triggerScenarios":"to_stream is invoked on a LogicalInsert node — e.g. an INSERT statement mis-routed into the streaming plan path (such as an INSERT inside a materialized-view/streaming definition or an optimizer bug in statement routing).","commonSituations":"Attempting to use INSERT within streaming DDL; frontend regressions in statement-kind routing (batch DML vs streaming); tooling that rewrites SQL into streaming plans containing inserts.","solutions":["Ensure INSERT statements are executed as batch DML (plain INSERT), not embedded in streaming queries like CREATE MATERIALIZED VIEW.","Check statement routing: the frontend should convert INSERT to a batch plan before any to_stream pass; upgrade RisingWave if this is a known regression.","As a developer, replace unreachable!() with an explanatory bail! for better diagnostics."],"exampleFix":"// before\nfn to_stream(&self, _ctx: &mut ToStreamContext) -> Result<StreamPlanRef> {\n    unreachable!(\"insert should always be converted to batch plan\");\n}\n// after\nfn to_stream(&self, _ctx: &mut ToStreamContext) -> Result<StreamPlanRef> {\n    bail!(\"insert should always be converted to batch plan\");\n}","handlingStrategy":"validation","validationCode":"// Reject INSERT inside streaming DDL before submitting\nlet upper = sql.to_uppercase();\nif upper.trim_start().starts_with(\"INSERT\") && upper.contains(\"CREATE MATERIALIZED VIEW\") {\n    return Err(\"INSERT must be a standalone batch DML statement\".into());\n}","typeGuard":"fn is_batch_dml(stmt: &Statement) -> bool {\n    matches!(stmt, Statement::Insert { .. })\n}","tryCatchPattern":"match client.run_sql(stmt) {\n    Err(e) if format!(\"{e}\").contains(\"converted to batch plan\") => {\n        client.run_sql(&strip_streaming_wrapper(stmt))?;\n    }\n    r => r?,\n}","preventionTips":["Always issue INSERT as a standalone statement.","Never embed INSERT in CREATE MATERIALIZED VIEW or stream definitions.","Route DML through batch execution paths in clients/tools.","Upgrade RisingWave if an INSERT unexpectedly hits the streaming planner."],"tags":["rust","query-optimizer","dml","insert","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"}