{"record":{"id":"dd5a95259bf260eb","repo":"risingwavelabs/risingwave","slug":"batchgapfill-is-not-implemented-yet","errorCode":null,"errorMessage":"BatchGapFill is not implemented yet","messagePattern":"BatchGapFill is not implemented yet","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_gap_fill.rs","lineNumber":177,"sourceCode":"        self.core.visit_exprs(v);\n    }\n}\n\nimpl PredicatePushdown for LogicalGapFill {\n    fn predicate_pushdown(\n        &self,\n        predicate: Condition,\n        ctx: &mut PredicatePushdownContext,\n    ) -> PlanRef {\n        // No pushdown, but keep recursing with a `true` condition so that shares below\n        // still receive a contribution from this parent (see `PredicatePushdownContext`).\n        gen_filter_and_pushdown(self, predicate, Condition::true_cond(), ctx)\n    }\n}\n\nimpl ToBatch for LogicalGapFill {\n    fn to_batch(&self) -> Result<super::BatchPlanRef> {\n        bail!(\"BatchGapFill is not implemented yet\")\n    }\n}\n\nimpl ToStream for LogicalGapFill {\n    fn to_stream(&self, ctx: &mut ToStreamContext) -> Result<super::StreamPlanRef> {\n        use super::{StreamEowcGapFill, StreamEowcSort, StreamGapFill};\n        use crate::error::ErrorCode;\n        use crate::optimizer::property::RequiredDist;\n\n        let stream_input = self.input().to_stream(ctx)?;\n        let partition_cols = &self.core.partition_by_cols;\n\n        // Choose distribution based on partition_by_cols\n        let new_input = if partition_cols.is_empty() {\n            // No partition: singleton distribution (backward compatible)\n            RequiredDist::single().streaming_enforce_if_not_satisfies(stream_input)?\n        } else {\n            // With partition: hash distribute by partition columns","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_gap_fill.rs#L159-L195","documentation":"GapFill (time-series gap filling, used with range windows) has no batch execution path, so `ToBatch for LogicalGapFill` unconditionally bails with this message. Gap fill is only lowered to stream nodes (StreamEowcGapFill / StreamGapFill / StreamEowcSort).","triggerScenarios":"Calling `to_batch` on a LogicalGapFill node — i.e. running a batch (ad-hoc) SELECT whose plan contains gap filling, typically a query using gap-fill semantics against a batch plan.","commonSituations":"Developers run a time-series gap-fill query in batch mode (not against a materialized view) or attempt distributed batch planning over gap-fill expressions.","solutions":["Execute the gap-fill query in streaming mode (e.g. against a materialized view) instead of batch mode.","Implement gap filling manually in batch SQL: generate the time grid with generate_series and LEFT JOIN the data onto it.","Implement `ToBatch for LogicalGapFill` if batch support is required upstream."],"exampleFix":"-- before (batch query with gap fill)\nSELECT gap_fill(ts, INTERVAL '1 minute'), avg(v) FROM metrics GROUP BY ...;\n\n-- after (manual batch gap fill)\nSELECT g.ts, m.avg_v\nFROM generate_series('2024-01-01', '2024-01-02', INTERVAL '1 minute') AS g(ts)\nLEFT JOIN (SELECT ts, avg(v) AS avg_v FROM metrics GROUP BY ts) m\n  ON m.ts = g.ts;","handlingStrategy":"try-catch","validationCode":"if query.is_batch() && query.uses_gap_fill() {\n    return Err(\"gap fill requires streaming execution\".into());\n}","typeGuard":"fn gap_fill_in_batch(plan: &LogicalPlan) -> bool {\n    matches!(plan.node_type(), PlanNodeType::LogicalGapFill)\n}","tryCatchPattern":"match to_batch(plan) {\n    Err(e) if e.to_string().contains(\"BatchGapFill\") =>\n        run_streaming_or_manual_series_fallback(query),\n    r => r,\n}","preventionTips":["Run gap-fill queries in streaming mode against MVs","Use generate_series + LEFT JOIN for batch gap filling","Check engine support matrix before using time-series functions in batch","Add a planner rule or early check that rejects gap fill in batch mode"],"tags":["rust","batch","gap-fill","timeseries"],"backgroundTag":"unsupported-operation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}