From 4f25b7bb416f0268e40cbd909dba9c898c261eff Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Thu, 27 Aug 2026 18:06:09 +0800
Subject: [PATCH 1/2] Use pg_neg_s{16,32,64}_overflow() for open-coded negation
 overflow checks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Several integer, bigint, money and interval-input functions guard the
negation of the most negative value by open-coding an explicit
"x == PG_INTnn_MIN" test before computing -x.  common/int.h already
provides pg_neg_s16_overflow(), pg_neg_s32_overflow() and
pg_neg_s64_overflow() for exactly this, which use
__builtin_sub_overflow() where available and fall back to the same
open-coded test otherwise.  Convert the hand-rolled checks to use these
helpers, as suggested by Dagfinn Ilmari Mannsåker.

This is a cleanup with no behavioral change: every converted site keeps
its existing error (or soft-error) path, and on platforms without
__builtin_sub_overflow() the inlined fallback is identical to the code
being replaced.

Discussion: https://postgr.es/m/CAON2xHO4tTFiow2KKLbpiOEJNL4Th5p1QVTm4mSdEU+KW1rrfQ@mail.gmail.com
---
 src/backend/utils/adt/cash.c     |  9 +++---
 src/backend/utils/adt/datetime.c | 17 ++++-------
 src/backend/utils/adt/int.c      | 52 +++++++++++++++++---------------
 src/backend/utils/adt/int8.c     | 31 +++++++++----------
 src/backend/utils/adt/numeric.c  |  3 +-
 5 files changed, 54 insertions(+), 58 deletions(-)

diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 310b3bb3fca..0820187b8e6 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -156,6 +156,8 @@ cash_mul_int64(Cash c, int64 i)
 static inline Cash
 cash_div_int64(Cash c, int64 i)
 {
+	Cash		res;
+
 	if (unlikely(i == 0))
 		ereport(ERROR,
 				(errcode(ERRCODE_DIVISION_BY_ZERO),
@@ -169,11 +171,11 @@ cash_div_int64(Cash c, int64 i)
 	 */
 	if (i == -1)
 	{
-		if (unlikely(c == PG_INT64_MIN))
+		if (pg_neg_s64_overflow(c, &res))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("money out of range")));
-		return -c;
+		return res;
 	}
 
 	/* No overflow is possible */
@@ -379,12 +381,11 @@ cash_in(PG_FUNCTION_ARGS)
 	 */
 	if (sgn > 0)
 	{
-		if (value == PG_INT64_MIN)
+		if (pg_neg_s64_overflow(value, &result))
 			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("value \"%s\" is out of range for type %s",
 							str, "money")));
-		result = -value;
 	}
 	else
 		result = value;
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 04ebc632178..32be8480dda 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -3595,9 +3595,9 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
 					if (*field[i] == '-')
 					{
 						/* flip the sign on time field */
-						if (itm_in->tm_usec == PG_INT64_MIN)
+						if (pg_neg_s64_overflow(itm_in->tm_usec,
+												&itm_in->tm_usec))
 							return DTERR_FIELD_OVERFLOW;
-						itm_in->tm_usec = -itm_in->tm_usec;
 					}
 
 					if (force_negative &&
@@ -3880,16 +3880,11 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
 	/* finally, AGO negates everything */
 	if (is_before)
 	{
-		if (itm_in->tm_usec == PG_INT64_MIN ||
-			itm_in->tm_mday == INT_MIN ||
-			itm_in->tm_mon == INT_MIN ||
-			itm_in->tm_year == INT_MIN)
+		if (pg_neg_s64_overflow(itm_in->tm_usec, &itm_in->tm_usec) ||
+			pg_neg_s32_overflow(itm_in->tm_mday, &itm_in->tm_mday) ||
+			pg_neg_s32_overflow(itm_in->tm_mon, &itm_in->tm_mon) ||
+			pg_neg_s32_overflow(itm_in->tm_year, &itm_in->tm_year))
 			return DTERR_FIELD_OVERFLOW;
-
-		itm_in->tm_usec = -itm_in->tm_usec;
-		itm_in->tm_mday = -itm_in->tm_mday;
-		itm_in->tm_mon = -itm_in->tm_mon;
-		itm_in->tm_year = -itm_in->tm_year;
 	}
 
 	return 0;
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 01608d8ca42..8e2fdbe2be0 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -800,12 +800,13 @@ Datum
 int4um(PG_FUNCTION_ARGS)
 {
 	int32		arg = PG_GETARG_INT32(0);
+	int32		result;
 
-	if (unlikely(arg == PG_INT32_MIN))
+	if (pg_neg_s32_overflow(arg, &result))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
-	PG_RETURN_INT32(-arg);
+	PG_RETURN_INT32(result);
 }
 
 Datum
@@ -882,11 +883,10 @@ int4div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT32_MIN))
+		if (pg_neg_s32_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("integer out of range")));
-		result = -arg1;
 		PG_RETURN_INT32(result);
 	}
 
