## Code Analysis ### Interval offsets become temporal frame bounds `server/ast/window.go:67-95` ```go var bounds [2]*vitess.FrameBound for i, bound := range []*tree.WindowFrameBound{node.Frame.Bounds.StartBound, node.Frame.Bounds.EndBound} { if bound == nil { continue } var boundType vitess.BoundType switch bound.BoundType { case tree.OffsetPreceding: boundType = vitess.ExprPreceding case tree.OffsetFollowing: boundType = vitess.ExprFollowing } boundExpr, err := nodeExpr(ctx, bound.OffsetExpr) if err != nil { return nil, err } bounds[i] = &vitess.FrameBound{Expr: boundExpr, Type: boundType} } ``` The window parser sends each interval offset through `nodeExpr` and preserves whether it is preceding or following when constructing the frame bound. ### Interval expressions preserve a microsecond delta `server/ast/expr.go:567-571` ```go case *tree.DInterval: // Interval formats its duration as a quoted SQL literal so stored defaults can be reparsed. return vitess.InjectedExpr{ Expression: pgexprs.NewInterval(node.Duration), }, nil ``` `server/expression/interval.go:43-52` ```go func NewInterval(d duration.Duration) *Interval { return &Interval{ Literal: expression.NewLiteral(d, pgtypes.Interval), delta: &expression.TimeDelta{ Months: d.Months, Days: d.Days, Microseconds: d.Nanos() / 1000, }, } } ``` The interval expression consumed by the frame evaluator is represented as a `TimeDelta`; its nanosecond duration is converted to microseconds before temporal membership is evaluated. ### Existing temporal boundary coverage `testing/go/window_test.go:314-328` ```go Name: "RANGE frame with INTERVAL month boundary is calendar-correct", SetUpScript: []string{ "CREATE TABLE month_edge (d DATE, v INT);", "INSERT INTO month_edge VALUES ('2022-01-31', 1), ('2022-02-28', 2), ('2022-03-01', 3);", }, Query: "SELECT sum(v) OVER (ORDER BY d RANGE BETWEEN UNBOUNDED PRECEDING AND INTERVAL '1' MONTH FOLLOWING) FROM month_edge ORDER BY d", Expected: []sql.Row{ {int64(3)}, {int64(6)}, {int64(6)}, }, ``` This existing test establishes that interval `RANGE` frames are expected to honor temporal boundaries, including calendar edge behavior. ### Observed SQL reproduction The test seeded five timestamped rows and ran both preceding and following three-minute `RANGE` frames. ```sql SELECT to_char(ts,'HH24:MI:SS.US') AS ts, v, sum(v) OVER (ORDER BY ts RANGE BETWEEN INTERVAL '3 minutes' PRECEDING AND CURRENT ROW) AS preceding_sum, sum(v) OVER (ORDER BY ts RANGE BETWEEN CURRENT ROW AND INTERVAL '3 minutes' FOLLOWING) AS following_sum FROM frame_probe ORDER BY ts; ``` ```text ts | v | preceding_sum | following_sum -----------------+----+---------------+--------------- 00:00:00.000000 | 1 | 1 | 7 00:02:59.999999 | 2 | 3 | 30 00:03:00.000000 | 4 | 7 | 28 00:03:00.000001 | 8 | 15 | 24 00:06:00.000000 | 16 | 28 | 16 (5 rows) ``` The row at `00:03:00.000001` has a preceding lower bound of `00:00:00.000001`, so the `00:00:00` value should be excluded and the sum should be `14`; the observed sum is `15`. The row at `00:02:59.999999` has a following upper bound of `00:05:59.999999`, so the `00:06:00` value should be excluded and the sum should be `14`; the observed sum is `30`. The exact `00:03:00` boundary is included, while the adjacent microsecond cases cross the frame boundary incorrectly. ### Result The captured SQL execution supports the reported defect: interval `RANGE` aggregates return incorrect totals for rows one microsecond inside or outside a three-minute temporal boundary, despite handling the exact boundary row correctly.