@@ -581,6 +581,56 @@ CREATE INDEX btest_time_alt_idx ON btest(time_alt);
581581 -> Seq Scan on _hyper_1_4_chunk (actual rows=1.00 loops=1)
582582 -> Seq Scan on _hyper_1_5_chunk (actual rows=1.00 loops=1)
583583
584+ ROLLBACK;
585+ -- two FIRST/LAST sharing the value column but ordering on different columns
586+ BEGIN;
587+ CREATE TABLE bookend_two_orderings(time timestamptz NOT NULL, time_alt timestamptz NOT NULL, val int NOT NULL);
588+ SELECT schema_name, table_name, created FROM create_hypertable('bookend_two_orderings', 'time');
589+ schema_name | table_name | created
590+ -------------+-----------------------+---------
591+ public | bookend_two_orderings | t
592+
593+ INSERT INTO bookend_two_orderings
594+ SELECT '2025-01-01'::timestamptz + g * interval '1 minute',
595+ '2025-01-01'::timestamptz + (201 - g) * interval '1 minute',
596+ g
597+ FROM generate_series(1, 200) g;
598+ CREATE INDEX ON bookend_two_orderings(time);
599+ CREATE INDEX ON bookend_two_orderings(time_alt);
600+ :PREFIX SELECT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
601+ --- QUERY PLAN ---
602+ Result (actual rows=1.00 loops=1)
603+ InitPlan 1 (returns $1)
604+ -> Limit (actual rows=1.00 loops=1)
605+ -> Index Scan using _hyper_3_8_chunk_bookend_two_orderings_time_alt_idx on _hyper_3_8_chunk (actual rows=1.00 loops=1)
606+ InitPlan 2 (returns $0)
607+ -> Limit (actual rows=1.00 loops=1)
608+ -> Index Scan using _hyper_3_8_chunk_bookend_two_orderings_time_idx1 on _hyper_3_8_chunk _hyper_3_8_chunk_1 (actual rows=1.00 loops=1)
609+
610+ :PREFIX SELECT last(val, time), last(val, time_alt) FROM bookend_two_orderings;
611+ --- QUERY PLAN ---
612+ Result (actual rows=1.00 loops=1)
613+ InitPlan 1 (returns $1)
614+ -> Limit (actual rows=1.00 loops=1)
615+ -> Index Scan Backward using _hyper_3_8_chunk_bookend_two_orderings_time_alt_idx on _hyper_3_8_chunk (actual rows=1.00 loops=1)
616+ Index Cond: (time_alt IS NOT NULL)
617+ InitPlan 2 (returns $0)
618+ -> Limit (actual rows=1.00 loops=1)
619+ -> Index Scan Backward using _hyper_3_8_chunk_bookend_two_orderings_time_idx1 on _hyper_3_8_chunk _hyper_3_8_chunk_1 (actual rows=1.00 loops=1)
620+ Index Cond: ("time" IS NOT NULL)
621+
622+ -- DISTINCT sorts on the first/last results, so the optimization must be skipped
623+ SET enable_hashagg = off;
624+ :PREFIX SELECT DISTINCT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
625+ --- QUERY PLAN ---
626+ Unique (actual rows=1.00 loops=1)
627+ -> Sort (actual rows=1.00 loops=1)
628+ Sort Key: (first(_hyper_3_8_chunk.val, _hyper_3_8_chunk."time")), (first(_hyper_3_8_chunk.val, _hyper_3_8_chunk.time_alt))
629+ Sort Method: quicksort
630+ -> Aggregate (actual rows=1.00 loops=1)
631+ -> Seq Scan on _hyper_3_8_chunk (actual rows=200.00 loops=1)
632+
633+ RESET enable_hashagg;
584634ROLLBACK;
585635-- Test with NULL numeric values
586636BEGIN;
@@ -606,25 +656,25 @@ INSERT INTO btest_numeric VALUES('2018-01-20T09:00:43', NULL);
606656 Result (actual rows=1.00 loops=1)
607657 InitPlan 1 (returns $0)
608658 -> Limit (actual rows=1.00 loops=1)
609- -> Index Scan Backward using _hyper_2_8_chunk_btest_numeric_time_idx on _hyper_2_8_chunk (actual rows=1.00 loops=1)
659+ -> Index Scan Backward using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (actual rows=1.00 loops=1)
610660
611661:PREFIX SELECT last(quantity, time) FROM btest_numeric;
612662--- QUERY PLAN ---
613663 Result (actual rows=1.00 loops=1)
614664 InitPlan 1 (returns $0)
615665 -> Limit (actual rows=1.00 loops=1)
616- -> Index Scan using _hyper_2_8_chunk_btest_numeric_time_idx on _hyper_2_8_chunk (actual rows=1.00 loops=1)
666+ -> Index Scan using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (actual rows=1.00 loops=1)
617667 Index Cond: ("time" IS NOT NULL)
618668
619669:PREFIX SELECT first(time, quantity) FROM btest_numeric;
620670--- QUERY PLAN ---
621671 Aggregate (actual rows=1.00 loops=1)
622- -> Seq Scan on _hyper_2_8_chunk (actual rows=2.00 loops=1)
672+ -> Seq Scan on _hyper_2_9_chunk (actual rows=2.00 loops=1)
623673
624674:PREFIX SELECT last(time, quantity) FROM btest_numeric;
625675--- QUERY PLAN ---
626676 Aggregate (actual rows=1.00 loops=1)
627- -> Seq Scan on _hyper_2_8_chunk (actual rows=2.00 loops=1)
677+ -> Seq Scan on _hyper_2_9_chunk (actual rows=2.00 loops=1)
628678
629679-- NULL values followed by non-NULL values
630680INSERT INTO btest_numeric VALUES('2019-01-20T09:00:43', 1);
@@ -636,8 +686,8 @@ INSERT INTO btest_numeric VALUES('2019-01-20T09:00:43', 2);
636686 -> Limit (actual rows=1.00 loops=1)
637687 -> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1.00 loops=1)
638688 Order: btest_numeric."time"
639- -> Index Scan Backward using _hyper_2_8_chunk_btest_numeric_time_idx on _hyper_2_8_chunk (actual rows=1.00 loops=1)
640- -> Index Scan Backward using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (never executed)
689+ -> Index Scan Backward using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (actual rows=1.00 loops=1)
690+ -> Index Scan Backward using _hyper_2_10_chunk_btest_numeric_time_idx on _hyper_2_10_chunk (never executed)
641691
642692:PREFIX SELECT last(quantity, time) FROM btest_numeric;
643693--- QUERY PLAN ---
@@ -646,24 +696,24 @@ INSERT INTO btest_numeric VALUES('2019-01-20T09:00:43', 2);
646696 -> Limit (actual rows=1.00 loops=1)
647697 -> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1.00 loops=1)
648698 Order: btest_numeric."time" DESC
649- -> Index Scan using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (actual rows=1.00 loops=1)
699+ -> Index Scan using _hyper_2_10_chunk_btest_numeric_time_idx on _hyper_2_10_chunk (actual rows=1.00 loops=1)
650700 Index Cond: ("time" IS NOT NULL)
651- -> Index Scan using _hyper_2_8_chunk_btest_numeric_time_idx on _hyper_2_8_chunk (never executed)
701+ -> Index Scan using _hyper_2_9_chunk_btest_numeric_time_idx on _hyper_2_9_chunk (never executed)
652702 Index Cond: ("time" IS NOT NULL)
653703
654704:PREFIX SELECT first(time, quantity) FROM btest_numeric;
655705--- QUERY PLAN ---
656706 Aggregate (actual rows=1.00 loops=1)
657707 -> Append (actual rows=4.00 loops=1)
658- -> Seq Scan on _hyper_2_8_chunk (actual rows=2.00 loops=1)
659708 -> Seq Scan on _hyper_2_9_chunk (actual rows=2.00 loops=1)
709+ -> Seq Scan on _hyper_2_10_chunk (actual rows=2.00 loops=1)
660710
661711:PREFIX SELECT last(time, quantity) FROM btest_numeric;
662712--- QUERY PLAN ---
663713 Aggregate (actual rows=1.00 loops=1)
664714 -> Append (actual rows=4.00 loops=1)
665- -> Seq Scan on _hyper_2_8_chunk (actual rows=2.00 loops=1)
666715 -> Seq Scan on _hyper_2_9_chunk (actual rows=2.00 loops=1)
716+ -> Seq Scan on _hyper_2_10_chunk (actual rows=2.00 loops=1)
667717
668718TRUNCATE btest_numeric;
669719-- non-NULL values followed by NULL values
@@ -678,8 +728,8 @@ INSERT INTO btest_numeric VALUES('2018-01-20T09:00:43', NULL);
678728 -> Limit (actual rows=1.00 loops=1)
679729 -> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1.00 loops=1)
680730 Order: btest_numeric."time"
681- -> Index Scan Backward using _hyper_2_11_chunk_btest_numeric_time_idx on _hyper_2_11_chunk (actual rows=1.00 loops=1)
682- -> Index Scan Backward using _hyper_2_10_chunk_btest_numeric_time_idx on _hyper_2_10_chunk (never executed)
731+ -> Index Scan Backward using _hyper_2_12_chunk_btest_numeric_time_idx on _hyper_2_12_chunk (actual rows=1.00 loops=1)
732+ -> Index Scan Backward using _hyper_2_11_chunk_btest_numeric_time_idx on _hyper_2_11_chunk (never executed)
683733
684734:PREFIX SELECT last(quantity, time) FROM btest_numeric;
685735--- QUERY PLAN ---
@@ -688,24 +738,24 @@ INSERT INTO btest_numeric VALUES('2018-01-20T09:00:43', NULL);
688738 -> Limit (actual rows=1.00 loops=1)
689739 -> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1.00 loops=1)
690740 Order: btest_numeric."time" DESC
691- -> Index Scan using _hyper_2_10_chunk_btest_numeric_time_idx on _hyper_2_10_chunk (actual rows=1.00 loops=1)
741+ -> Index Scan using _hyper_2_11_chunk_btest_numeric_time_idx on _hyper_2_11_chunk (actual rows=1.00 loops=1)
692742 Index Cond: ("time" IS NOT NULL)
693- -> Index Scan using _hyper_2_11_chunk_btest_numeric_time_idx on _hyper_2_11_chunk (never executed)
743+ -> Index Scan using _hyper_2_12_chunk_btest_numeric_time_idx on _hyper_2_12_chunk (never executed)
694744 Index Cond: ("time" IS NOT NULL)
695745
696746:PREFIX SELECT first(time, quantity) FROM btest_numeric;
697747--- QUERY PLAN ---
698748 Aggregate (actual rows=1.00 loops=1)
699749 -> Append (actual rows=4.00 loops=1)
700- -> Seq Scan on _hyper_2_10_chunk (actual rows=2.00 loops=1)
701750 -> Seq Scan on _hyper_2_11_chunk (actual rows=2.00 loops=1)
751+ -> Seq Scan on _hyper_2_12_chunk (actual rows=2.00 loops=1)
702752
703753:PREFIX SELECT last(time, quantity) FROM btest_numeric;
704754--- QUERY PLAN ---
705755 Aggregate (actual rows=1.00 loops=1)
706756 -> Append (actual rows=4.00 loops=1)
707- -> Seq Scan on _hyper_2_10_chunk (actual rows=2.00 loops=1)
708757 -> Seq Scan on _hyper_2_11_chunk (actual rows=2.00 loops=1)
758+ -> Seq Scan on _hyper_2_12_chunk (actual rows=2.00 loops=1)
709759
710760ROLLBACK;
711761-- we want test results as part of the output too to make sure we produce correct output
@@ -989,6 +1039,40 @@ CREATE INDEX btest_time_alt_idx ON btest(time_alt);
9891039------
9901040 35.3
9911041
1042+ ROLLBACK;
1043+ -- two FIRST/LAST sharing the value column but ordering on different columns
1044+ BEGIN;
1045+ CREATE TABLE bookend_two_orderings(time timestamptz NOT NULL, time_alt timestamptz NOT NULL, val int NOT NULL);
1046+ SELECT schema_name, table_name, created FROM create_hypertable('bookend_two_orderings', 'time');
1047+ schema_name | table_name | created
1048+ -------------+-----------------------+---------
1049+ public | bookend_two_orderings | t
1050+
1051+ INSERT INTO bookend_two_orderings
1052+ SELECT '2025-01-01'::timestamptz + g * interval '1 minute',
1053+ '2025-01-01'::timestamptz + (201 - g) * interval '1 minute',
1054+ g
1055+ FROM generate_series(1, 200) g;
1056+ CREATE INDEX ON bookend_two_orderings(time);
1057+ CREATE INDEX ON bookend_two_orderings(time_alt);
1058+ :PREFIX SELECT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1059+ first | first
1060+ -------+-------
1061+ 1 | 200
1062+
1063+ :PREFIX SELECT last(val, time), last(val, time_alt) FROM bookend_two_orderings;
1064+ last | last
1065+ ------+------
1066+ 200 | 1
1067+
1068+ -- DISTINCT sorts on the first/last results, so the optimization must be skipped
1069+ SET enable_hashagg = off;
1070+ :PREFIX SELECT DISTINCT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1071+ first | first
1072+ -------+-------
1073+ 1 | 200
1074+
1075+ RESET enable_hashagg;
9921076ROLLBACK;
9931077-- Test with NULL numeric values
9941078BEGIN;
@@ -1169,6 +1253,24 @@ CREATE INDEX btest_time_alt_idx ON btest(time_alt);
11691253-- test nested FIRST/LAST in ORDER BY - no optimization possible
11701254:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
11711255ROLLBACK;
1256+ -- two FIRST/LAST sharing the value column but ordering on different columns
1257+ BEGIN;
1258+ CREATE TABLE bookend_two_orderings(time timestamptz NOT NULL, time_alt timestamptz NOT NULL, val int NOT NULL);
1259+ SELECT schema_name, table_name, created FROM create_hypertable('bookend_two_orderings', 'time');
1260+ INSERT INTO bookend_two_orderings
1261+ SELECT '2025-01-01'::timestamptz + g * interval '1 minute',
1262+ '2025-01-01'::timestamptz + (201 - g) * interval '1 minute',
1263+ g
1264+ FROM generate_series(1, 200) g;
1265+ CREATE INDEX ON bookend_two_orderings(time);
1266+ CREATE INDEX ON bookend_two_orderings(time_alt);
1267+ :PREFIX SELECT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1268+ :PREFIX SELECT last(val, time), last(val, time_alt) FROM bookend_two_orderings;
1269+ -- DISTINCT sorts on the first/last results, so the optimization must be skipped
1270+ SET enable_hashagg = off;
1271+ :PREFIX SELECT DISTINCT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1272+ RESET enable_hashagg;
1273+ ROLLBACK;
11721274-- Test with NULL numeric values
11731275BEGIN;
11741276TRUNCATE btest_numeric;
@@ -1292,6 +1394,24 @@ CREATE INDEX btest_time_alt_idx ON btest(time_alt);
12921394-- test nested FIRST/LAST in ORDER BY - no optimization possible
12931395:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
12941396ROLLBACK;
1397+ -- two FIRST/LAST sharing the value column but ordering on different columns
1398+ BEGIN;
1399+ CREATE TABLE bookend_two_orderings(time timestamptz NOT NULL, time_alt timestamptz NOT NULL, val int NOT NULL);
1400+ SELECT schema_name, table_name, created FROM create_hypertable('bookend_two_orderings', 'time');
1401+ INSERT INTO bookend_two_orderings
1402+ SELECT '2025-01-01'::timestamptz + g * interval '1 minute',
1403+ '2025-01-01'::timestamptz + (201 - g) * interval '1 minute',
1404+ g
1405+ FROM generate_series(1, 200) g;
1406+ CREATE INDEX ON bookend_two_orderings(time);
1407+ CREATE INDEX ON bookend_two_orderings(time_alt);
1408+ :PREFIX SELECT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1409+ :PREFIX SELECT last(val, time), last(val, time_alt) FROM bookend_two_orderings;
1410+ -- DISTINCT sorts on the first/last results, so the optimization must be skipped
1411+ SET enable_hashagg = off;
1412+ :PREFIX SELECT DISTINCT first(val, time), first(val, time_alt) FROM bookend_two_orderings;
1413+ RESET enable_hashagg;
1414+ ROLLBACK;
12951415-- Test with NULL numeric values
12961416BEGIN;
12971417TRUNCATE btest_numeric;
0 commit comments