Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .unreleased/fix_10059
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixes: #10059 Fix error when using first/last aggregates in a HAVING clause
8 changes: 5 additions & 3 deletions src/planner/agg_bookend.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
164 changes: 164 additions & 0 deletions test/expected/agg_bookends-15.out
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down
Loading
Loading