From 3738b36237e21a9341abea8a599e7ed7fa899307 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 9 Aug 2025 21:33:56 +0800
Subject: [PATCH v6 05/18] error safe for casting numeric to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'numeric'::regtype
order by castsource::regtype;

castsource |    casttarget    | castfunc | castcontext | castmethod |     prosrc     | proname
------------+------------------+----------+-------------+------------+----------------+---------
 numeric    | bigint           |     1779 | a           | f          | numeric_int8   | int8
 numeric    | smallint         |     1783 | a           | f          | numeric_int2   | int2
 numeric    | integer          |     1744 | a           | f          | numeric_int4   | int4
 numeric    | real             |     1745 | i           | f          | numeric_float4 | float4
 numeric    | double precision |     1746 | i           | f          | numeric_float8 | float8
 numeric    | money            |     3824 | a           | f          | numeric_cash   | money
 numeric    | numeric          |     1703 | i           | f          | numeric        | numeric
(7 rows)

current safe cast for money data type is not supported, so no realted function
refactoring.
---
 src/backend/utils/adt/numeric.c | 69 ++++++++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 13 deletions(-)

diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 122f2efab8b..2ab26d59e41 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -1259,7 +1259,8 @@ numeric		(PG_FUNCTION_ARGS)
 	 */
 	if (NUMERIC_IS_SPECIAL(num))
 	{
-		(void) apply_typmod_special(num, typmod, NULL);
+		if (!apply_typmod_special(num, typmod, fcinfo->context))
+			PG_RETURN_NULL();
 		PG_RETURN_NUMERIC(duplicate_numeric(num));
 	}
 
@@ -1310,7 +1311,8 @@ numeric		(PG_FUNCTION_ARGS)
 	init_var(&var);
 
 	set_var_from_num(num, &var);
-	(void) apply_typmod(&var, typmod, NULL);
+	if (!apply_typmod(&var, typmod, fcinfo->context))
+		PG_RETURN_NULL();
 	new = make_result(&var);
 
 	free_var(&var);
@@ -4550,7 +4552,22 @@ numeric_int4(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
+	if (likely(!fcinfo->context))
+		PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
+	else
+	{
+		bool	has_error;
+		int32	result;
+		Node	*escontext = fcinfo->context;
+
+		result = numeric_int4_opt_error(num, &has_error);
+		if (has_error)
+			ereturn(escontext, (Datum) 0,
+					errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					errmsg("integer out of range"));
+
+		PG_RETURN_INT32(result);
+	}
 }
 
 /*
@@ -4638,7 +4655,22 @@ numeric_int8(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
+	if (likely(!fcinfo->context))
+		PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
+	else
+	{
+		bool	has_error;
+		int64	result;
+		Node	*escontext = fcinfo->context;
+
+		result = numeric_int8_opt_error(num, &has_error);
+		if (has_error)
+			ereturn(escontext, (Datum) 0,
+					errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					errmsg("bigint out of range"));
+
+		PG_RETURN_INT64(result);
+	}
 }
 
 
@@ -4662,11 +4694,11 @@ numeric_int2(PG_FUNCTION_ARGS)
 	if (NUMERIC_IS_SPECIAL(num))
 	{
 		if (NUMERIC_IS_NAN(num))
-			ereport(ERROR,
+			ereturn(fcinfo->context, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert NaN to %s", "smallint")));
 		else
-			ereport(ERROR,
+			ereturn(fcinfo->context, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert infinity to %s", "smallint")));
 	}
@@ -4675,12 +4707,12 @@ numeric_int2(PG_FUNCTION_ARGS)
 	init_var_from_num(num, &x);
 
 	if (!numericvar_to_int64(&x, &val))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
 	if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
@@ -4745,10 +4777,14 @@ numeric_float8(PG_FUNCTION_ARGS)
 
 	tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 											  NumericGetDatum(num)));
-
-	result = DirectFunctionCall1(float8in, CStringGetDatum(tmp));
-
-	pfree(tmp);
+	if (!DirectInputFunctionCallSafe(float8in, tmp,
+									 InvalidOid, -1,
+									 (Node *) fcinfo->context,
+									 &result))
+	{
+		pfree(tmp);
+		PG_RETURN_NULL();
+	}
 
 	PG_RETURN_DATUM(result);
 }
@@ -4840,7 +4876,14 @@ numeric_float4(PG_FUNCTION_ARGS)
 	tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 											  NumericGetDatum(num)));
 
-	result = DirectFunctionCall1(float4in, CStringGetDatum(tmp));
+	if (!DirectInputFunctionCallSafe(float4in, tmp,
+									 InvalidOid, -1,
+									 (Node *) fcinfo->context,
+									 &result))
+	{
+		pfree(tmp);
+		PG_RETURN_NULL();
+	}
 
 	pfree(tmp);
 
-- 
2.34.1

