@@ -489,3 +489,95 @@ SELECT * FROM STATS LEFT JOIN VOLUME USING (bucket);
489489DROP TABLE gf8844_table1;
490490DROP TABLE gf8844_table2;
491491RESET timezone;
492+ -- Fix for #5202: time_bucket_gapfill with HAVING clause returning incorrect rows
493+ -- HAVING quals must be evaluated on top of the GapFill node so they do not
494+ -- remove groups before gap rows have been generated.
495+ SET timezone TO 'UTC';
496+ CREATE TABLE gf5202(time timestamptz, device_id int, value float);
497+ INSERT INTO gf5202 VALUES
498+ ('2023-01-03T00:00:00Z', 1, 4),
499+ ('2023-01-03T01:00:00Z', 1, 4),
500+ ('2023-01-03T02:00:00Z', 1, 4),
501+ ('2023-01-05T00:00:00Z', 1, 6);
502+ -- Filter should appear on the Custom Scan (GapFill) node, not on the GroupAggregate.
503+ EXPLAIN (COSTS OFF)
504+ SELECT time_bucket_gapfill('1 day', time) AS day, device_id, count(*)
505+ FROM gf5202
506+ WHERE time >= '2023-01-01T00:00:00Z' AND time < '2023-01-08T00:00:00Z'
507+ GROUP BY day, device_id
508+ HAVING count(*) < 2;
509+ --- QUERY PLAN ---
510+ Custom Scan (GapFill)
511+ Filter: ((count(*)) < 2)
512+ -> Sort
513+ Sort Key: device_id, (time_bucket_gapfill('@ 1 day'::interval, "time", NULL::timestamp with time zone, NULL::timestamp with time zone))
514+ -> GroupAggregate
515+ Group Key: (time_bucket_gapfill('@ 1 day'::interval, "time", NULL::timestamp with time zone, NULL::timestamp with time zone)), device_id
516+ -> Sort
517+ Sort Key: (time_bucket_gapfill('@ 1 day'::interval, "time", NULL::timestamp with time zone, NULL::timestamp with time zone)), device_id
518+ -> Seq Scan on gf5202
519+ Filter: (("time" >= 'Sun Jan 01 00:00:00 2023 UTC'::timestamp with time zone) AND ("time" < 'Sun Jan 08 00:00:00 2023 UTC'::timestamp with time zone))
520+
521+ -- HAVING count(*) < 2: real group with count=3 (2023-01-03) is dropped.
522+ -- Real group with count=1 (2023-01-05) is kept. Gap rows (NULL count) are
523+ -- rejected by <2 (NULL<2 is UNKNOWN -> false) under SQL semantics.
524+ SELECT time_bucket_gapfill('1 day', time) AS day, device_id, count(*)
525+ FROM gf5202
526+ WHERE time >= '2023-01-01T00:00:00Z' AND time < '2023-01-08T00:00:00Z'
527+ GROUP BY day, device_id
528+ HAVING count(*) < 2
529+ ORDER BY day, device_id;
530+ day | device_id | count
531+ ------------------------------+-----------+-------
532+ Thu Jan 05 00:00:00 2023 UTC | 1 | 1
533+
534+ -- HAVING count(*) IS NULL keeps only the gap rows.
535+ SELECT time_bucket_gapfill('1 day', time) AS day, device_id, count(*)
536+ FROM gf5202
537+ WHERE time >= '2023-01-01T00:00:00Z' AND time < '2023-01-08T00:00:00Z'
538+ GROUP BY day, device_id
539+ HAVING count(*) IS NULL
540+ ORDER BY day, device_id;
541+ day | device_id | count
542+ ------------------------------+-----------+-------
543+ Sun Jan 01 00:00:00 2023 UTC | 1 |
544+ Mon Jan 02 00:00:00 2023 UTC | 1 |
545+ Wed Jan 04 00:00:00 2023 UTC | 1 |
546+ Fri Jan 06 00:00:00 2023 UTC | 1 |
547+ Sat Jan 07 00:00:00 2023 UTC | 1 |
548+
549+ -- HAVING predicate combining agg and group column.
550+ SELECT time_bucket_gapfill('1 day', time) AS day, device_id, count(*)
551+ FROM gf5202
552+ WHERE time >= '2023-01-01T00:00:00Z' AND time < '2023-01-08T00:00:00Z'
553+ GROUP BY day, device_id
554+ HAVING count(*) IS NULL OR count(*) < 2
555+ ORDER BY day, device_id;
556+ day | device_id | count
557+ ------------------------------+-----------+-------
558+ Sun Jan 01 00:00:00 2023 UTC | 1 |
559+ Mon Jan 02 00:00:00 2023 UTC | 1 |
560+ Wed Jan 04 00:00:00 2023 UTC | 1 |
561+ Thu Jan 05 00:00:00 2023 UTC | 1 | 1
562+ Fri Jan 06 00:00:00 2023 UTC | 1 |
563+ Sat Jan 07 00:00:00 2023 UTC | 1 |
564+
565+ -- HAVING that rejects every real group must still produce the gap rows when
566+ -- the predicate leaves room for them via IS NULL. Before the fix this
567+ -- returned zero rows because HAVING ran below the gapfill node.
568+ SELECT time_bucket_gapfill('1 day', time) AS day, device_id, count(*)
569+ FROM gf5202
570+ WHERE time >= '2023-01-01T00:00:00Z' AND time < '2023-01-08T00:00:00Z'
571+ GROUP BY day, device_id
572+ HAVING count(*) > 1000 OR count(*) IS NULL
573+ ORDER BY day, device_id;
574+ day | device_id | count
575+ ------------------------------+-----------+-------
576+ Sun Jan 01 00:00:00 2023 UTC | 1 |
577+ Mon Jan 02 00:00:00 2023 UTC | 1 |
578+ Wed Jan 04 00:00:00 2023 UTC | 1 |
579+ Fri Jan 06 00:00:00 2023 UTC | 1 |
580+ Sat Jan 07 00:00:00 2023 UTC | 1 |
581+
582+ DROP TABLE gf5202;
583+ RESET timezone;
0 commit comments