On Fri, 4 Sept 2026 at 16:59, Michael Paquier <[email protected]> wrote:
> Any thoughts or comments from others?

I think if we don't add overflow error checking for sum(int2) and
sum(int4) today, we'll need to do it at some point in the future. I
suspect we've only gotten away with it for this long, not because
nobody aggregates 4+ billion rows, but because the values being
aggregated are unlikely to be large enough to cause the overflow.

Since Andrei has demonstrated that it's possible to hit that limit
with a non-parallel query in less than 5 minutes, albeit that is
passing INT_MAX (the most extreme case), it might be worth adding the
checks. I was surprised that it only took 5 mins to do 4 billion rows,
especially with generate_series. It's probably just a matter of time
before someone discovers this with a real-world case out in the wild.

If there's a measurable performance regression from adding the
overflow checks, does the attached buy enough of it back? I couldn't
really measure much of a performance difference from it on my Zen2
machine, so I didn't try with the overflow patch.

The patch adds PG_RETURN_INPUT(n) to avoid some of the branching in
int4_sum() so that it immediately returns the aggstate when the value
being aggregated is null. With my compiler, it cut int4_sum from 18
down to 16 instructions.

David
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 37f24e33857..79295d43ddf 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -6378,22 +6378,15 @@ int4_sum(PG_FUNCTION_ARGS)
        int64           oldsum;
        int64           newval;
 
+       /* Return the left input unchanged if right input is null. */
+       if (PG_ARGISNULL(1))
+               PG_RETURN_INPUT(0);
+
        if (PG_ARGISNULL(0))
-       {
-               /* No non-null input seen so far... */
-               if (PG_ARGISNULL(1))
-                       PG_RETURN_NULL();       /* still no non-null */
-               /* This is the first non-null input. */
-               newval = (int64) PG_GETARG_INT32(1);
-               PG_RETURN_INT64(newval);
-       }
+               PG_RETURN_INT64((int64) PG_GETARG_INT32(1));
 
        oldsum = PG_GETARG_INT64(0);
 
-       /* Leave sum unchanged if new input is null. */
-       if (PG_ARGISNULL(1))
-               PG_RETURN_INT64(oldsum);
-
        /* OK to do the addition. */
        newval = oldsum + (int64) PG_GETARG_INT32(1);
 
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 04b7914095f..e3c2b3ac686 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -349,6 +349,16 @@ extern varlena *pg_detoast_datum_packed(varlena *datum);
 /* A few internal functions return void (which is not the same as NULL!) */
 #define PG_RETURN_VOID()        return (Datum) 0
 
+/*
+ * A shortcut to allow functions to return the value of the give input
+ * parameter, NULL if that parameter was NULL and the value of the parameter
+ * otherwise.  The caller is responsible for ensuring the types match.
+ */
+#define PG_RETURN_INPUT(n)  do { \
+               fcinfo->isnull = fcinfo->args[n].isnull; \
+               return fcinfo->args[n].value; \
+       } while (0)
+
 /* Macros for returning results of standard types */
 
 #define PG_RETURN_DATUM(x)      return (x)

Reply via email to