Repository navigation
Open
Labels
Milestone
Description
Summary
TUMBLE(..., INTERVAL '1 month') computes window_start and window_end with two different month lengths. As a result, window_end can be earlier than the row's timestamp. Also, one window's end doesn't match the next window's start.
Related: #19075 (same root cause, reported with 1 year and 1 month windows). Found by @chenzl25 while reviewing #27648.
Reproduction
with v(ts) as (values
('2023-03-21 10:00'::timestamp),
('2023-03-23 10:00'::timestamp),
('2023-03-25 10:00'::timestamp))
select ts, window_start, window_end
from tumble(v, ts, interval '1 month')
order by ts;Output (from @chenzl25's review):
ts | window_start | window_end
2023-03-21 10:00 | 2023-02-22 00:00 | 2023-03-22 00:00
2023-03-23 10:00 | 2023-02-22 00:00 | 2023-03-22 00:00 <- ts >= window_end
2023-03-25 10:00 | 2023-03-24 00:00 | 2023-04-24 00:00
Expected: window_start <= ts < window_end for every row, and each window_end equals the next window_start.
Cause
window_startcomes fromtumble_start.interval_to_micro_secondinsrc/expr/impl/src/scalar/tumble.rsturns the window size into a fixed duration, with 1 month =USECS_PER_MONTH= 30 days and 1 day = 24 hours.window_endis planned inplan_tumble_window(src/frontend/src/planner/relation.rs) aswindow_start + window_size. That callsTimestamp::checked_add(Interval)(src/common/src/types/datetime.rs), which adds a calendar month (28 to 31 days).
Impact
- Batch and streaming queries using
TUMBLEwith a month or year window return rows outside their window. Aggregates grouped bywindow_endare wrong. - After fix(frontend): don't derive watermarks through timestamp ± months #27648,
timestamp ± monthsno longer derives a watermark. A month-sizedwindow_endthen has no watermark, so its window state is never cleaned up. timestamptzwindows sized in days probably have a similar mismatch across DST changes.timestamptz + intervaltreats a day as 23 or 25 hours there, buttumble_startuses 24. Not yet reproduced.HOPbuildswindow_endthe same way (src/frontend/src/optimizer/plan_node/generic/hop_window.rs) and probably has the same bug. Not yet reproduced.
Options
- Reject month and year strides, like PostgreSQL's
date_bindoes. This is simple, but it breaks existing queries that use them. - Compute
window_endwith the same fixed-duration arithmetic. At plan time, turn the literal window size into a pure-microsecond interval before theAdd. Windows stay contiguous andwindow_endkeeps its watermark.EXPLAINoutput changes, for example'3 days'becomes'72:00:00'.
Calendar-aligned monthly buckets would still need date_trunc('month', ts), as suggested in #19075.
Activity
yashaggarwal85d commentedon Oct 27, 2026
Hi! I have raised a PR for a potential solution for this issue! Please have a look. Thanks