aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/nodeAgg.c11
-rw-r--r--src/test/regress/expected/aggregates.out71
-rw-r--r--src/test/regress/sql/aggregates.sql63
3 files changed, 145 insertions, 0 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index d26ce0847a0..da6ef1a94c4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1246,6 +1246,17 @@ advance_combine_function(AggState *aggstate,
pergroupstate->noTransValue = false;
return;
}
+
+ if (pergroupstate->transValueIsNull)
+ {
+ /*
+ * Don't call a strict function with NULL inputs. Note it is
+ * possible to get here despite the above tests, if the combinefn
+ * is strict *and* returned a NULL on a prior cycle. If that
+ * happens we will propagate the NULL all the way to the end.
+ */
+ return;
+ }
}
/* We run the combine functions in per-input-tuple memory context */
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 3408cf3333e..f8c42f911b5 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -1993,3 +1993,74 @@ NOTICE: sum_transfn called with 4
(1 row)
rollback;
+-- test that the aggregate transition logic correctly handles
+-- transition / combine functions returning NULL
+-- First test the case of a normal transition function returning NULL
+BEGIN;
+CREATE FUNCTION balkifnull(int8, int4)
+RETURNS int8
+STRICT
+LANGUAGE plpgsql AS $$
+BEGIN
+ IF $1 IS NULL THEN
+ RAISE 'erroneously called with NULL argument';
+ END IF;
+ RETURN NULL;
+END$$;
+CREATE AGGREGATE balk(
+ BASETYPE = int4,
+ SFUNC = balkifnull(int8, int4),
+ STYPE = int8,
+ "PARALLEL" = SAFE,
+ INITCOND = '0');
+SELECT balk(1) FROM tenk1;
+ balk
+------
+
+(1 row)
+
+ROLLBACK;
+-- Secondly test the case of a parallel aggregate combiner function
+-- returning NULL. For that use normal transition function, but a
+-- combiner function returning NULL.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+CREATE FUNCTION balkifnull(int8, int8)
+RETURNS int8
+PARALLEL SAFE
+STRICT
+LANGUAGE plpgsql AS $$
+BEGIN
+ IF $1 IS NULL THEN
+ RAISE 'erroneously called with NULL argument';
+ END IF;
+ RETURN NULL;
+END$$;
+CREATE AGGREGATE balk(
+ BASETYPE = int4,
+ SFUNC = int4_sum(int8, int4),
+ STYPE = int8,
+ COMBINEFUNC = balkifnull(int8, int8),
+ "PARALLEL" = SAFE,
+ INITCOND = '0'
+);
+-- force use of parallelism
+ALTER TABLE tenk1 set (parallel_workers = 4);
+SET LOCAL parallel_setup_cost=0;
+SET LOCAL max_parallel_workers_per_gather=4;
+EXPLAIN (COSTS OFF) SELECT balk(1) FROM tenk1;
+ QUERY PLAN
+--------------------------------------------------------------------------------
+ Finalize Aggregate
+ -> Gather
+ Workers Planned: 4
+ -> Partial Aggregate
+ -> Parallel Index Only Scan using tenk1_thous_tenthous on tenk1
+(5 rows)
+
+SELECT balk(1) FROM tenk1;
+ balk
+------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 55c8528fd57..1bfc5e649c3 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -843,3 +843,66 @@ create aggregate my_half_sum(int4)
select my_sum(one),my_half_sum(one) from (values(1),(2),(3),(4)) t(one);
rollback;
+
+
+-- test that the aggregate transition logic correctly handles
+-- transition / combine functions returning NULL
+
+-- First test the case of a normal transition function returning NULL
+BEGIN;
+CREATE FUNCTION balkifnull(int8, int4)
+RETURNS int8
+STRICT
+LANGUAGE plpgsql AS $$
+BEGIN
+ IF $1 IS NULL THEN
+ RAISE 'erroneously called with NULL argument';
+ END IF;
+ RETURN NULL;
+END$$;
+
+CREATE AGGREGATE balk(
+ BASETYPE = int4,
+ SFUNC = balkifnull(int8, int4),
+ STYPE = int8,
+ "PARALLEL" = SAFE,
+ INITCOND = '0');
+
+SELECT balk(1) FROM tenk1;
+
+ROLLBACK;
+
+-- Secondly test the case of a parallel aggregate combiner function
+-- returning NULL. For that use normal transition function, but a
+-- combiner function returning NULL.
+BEGIN ISOLATION LEVEL REPEATABLE READ;
+CREATE FUNCTION balkifnull(int8, int8)
+RETURNS int8
+PARALLEL SAFE
+STRICT
+LANGUAGE plpgsql AS $$
+BEGIN
+ IF $1 IS NULL THEN
+ RAISE 'erroneously called with NULL argument';
+ END IF;
+ RETURN NULL;
+END$$;
+
+CREATE AGGREGATE balk(
+ BASETYPE = int4,
+ SFUNC = int4_sum(int8, int4),
+ STYPE = int8,
+ COMBINEFUNC = balkifnull(int8, int8),
+ "PARALLEL" = SAFE,
+ INITCOND = '0'
+);
+
+-- force use of parallelism
+ALTER TABLE tenk1 set (parallel_workers = 4);
+SET LOCAL parallel_setup_cost=0;
+SET LOCAL max_parallel_workers_per_gather=4;
+
+EXPLAIN (COSTS OFF) SELECT balk(1) FROM tenk1;
+SELECT balk(1) FROM tenk1;
+
+ROLLBACK;