@@ -915,12 +915,13 @@ Datum
 int2um(PG_FUNCTION_ARGS)
 {
 	int16		arg = PG_GETARG_INT16(0);
+	int16		result;
 
-	if (unlikely(arg == PG_INT16_MIN))
+	if (pg_neg_s16_overflow(arg, &result))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
-	PG_RETURN_INT16(-arg);
+	PG_RETURN_INT16(result);
 }
 
 Datum
@@ -998,11 +999,10 @@ int2div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT16_MIN))
+		if (pg_neg_s16_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("smallint out of range")));
-		result = -arg1;
 		PG_RETURN_INT16(result);
 	}
 
@@ -1140,11 +1140,10 @@ int42div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT32_MIN))
+		if (pg_neg_s32_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("integer out of range")));
-		result = -arg1;
 		PG_RETURN_INT32(result);
 	}
 
@@ -1223,11 +1222,15 @@ int4abs(PG_FUNCTION_ARGS)
 	int32		arg1 = PG_GETARG_INT32(0);
 	int32		result;
 
-	if (unlikely(arg1 == PG_INT32_MIN))
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("integer out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
+	if (arg1 < 0)
+	{
+		if (pg_neg_s32_overflow(arg1, &result))
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+	else
+		result = arg1;
 	PG_RETURN_INT32(result);
 }
 
@@ -1237,11 +1240,15 @@ int2abs(PG_FUNCTION_ARGS)
 	int16		arg1 = PG_GETARG_INT16(0);
 	int16		result;
 
-	if (unlikely(arg1 == PG_INT16_MIN))
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("smallint out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
+	if (arg1 < 0)
+	{
+		if (pg_neg_s16_overflow(arg1, &result))
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("smallint out of range")));
+	}
+	else
+		result = arg1;
 	PG_RETURN_INT16(result);
 }
 
@@ -1361,14 +1368,11 @@ int4lcm(PG_FUNCTION_ARGS)
 				 errmsg("integer out of range")));
 
 	/* If the result is INT_MIN, it cannot be represented. */
-	if (unlikely(result == PG_INT32_MIN))
+	if (result < 0 && pg_neg_s32_overflow(result, &result))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
 
-	if (result < 0)
-		result = -result;
-
 	PG_RETURN_INT32(result);
 }
 
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 1f59d831600..5a59de0f5c4 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -449,11 +449,10 @@ int8um(PG_FUNCTION_ARGS)
 	int64		arg = PG_GETARG_INT64(0);
 	int64		result;
 
-	if (unlikely(arg == PG_INT64_MIN))
+	if (pg_neg_s64_overflow(arg, &result))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
-	result = -arg;
 	PG_RETURN_INT64(result);
 }
 
@@ -531,11 +530,10 @@ int8div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT64_MIN))
+		if (pg_neg_s64_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("bigint out of range")));
-		result = -arg1;
 		PG_RETURN_INT64(result);
 	}
 
@@ -556,11 +554,15 @@ int8abs(PG_FUNCTION_ARGS)
 	int64		arg1 = PG_GETARG_INT64(0);
 	int64		result;
 
-	if (unlikely(arg1 == PG_INT64_MIN))
-		ereport(ERROR,
-				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-				 errmsg("bigint out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
+	if (arg1 < 0)
+	{
+		if (pg_neg_s64_overflow(arg1, &result))
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("bigint out of range")));
+	}
+	else
+		result = arg1;
 	PG_RETURN_INT64(result);
 }
 
@@ -713,14 +715,11 @@ int8lcm(PG_FUNCTION_ARGS)
 				 errmsg("bigint out of range")));
 
 	/* If the result is INT64_MIN, it cannot be represented. */
-	if (unlikely(result == PG_INT64_MIN))
+	if (result < 0 && pg_neg_s64_overflow(result, &result))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
 
-	if (result < 0)
-		result = -result;
-
 	PG_RETURN_INT64(result);
 }
 
@@ -991,11 +990,10 @@ int84div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT64_MIN))
+		if (pg_neg_s64_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("bigint out of range")));
-		result = -arg1;
 		PG_RETURN_INT64(result);
 	}
 
@@ -1133,11 +1131,10 @@ int82div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT64_MIN))
+		if (pg_neg_s64_overflow(arg1, &result))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("bigint out of range")));
-		result = -arg1;
 		PG_RETURN_INT64(result);
 	}
 
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 523cd3cd608..37f24e33857 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -7870,9 +7870,8 @@ numericvar_to_int64(const NumericVar *var, int64 *result)
 
 	if (!neg)
 	{
-		if (unlikely(val == PG_INT64_MIN))
+		if (unlikely(pg_neg_s64_overflow(val, &val)))
 			return false;
-		val = -val;
 	}
 	*result = val;
 
-- 
2.47.3

