{"record":{"id":"a9a2ab03dfbadc3d","repo":"risingwavelabs/risingwave","slug":"upsert-stream-is-not-supported-as-input-of-pla","errorCode":null,"errorMessage":"upsert stream is not supported as input of {}, plan:\n{}","messagePattern":"upsert stream is not supported as input of (.+?), plan:\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/property/stream_kind.rs","lineNumber":123,"sourceCode":"    }\n}\n\n/// Reject upsert stream as input.\nmacro_rules! reject_upsert_input {\n    ($input:expr) => {\n        reject_upsert_input!(\n            $input,\n            std::any::type_name::<Self>().split(\"::\").last().unwrap()\n        )\n    };\n\n    ($input:expr, $curr:expr) => {{\n        use crate::optimizer::plan_node::Explain;\n        use crate::optimizer::property::StreamKind;\n\n        let kind = $input.stream_kind();\n        if let StreamKind::Upsert = kind {\n            risingwave_common::bail!(\n                \"upsert stream is not supported as input of {}, plan:\\n{}\",\n                $curr,\n                $input.explain_to_string()\n            );\n        }\n        kind\n    }};\n}\npub(crate) use reject_upsert_input;\n","sourceCodeStart":105,"sourceCodeEnd":133,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/property/stream_kind.rs#L105-L133","documentation":"This macro checks the stream kind of an operator's input during optimization. RisingWave cannot build certain stream operators (e.g. aggregations, joins that require append-only or non-upsert input) on top of an upsert stream, so it bails with the operator name and the full plan text.","triggerScenarios":"Optimizer applying a plan-node macro (`assert...` style) whose macro receives an input whose `stream_kind()` is `StreamKind::Upsert`; typically when a user query composes an operator above an upsert-producing source or MV.","commonSituations":"Creating an MV or query on top of an upsert materialized view/source with operators that lack upsert input support; version changes that tighten stream-kind requirements.","solutions":["Rewrite the query to consume the upsert stream through an operator that supports upsert input (e.g. use an aggregation or a supported sink).","Define the upstream MV without upsert (use an append-only source or add a primary key that makes it append-compatible).","Read the embedded plan text to identify which node produces the upsert kind and restructure above it.","Upgrade or restructure per current docs on supported upsert input operators."],"exampleFix":"-- before: plain count over an upsert MV\nCREATE MATERIALIZED VIEW mv2 AS SELECT count(*) FROM upsert_mv;\n-- after: force retraction-aware aggregation or use append-only input\nCREATE MATERIALIZED VIEW mv2 AS SELECT count(*) FROM (SELECT * FROM upsert_mv) GROUP BY 1; -- or build on append-only source","handlingStrategy":"validation","validationCode":"// Before creating an MV over a stream, verify it is not upsert\n-- SELECT ... check upstream MV definition; append-only sources only","typeGuard":null,"tryCatchPattern":"// Wrap MV creation and read the bail message's plan text to find the upsert node\nCREATE MATERIALIZED VIEW ...; -- on failure, inspect plan text in the error for 'Upsert'","preventionTips":["Only build new MVs on append-only sources or non-upsert upstream MVs","Check upstream MV stream kind in docs before composing operators","Add a primary key to sources to keep streams append-only where possible"],"tags":["rust","streaming","optimizer","upsert"],"backgroundTag":"unsupported-operation","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"}