[PATCH] Replace COUNT(NULL) with '0'::bigint
Started by Junwang Zhaoabout 2 hours ago1 messages
Hi,
In [1]/messages/by-id/CAApHDvrde9DUpQ3DhPd3ia9tchVmhZqewfzxSYWmYFWVj=LPpg@mail.gmail.com, David Rowley noted that COUNT(NULL) can be replaced
with '0'::bigint. The change should be straightforward, and I came
up with the attached patch to implement it.
[1]: /messages/by-id/CAApHDvrde9DUpQ3DhPd3ia9tchVmhZqewfzxSYWmYFWVj=LPpg@mail.gmail.com
--
Regards
Junwang Zhao
Attachments:
0001-Replace-COUNT-NULL-with-0-bigint.patchapplication/octet-stream; name=0001-Replace-COUNT-NULL-with-0-bigint.patchDownload
From 98c46bd8a6138e6dba4f55f35e2d719c5c80ee15 Mon Sep 17 00:00:00 2001
From: Junwang Zhao <zhjwpku@gmail.com>
Date: Sun, 25 Jan 2026 14:16:58 +0800
Subject: [PATCH] Replace COUNT(NULL) with '0'::bigint
When count() receives a constant NULL argument, replace it with
'0'::bigint constant instead of leaving it as NULL.
---
src/backend/utils/adt/int8.c | 16 ++++++++++++++++
src/test/regress/expected/aggregates.out | 15 +++++++--------
src/test/regress/sql/aggregates.sql | 2 +-
3 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 37d34685b93..438733684bd 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -20,6 +20,7 @@
#include "common/int.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/supportnodes.h"
#include "optimizer/optimizer.h"
@@ -855,6 +856,21 @@ int8inc_support(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(newagg);
}
+
+ /* Replace constant NULL argument with '0'::bigint */
+ if (IsA(arg, Const) && ((Const *) arg)->constisnull)
+ {
+ Const *newconst;
+
+ newconst = makeConst(INT8OID,
+ -1,
+ InvalidOid,
+ sizeof(int64),
+ Int64GetDatum(0),
+ false,
+ true);
+ PG_RETURN_POINTER(newconst);
+ }
}
}
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index cae8e7bca31..ef15e994e3f 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -2922,16 +2922,15 @@ select count('bananas') from agg_simplify;
Output: a, not_null_col, nullable_col
(4 rows)
--- Ensure count(null) isn't optimized
+-- Ensure count(null) is replaced with '0'::bigint
explain (costs off, verbose)
select count(null) from agg_simplify;
- QUERY PLAN
------------------------------------------------
- Aggregate
- Output: count(NULL::unknown)
- -> Seq Scan on public.agg_simplify
- Output: a, not_null_col, nullable_col
-(4 rows)
+ QUERY PLAN
+-----------------------------
+ Result
+ Output: '0'::bigint
+ Replaces: MinMaxAggregate
+(3 rows)
-- Ensure count(nullable_col) does not use count(*)
explain (costs off, verbose)
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 850f5a5787f..34990a09823 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -1145,7 +1145,7 @@ select count(not_null_col) from agg_simplify;
explain (costs off, verbose)
select count('bananas') from agg_simplify;
--- Ensure count(null) isn't optimized
+-- Ensure count(null) is replaced with '0'::bigint
explain (costs off, verbose)
select count(null) from agg_simplify;
--
2.41.0