diff --git a/.unreleased/fix_10059 b/.unreleased/fix_10059 new file mode 100644 index 00000000000..323c7dadae5 --- /dev/null +++ b/.unreleased/fix_10059 @@ -0,0 +1 @@ +Fixes: #10059 Fix error when using first/last aggregates in a HAVING clause diff --git a/src/planner/agg_bookend.c b/src/planner/agg_bookend.c index 4096cb7a4ab..f7a8c4c66cd 100644 --- a/src/planner/agg_bookend.c +++ b/src/planner/agg_bookend.c @@ -143,6 +143,10 @@ replace_aggref_in_tlist(MinMaxAggPath *minmaxagg_path, List *first_last_aggs) ((Path *) minmaxagg_path)->pathtarget->exprs = (List *) mutate_aggref_node((Node *) ((Path *) minmaxagg_path)->pathtarget->exprs, (void *) &context); + + /* HAVING quals can also reference first/last aggregates */ + minmaxagg_path->quals = + (List *) mutate_aggref_node((Node *) minmaxagg_path->quals, (void *) &context); } static StrategyNumber @@ -538,9 +542,7 @@ find_first_last_aggs_walker(Node *node, List **context) { FirstLastAggInfo *existing = (FirstLastAggInfo *) lfirst(l); - mminfo = existing->m_agg_info; - if (mminfo->aggfnoid == aggref->aggfnoid && equal(mminfo->target, value->expr) && - equal(existing->sort, sort->expr)) + if (equal(existing->aggref, aggref)) { return false; } diff --git a/test/expected/agg_bookends-15.out b/test/expected/agg_bookends-15.out index 3839d5da64d..a06d863ab38 100644 --- a/test/expected/agg_bookends-15.out +++ b/test/expected/agg_bookends-15.out @@ -445,6 +445,126 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); -> Seq Scan on _hyper_1_4_chunk (actual rows=1.00 loops=1) -> Seq Scan on _hyper_1_5_chunk (actual rows=1.00 loops=1) +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($0 IS NOT NULL) + InitPlan 1 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($0 > '0'::double precision) + InitPlan 1 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 IS NOT NULL) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 > '0'::double precision) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 IS NOT NULL) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; --- QUERY PLAN --- @@ -974,6 +1094,34 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); ------ 35.3 +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + last +------ + 35.3 + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; + first +------- + 36.5 + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + first +------- + 36.5 + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; + first +------- + 36.5 + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; + last +------ + 35.3 + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; last @@ -1233,6 +1381,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; @@ -1374,6 +1530,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; diff --git a/test/expected/agg_bookends-16.out b/test/expected/agg_bookends-16.out index 3839d5da64d..a06d863ab38 100644 --- a/test/expected/agg_bookends-16.out +++ b/test/expected/agg_bookends-16.out @@ -445,6 +445,126 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); -> Seq Scan on _hyper_1_4_chunk (actual rows=1.00 loops=1) -> Seq Scan on _hyper_1_5_chunk (actual rows=1.00 loops=1) +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($0 IS NOT NULL) + InitPlan 1 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($0 > '0'::double precision) + InitPlan 1 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 IS NOT NULL) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 > '0'::double precision) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ($1 IS NOT NULL) + InitPlan 1 (returns $1) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + Index Cond: ("time" IS NOT NULL) + InitPlan 2 (returns $0) + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (actual rows=1.00 loops=1) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (never executed) + Index Cond: ("time" IS NOT NULL) + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; --- QUERY PLAN --- @@ -974,6 +1094,34 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); ------ 35.3 +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + last +------ + 35.3 + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; + first +------- + 36.5 + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + first +------- + 36.5 + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; + first +------- + 36.5 + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; + last +------ + 35.3 + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; last @@ -1233,6 +1381,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; @@ -1374,6 +1530,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; diff --git a/test/expected/agg_bookends-17.out b/test/expected/agg_bookends-17.out index b9669403a9e..d417ef71901 100644 --- a/test/expected/agg_bookends-17.out +++ b/test/expected/agg_bookends-17.out @@ -410,6 +410,106 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); -> Seq Scan on _hyper_1_4_chunk (actual rows=1.00 loops=1) -> Seq Scan on _hyper_1_5_chunk (actual rows=1.00 loops=1) +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 > '0'::double precision) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 > '0'::double precision) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (never executed) + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; --- QUERY PLAN --- @@ -922,6 +1022,34 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); ------ 35.3 +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + last +------ + 35.3 + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; + first +------- + 36.5 + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + first +------- + 36.5 + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; + first +------- + 36.5 + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; + last +------ + 35.3 + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; last @@ -1181,6 +1309,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; @@ -1322,6 +1458,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; diff --git a/test/expected/agg_bookends-18.out b/test/expected/agg_bookends-18.out index b9669403a9e..d417ef71901 100644 --- a/test/expected/agg_bookends-18.out +++ b/test/expected/agg_bookends-18.out @@ -410,6 +410,106 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); -> Seq Scan on _hyper_1_4_chunk (actual rows=1.00 loops=1) -> Seq Scan on _hyper_1_5_chunk (actual rows=1.00 loops=1) +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 > '0'::double precision) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 > '0'::double precision) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" + -> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed) + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; +--- QUERY PLAN --- + Result (actual rows=1.00 loops=1) + One-Time Filter: ((InitPlan 1).col1 IS NOT NULL) + InitPlan 1 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest (actual rows=1.00 loops=1) + Order: btest."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed) + InitPlan 2 + -> Limit (actual rows=1.00 loops=1) + -> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1.00 loops=1) + Order: btest_1."time" DESC + -> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (actual rows=1.00 loops=1) + -> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed) + -> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed) + -> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed) + -> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (never executed) + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; --- QUERY PLAN --- @@ -922,6 +1022,34 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); ------ 35.3 +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + last +------ + 35.3 + +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; + first +------- + 36.5 + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + first +------- + 36.5 + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; + first +------- + 36.5 + +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; + last +------ + 35.3 + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; last @@ -1181,6 +1309,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; @@ -1322,6 +1458,14 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); :PREFIX SELECT max(time), last(temp, time) FROM btest; -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30; -- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47'; diff --git a/test/sql/include/agg_bookends_query.sql b/test/sql/include/agg_bookends_query.sql index 1988f006c3e..06c05dae977 100644 --- a/test/sql/include/agg_bookends_query.sql +++ b/test/sql/include/agg_bookends_query.sql @@ -92,6 +92,17 @@ INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5); -- can't do index scan when using FIRST/LAST in ORDER BY :PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time); +-- do index scan when using FIRST/LAST in HAVING +:PREFIX SELECT last(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; +:PREFIX SELECT first(temp, time) FROM btest HAVING first(temp, time) > 0; + +-- HAVING references a FIRST/LAST aggregate not present in the target list +:PREFIX SELECT first(temp, time) FROM btest HAVING last(temp, time) IS NOT NULL; + +-- DISTINCT in HAVING while the target list has the same aggregate without DISTINCT +:PREFIX SELECT first(temp, time) FROM btest HAVING first(DISTINCT temp, time) > 0; +:PREFIX SELECT last(temp, time) FROM btest HAVING last(DISTINCT temp, time) IS NOT NULL; + -- do index scan :PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;