From 56410d265d439ed479b210e57242476e15bb6edb 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] 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 and money functions guard the negation of the
most negative value by open-coding an explicit "x == PG_INTnn_MIN" test
before computing -x.  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 remaining 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.

---
 src/backend/utils/adt/cash.c    |  9 +++++----
 src/backend/utils/adt/int.c     | 33 ++++++++++++++-------------------
 src/backend/utils/adt/int8.c    | 22 +++++++---------------
 src/backend/utils/adt/numeric.c |  3 +--
 4 files changed, 27 insertions(+), 40 deletions(-)

diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 310b3bb3fca..5fa5288629f 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -169,11 +169,13 @@ cash_div_int64(Cash c, int64 i)
 	 */
 	if (i == -1)
 	{
-		if (unlikely(c == PG_INT64_MIN))
+		Cash		result;
+
+		if (unlikely(pg_neg_s64_overflow(c, &result)))
 			ereport(ERROR,
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("money out of range")));
-		return -c;
+		return result;
 	}
 
 	/* 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/int.c b/src/backend/utils/adt/int.c
index 01608d8ca42..d4aedb2ffbc 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 (unlikely(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 (unlikely(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 (unlikely(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 (unlikely(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 (unlikely(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,11 @@ int4abs(PG_FUNCTION_ARGS)
 	int32		arg1 = PG_GETARG_INT32(0);
 	int32		result;
 
-	if (unlikely(arg1 == PG_INT32_MIN))
+	result = arg1;
+	if (arg1 < 0 && unlikely(pg_neg_s32_overflow(arg1, &result)))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
 	PG_RETURN_INT32(result);
 }
 
@@ -1237,11 +1236,11 @@ int2abs(PG_FUNCTION_ARGS)
 	int16		arg1 = PG_GETARG_INT16(0);
 	int16		result;
 
-	if (unlikely(arg1 == PG_INT16_MIN))
+	result = arg1;
+	if (arg1 < 0 && unlikely(pg_neg_s16_overflow(arg1, &result)))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
 	PG_RETURN_INT16(result);
 }
 
@@ -1360,15 +1359,11 @@ int4lcm(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
 
-	/* If the result is INT_MIN, it cannot be represented. */
-	if (unlikely(result == PG_INT32_MIN))
+	if (result < 0 && unlikely(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..72390e985c3 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 (unlikely(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 (unlikely(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,11 @@ int8abs(PG_FUNCTION_ARGS)
 	int64		arg1 = PG_GETARG_INT64(0);
 	int64		result;
 
-	if (unlikely(arg1 == PG_INT64_MIN))
+	result = arg1;
+	if (arg1 < 0 && unlikely(pg_neg_s64_overflow(arg1, &result)))
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
-	result = (arg1 < 0) ? -arg1 : arg1;
 	PG_RETURN_INT64(result);
 }
 
@@ -712,15 +710,11 @@ int8lcm(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
 
-	/* If the result is INT64_MIN, it cannot be represented. */
-	if (unlikely(result == PG_INT64_MIN))
+	if (result < 0 && unlikely(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 +985,10 @@ int84div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT64_MIN))
+		if (unlikely(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 +1126,10 @@ int82div(PG_FUNCTION_ARGS)
 	 */
 	if (arg2 == -1)
 	{
-		if (unlikely(arg1 == PG_INT64_MIN))
+		if (unlikely(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

