risingwavelabs/risingwave · error

BatchGapFill is not implemented yet

Error message

BatchGapFill is not implemented yet

What it means

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).

Solutions

  1. Execute the gap-fill query in streaming mode (e.g. against a materialized view) instead of batch mode.
  2. Implement gap filling manually in batch SQL: generate the time grid with generate_series and LEFT JOIN the data onto it.
  3. Implement `ToBatch for LogicalGapFill` if batch support is required upstream.

Example fix

-- before (batch query with gap fill)
SELECT gap_fill(ts, INTERVAL '1 minute'), avg(v) FROM metrics GROUP BY ...;

-- after (manual batch gap fill)
SELECT g.ts, m.avg_v
FROM generate_series('2024-01-01', '2024-01-02', INTERVAL '1 minute') AS g(ts)
LEFT JOIN (SELECT ts, avg(v) AS avg_v FROM metrics GROUP BY ts) m
  ON m.ts = g.ts;
Defensive patterns

Strategy: try-catch

Validate before calling

if query.is_batch() && query.uses_gap_fill() {
    return Err("gap fill requires streaming execution".into());
}

Type guard

fn gap_fill_in_batch(plan: &LogicalPlan) -> bool {
    matches!(plan.node_type(), PlanNodeType::LogicalGapFill)
}

Try / catch

match to_batch(plan) {
    Err(e) if e.to_string().contains("BatchGapFill") =>
        run_streaming_or_manual_series_fallback(query),
    r => r,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/dd5a95259bf260eb. Report an issue: GitHub.

Appendix: source

Thrown at src/frontend/src/optimizer/plan_node/logical_gap_fill.rs:177

        self.core.visit_exprs(v);
    }
}

impl PredicatePushdown for LogicalGapFill {
    fn predicate_pushdown(
        &self,
        predicate: Condition,
        ctx: &mut PredicatePushdownContext,
    ) -> PlanRef {
        // No pushdown, but keep recursing with a `true` condition so that shares below
        // still receive a contribution from this parent (see `PredicatePushdownContext`).
        gen_filter_and_pushdown(self, predicate, Condition::true_cond(), ctx)
    }
}

impl ToBatch for LogicalGapFill {
    fn to_batch(&self) -> Result<super::BatchPlanRef> {
        bail!("BatchGapFill is not implemented yet")
    }
}

impl ToStream for LogicalGapFill {
    fn to_stream(&self, ctx: &mut ToStreamContext) -> Result<super::StreamPlanRef> {
        use super::{StreamEowcGapFill, StreamEowcSort, StreamGapFill};
        use crate::error::ErrorCode;
        use crate::optimizer::property::RequiredDist;

        let stream_input = self.input().to_stream(ctx)?;
        let partition_cols = &self.core.partition_by_cols;

        // Choose distribution based on partition_by_cols
        let new_input = if partition_cols.is_empty() {
            // No partition: singleton distribution (backward compatible)
            RequiredDist::single().streaming_enforce_if_not_satisfies(stream_input)?
        } else {
            // With partition: hash distribute by partition columns

View on GitHub (pinned to 6469eb736d)