On Wed, Sep 3, 2025 at 1:04 PM Dean Rasheed <dean.a.rash...@gmail.com> wrote:
>
> On Wed, 3 Sept 2025 at 07:47, Michael Paquier <mich...@paquier.xyz> wrote:
> >
> > The same error message is repeated twice.  How about using some gotos
> > and one single ereport instead of two?  The same can be said for
> > numeric_div_safe() and numeric_mod_safe(), for the division-by-0
> > messages.
>
> In numeric_div_safe() and numeric_mod_safe():
>
> -     * If "have_error" is provided, check for division by zero here
> +     * If "escontext" is provided, raise division by zero soft error here
>       */
> -    if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
> -    {
> -        *have_error = true;
> -        return NULL;
> -    }
> +    if (escontext && (arg2.ndigits == 0 || arg2.digits[0] == 0))
> +        ereturn(escontext, NULL,
> +                errcode(ERRCODE_DIVISION_BY_ZERO),
> +                errmsg("division by zero"));
>
> This might as well now be made to check for division-by-zero even if
> escontext is NULL.

Agreed -- did the same in the attached version including the error
deduplication that Michael suggested.

Regards,
Amul
From d42234494a32becf41438548b3d6ba462ad2cd58 Mon Sep 17 00:00:00 2001
From: Amul Sul <sulamul@gmail.com>
Date: Wed, 3 Sep 2025 17:02:23 +0530
Subject: [PATCH v3] Change *_opt_error to soft error reporting.

The existing *_opt_error functions take a boolean argument to set an
error flag instead of throwing an error if that passed, which allows
the caller to handle it. However, the commit d9f7f5d32f20 added "soft"
error-reporting infrastructure that can be used here instead.

Similarly, change the pg_lsn_in_internal function to use soft error
reporting and rename it to pg_lsn_in_safe.
---
 src/backend/access/transam/xlogrecovery.c |   6 +-
 src/backend/utils/adt/formatting.c        |   6 +-
 src/backend/utils/adt/jsonpath_exec.c     |  62 ++---
 src/backend/utils/adt/numeric.c           | 266 +++++++---------------
 src/backend/utils/adt/pg_lsn.c            |  33 ++-
 src/backend/utils/adt/timestamp.c         |  46 ++--
 src/include/utils/numeric.h               |  22 +-
 src/include/utils/pg_lsn.h                |   5 +-
 8 files changed, 173 insertions(+), 273 deletions(-)

diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index f23ec8969c2..346319338a0 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -4834,10 +4834,10 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
 	{
 		XLogRecPtr	lsn;
 		XLogRecPtr *myextra;
-		bool		have_error = false;
+		ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-		lsn = pg_lsn_in_internal(*newval, &have_error);
-		if (have_error)
+		lsn = pg_lsn_in_safe(*newval, (Node *) &escontext);
+		if (escontext.error_occurred)
 			return false;
 
 		myextra = (XLogRecPtr *) guc_malloc(LOG, sizeof(XLogRecPtr));
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 7ad453314c3..78e19ac39ac 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -6389,12 +6389,12 @@ numeric_to_char(PG_FUNCTION_ARGS)
 	if (IS_ROMAN(&Num))
 	{
 		int32		intvalue;
-		bool		err;
+		ErrorSaveContext escontext = {T_ErrorSaveContext};
 
 		/* Round and convert to int */
-		intvalue = numeric_int4_opt_error(value, &err);
+		intvalue = numeric_int4_safe(value, (Node *) &escontext);
 		/* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
-		if (err)
+		if (escontext.error_occurred)
 			intvalue = PG_INT32_MAX;
 		numstr = int_to_roman(intvalue);
 	}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 5a562535223..8156695e97e 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -252,7 +252,8 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
 												   JsonbValue *larg,
 												   JsonbValue *rarg,
 												   void *param);
-typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
+typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2,
+									 Node *escontext);
 
 static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
 										  JsonPathGetVarCallback getVar,
@@ -808,23 +809,23 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
 		case jpiAdd:
 			return executeBinaryArithmExpr(cxt, jsp, jb,
-										   numeric_add_opt_error, found);
+										   numeric_add_safe, found);
 
 		case jpiSub:
 			return executeBinaryArithmExpr(cxt, jsp, jb,
-										   numeric_sub_opt_error, found);
+										   numeric_sub_safe, found);
 
 		case jpiMul:
 			return executeBinaryArithmExpr(cxt, jsp, jb,
-										   numeric_mul_opt_error, found);
+										   numeric_mul_safe, found);
 
 		case jpiDiv:
 			return executeBinaryArithmExpr(cxt, jsp, jb,
-										   numeric_div_opt_error, found);
+										   numeric_div_safe, found);
 
 		case jpiMod:
 			return executeBinaryArithmExpr(cxt, jsp, jb,
-										   numeric_mod_opt_error, found);
+										   numeric_mod_safe, found);
 
 		case jpiPlus:
 			return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
@@ -1269,11 +1270,12 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
 				if (jb->type == jbvNumeric)
 				{
-					bool		have_error;
+					ErrorSaveContext escontext = {T_ErrorSaveContext};
 					int64		val;
 
-					val = numeric_int8_opt_error(jb->val.numeric, &have_error);
-					if (have_error)
+					val = numeric_int8_safe(jb->val.numeric,
+											(Node *) &escontext);
+					if (escontext.error_occurred)
 						RETURN_ERROR(ereport(ERROR,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
@@ -1466,7 +1468,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 					Datum		dtypmod;
 					int32		precision;
 					int32		scale = 0;
-					bool		have_error;
 					bool		noerr;
 					ArrayType  *arrtypmod;
 					Datum		datums[2];
@@ -1478,9 +1479,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 					if (elem.type != jpiNumeric)
 						elog(ERROR, "invalid jsonpath item type for .decimal() precision");
 
-					precision = numeric_int4_opt_error(jspGetNumeric(&elem),
-													   &have_error);
-					if (have_error)
+					precision = numeric_int4_safe(jspGetNumeric(&elem),
+												  (Node *) &escontext);
+					if (escontext.error_occurred)
 						RETURN_ERROR(ereport(ERROR,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("precision of jsonpath item method .%s() is out of range for type integer",
@@ -1492,9 +1493,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 						if (elem.type != jpiNumeric)
 							elog(ERROR, "invalid jsonpath item type for .decimal() scale");
 
-						scale = numeric_int4_opt_error(jspGetNumeric(&elem),
-													   &have_error);
-						if (have_error)
+						scale = numeric_int4_safe(jspGetNumeric(&elem),
+												  (Node *) &escontext);
+						if (escontext.error_occurred)
 							RETURN_ERROR(ereport(ERROR,
 												 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 												  errmsg("scale of jsonpath item method .%s() is out of range for type integer",
@@ -1550,11 +1551,12 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
 				if (jb->type == jbvNumeric)
 				{
-					bool		have_error;
 					int32		val;
+					ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-					val = numeric_int4_opt_error(jb->val.numeric, &have_error);
-					if (have_error)
+					val = numeric_int4_safe(jb->val.numeric,
+											(Node *) &escontext);
+					if (escontext.error_occurred)
 						RETURN_ERROR(ereport(ERROR,
 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
 											  errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
@@ -2149,11 +2151,11 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 	}
 	else
 	{
-		bool		error = false;
+		ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-		res = func(lval->val.numeric, rval->val.numeric, &error);
+		res = func(lval->val.numeric, rval->val.numeric, (Node *) &escontext);
 
-		if (error)
+		if (escontext.error_occurred)
 			return jperError;
 	}
 
@@ -2433,7 +2435,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
 		if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
 			jsp->content.arg)
 		{
-			bool		have_error;
+			ErrorSaveContext escontext = {T_ErrorSaveContext};
 
 			jspGetArg(jsp, &elem);
 
@@ -2441,9 +2443,9 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
 				elog(ERROR, "invalid jsonpath item type for %s argument",
 					 jspOperationName(jsp->type));
 
-			time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
-													&have_error);
-			if (have_error)
+			time_precision = numeric_int4_safe(jspGetNumeric(&elem),
+											   (Node *) &escontext);
+			if (escontext.error_occurred)
 				RETURN_ERROR(ereport(ERROR,
 									 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
 									  errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
@@ -3462,7 +3464,7 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
 	JsonValueList found = {0};
 	JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
 	Datum		numeric_index;
-	bool		have_error = false;
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
 
 	if (jperIsError(res))
 		return res;
@@ -3477,10 +3479,10 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
 										NumericGetDatum(jbv->val.numeric),
 										Int32GetDatum(0));
 
-	*index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
-									&have_error);
+	*index = numeric_int4_safe(DatumGetNumeric(numeric_index),
+							   (Node *) &escontext);
 
-	if (have_error)
+	if (escontext.error_occurred)
 		RETURN_ERROR(ereport(ERROR,
 							 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
 							  errmsg("jsonpath array subscript is out of integer range"))));
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index b6287f5d973..9157cedd352 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -517,7 +517,7 @@ static void numericvar_deserialize(StringInfo buf, NumericVar *var);
 
 static Numeric duplicate_numeric(Numeric num);
 static Numeric make_result(const NumericVar *var);
-static Numeric make_result_opt_error(const NumericVar *var, bool *have_error);
+static Numeric make_result_safe(const NumericVar *var, Node *escontext);
 
 static bool apply_typmod(NumericVar *var, int32 typmod, Node *escontext);
 static bool apply_typmod_special(Numeric num, int32 typmod, Node *escontext);
@@ -717,7 +717,6 @@ numeric_in(PG_FUNCTION_ARGS)
 		 */
 		NumericVar	value;
 		int			base;
-		bool		have_error;
 
 		init_var(&value);
 
@@ -776,12 +775,7 @@ numeric_in(PG_FUNCTION_ARGS)
 		if (!apply_typmod(&value, typmod, escontext))
 			PG_RETURN_NULL();
 
-		res = make_result_opt_error(&value, &have_error);
-
-		if (have_error)
-			ereturn(escontext, (Datum) 0,
-					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-					 errmsg("value overflows numeric format")));
+		res = make_result_safe(&value, escontext);
 
 		free_var(&value);
 	}
@@ -2874,20 +2868,18 @@ numeric_add(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 
-	res = numeric_add_opt_error(num1, num2, NULL);
+	res = numeric_add_safe(num1, num2, NULL);
 
 	PG_RETURN_NUMERIC(res);
 }
 
 /*
- * numeric_add_opt_error() -
+ * numeric_add_safe() -
  *
- *	Internal version of numeric_add().  If "*have_error" flag is provided,
- *	on error it's set to true, NULL returned.  This is helpful when caller
- *	need to handle errors by itself.
+ *	Internal version of numeric_add() supports "soft" error reporting.
  */
 Numeric
-numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
+numeric_add_safe(Numeric num1, Numeric num2, Node *escontext)
 {
 	NumericVar	arg1;
 	NumericVar	arg2;
@@ -2931,7 +2923,7 @@ numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
 	init_var(&result);
 	add_var(&arg1, &arg2, &result);
 
-	res = make_result_opt_error(&result, have_error);
+	res = make_result_safe(&result, escontext);
 
 	free_var(&result);
 
@@ -2951,21 +2943,19 @@ numeric_sub(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 
-	res = numeric_sub_opt_error(num1, num2, NULL);
+	res = numeric_sub_safe(num1, num2, NULL);
 
 	PG_RETURN_NUMERIC(res);
 }
 
 
 /*
- * numeric_sub_opt_error() -
+ * numeric_sub_safe() -
  *
- *	Internal version of numeric_sub().  If "*have_error" flag is provided,
- *	on error it's set to true, NULL returned.  This is helpful when caller
- *	need to handle errors by itself.
+ *	Internal version of numeric_sub() supports "soft" error reporting.
  */
 Numeric
-numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
+numeric_sub_safe(Numeric num1, Numeric num2, Node *escontext)
 {
 	NumericVar	arg1;
 	NumericVar	arg2;
@@ -3009,7 +2999,7 @@ numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
 	init_var(&result);
 	sub_var(&arg1, &arg2, &result);
 
-	res = make_result_opt_error(&result, have_error);
+	res = make_result_safe(&result, escontext);
 
 	free_var(&result);
 
@@ -3029,21 +3019,19 @@ numeric_mul(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 
-	res = numeric_mul_opt_error(num1, num2, NULL);
+	res = numeric_mul_safe(num1, num2, NULL);
 
 	PG_RETURN_NUMERIC(res);
 }
 
 
 /*
- * numeric_mul_opt_error() -
+ * numeric_mul_safe() -
  *
- *	Internal version of numeric_mul().  If "*have_error" flag is provided,
- *	on error it's set to true, NULL returned.  This is helpful when caller
- *	need to handle errors by itself.
+ *	Internal version of numeric_mul() supports "soft" error reporting.
  */
 Numeric
-numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
+numeric_mul_safe(Numeric num1, Numeric num2, Node *escontext)
 {
 	NumericVar	arg1;
 	NumericVar	arg2;
@@ -3130,7 +3118,7 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
 	if (result.dscale > NUMERIC_DSCALE_MAX)
 		round_var(&result, NUMERIC_DSCALE_MAX);
 
-	res = make_result_opt_error(&result, have_error);
+	res = make_result_safe(&result, escontext);
 
 	free_var(&result);
 
@@ -3150,21 +3138,19 @@ numeric_div(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 
-	res = numeric_div_opt_error(num1, num2, NULL);
+	res = numeric_div_safe(num1, num2, NULL);
 
 	PG_RETURN_NUMERIC(res);
 }
 
 
 /*
- * numeric_div_opt_error() -
+ * numeric_div_safe() -
  *
- *	Internal version of numeric_div().  If "*have_error" flag is provided,
- *	on error it's set to true, NULL returned.  This is helpful when caller
- *	need to handle errors by itself.
+ *	Internal version of numeric_div() supports "soft" error reporting.
  */
 Numeric
-numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
+numeric_div_safe(Numeric num1, Numeric num2, Node *escontext)
 {
 	NumericVar	arg1;
 	NumericVar	arg2;
@@ -3172,9 +3158,6 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
 	Numeric		res;
 	int			rscale;
 
-	if (have_error)
-		*have_error = false;
-
 	/*
 	 * Handle NaN and infinities
 	 */
@@ -3189,15 +3172,7 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
 			switch (numeric_sign_internal(num2))
 			{
 				case 0:
-					if (have_error)
-					{
-						*have_error = true;
-						return NULL;
-					}
-					ereport(ERROR,
-							(errcode(ERRCODE_DIVISION_BY_ZERO),
-							 errmsg("division by zero")));
-					break;
+					goto div_error;
 				case 1:
 					return make_result(&const_pinf);
 				case -1:
@@ -3212,15 +3187,7 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
 			switch (numeric_sign_internal(num2))
 			{
 				case 0:
-					if (have_error)
-					{
-						*have_error = true;
-						return NULL;
-					}
-					ereport(ERROR,
-							(errcode(ERRCODE_DIVISION_BY_ZERO),
-							 errmsg("division by zero")));
-					break;
+					goto div_error;
 				case 1:
 					return make_result(&const_ninf);
 				case -1:
@@ -3251,25 +3218,25 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
 	 */
 	rscale = select_div_scale(&arg1, &arg2);
 
-	/*
-	 * If "have_error" is provided, check for division by zero here
-	 */
-	if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
-	{
-		*have_error = true;
-		return NULL;
-	}
+	/* Check for division by zero */
+	if (arg2.ndigits == 0 || arg2.digits[0] == 0)
+		goto div_error;
 
 	/*
 	 * Do the divide and return the result
 	 */
 	div_var(&arg1, &arg2, &result, rscale, true, true);
 
-	res = make_result_opt_error(&result, have_error);
+	res = make_result_safe(&result, escontext);
 
 	free_var(&result);
 
 	return res;
+
+div_error:
+	ereturn(escontext, NULL,
+			errcode(ERRCODE_DIVISION_BY_ZERO),
+			errmsg("division by zero"));
 }
 
 
@@ -3374,30 +3341,25 @@ numeric_mod(PG_FUNCTION_ARGS)
 	Numeric		num2 = PG_GETARG_NUMERIC(1);
 	Numeric		res;
 
-	res = numeric_mod_opt_error(num1, num2, NULL);
+	res = numeric_mod_safe(num1, num2, NULL);
 
 	PG_RETURN_NUMERIC(res);
 }
 
 
 /*
- * numeric_mod_opt_error() -
+ * numeric_mod_safe() -
  *
- *	Internal version of numeric_mod().  If "*have_error" flag is provided,
- *	on error it's set to true, NULL returned.  This is helpful when caller
- *	need to handle errors by itself.
+ *	Internal version of numeric_mod() supports "soft" error reporting.
  */
 Numeric
-numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
+numeric_mod_safe(Numeric num1, Numeric num2, Node *escontext)
 {
 	Numeric		res;
 	NumericVar	arg1;
 	NumericVar	arg2;
 	NumericVar	result;
 
-	if (have_error)
-		*have_error = false;
-
 	/*
 	 * Handle NaN and infinities.  We follow POSIX fmod() on this, except that
 	 * POSIX treats x-is-infinite and y-is-zero identically, raising EDOM and
@@ -3410,16 +3372,8 @@ numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
 		if (NUMERIC_IS_INF(num1))
 		{
 			if (numeric_sign_internal(num2) == 0)
-			{
-				if (have_error)
-				{
-					*have_error = true;
-					return NULL;
-				}
-				ereport(ERROR,
-						(errcode(ERRCODE_DIVISION_BY_ZERO),
-						 errmsg("division by zero")));
-			}
+				goto div_error;
+
 			/* Inf % any nonzero = NaN */
 			return make_result(&const_nan);
 		}
@@ -3432,22 +3386,22 @@ numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
 
 	init_var(&result);
 
-	/*
-	 * If "have_error" is provided, check for division by zero here
-	 */
-	if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
-	{
-		*have_error = true;
-		return NULL;
-	}
+	/* Check for division by zero */
+	if (arg2.ndigits == 0 || arg2.digits[0] == 0)
+		goto div_error;
 
 	mod_var(&arg1, &arg2, &result);
 
-	res = make_result_opt_error(&result, NULL);
+	res = make_result_safe(&result, escontext);
 
 	free_var(&result);
 
 	return res;
+
+div_error:
+	ereturn(escontext, NULL,
+			errcode(ERRCODE_DIVISION_BY_ZERO),
+			errmsg("division by zero"));
 }
 
 
@@ -4404,52 +4358,34 @@ int4_numeric(PG_FUNCTION_ARGS)
 	PG_RETURN_NUMERIC(int64_to_numeric(val));
 }
 
+/*
+ * Internal version of int4_numeric() supports "soft" error reporting.
+ */
 int32
-numeric_int4_opt_error(Numeric num, bool *have_error)
+numeric_int4_safe(Numeric num, Node *escontext)
 {
 	NumericVar	x;
 	int32		result;
 
-	if (have_error)
-		*have_error = false;
-
 	if (NUMERIC_IS_SPECIAL(num))
 	{
-		if (have_error)
-		{
-			*have_error = true;
-			return 0;
-		}
+		if (NUMERIC_IS_NAN(num))
+			ereturn(escontext, 0,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert NaN to %s", "integer")));
 		else
-		{
-			if (NUMERIC_IS_NAN(num))
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("cannot convert NaN to %s", "integer")));
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("cannot convert infinity to %s", "integer")));
-		}
+			ereturn(escontext, 0,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert infinity to %s", "integer")));
 	}
 
 	/* Convert to variable format, then convert to int4 */
 	init_var_from_num(num, &x);
 
 	if (!numericvar_to_int32(&x, &result))
-	{
-		if (have_error)
-		{
-			*have_error = true;
-			return 0;
-		}
-		else
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-					 errmsg("integer out of range")));
-		}
-	}
+		ereturn(escontext, 0,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
 
 	return result;
 }
@@ -4459,7 +4395,7 @@ numeric_int4(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
+	PG_RETURN_INT32(numeric_int4_safe(num, NULL));
 }
 
 /*
@@ -4492,52 +4428,34 @@ int8_numeric(PG_FUNCTION_ARGS)
 	PG_RETURN_NUMERIC(int64_to_numeric(val));
 }
 
+/*
+ * Internal version of int8_numeric() supports "soft" error reporting.
+ */
 int64
-numeric_int8_opt_error(Numeric num, bool *have_error)
+numeric_int8_safe(Numeric num, Node *escontext)
 {
 	NumericVar	x;
 	int64		result;
 
-	if (have_error)
-		*have_error = false;
-
 	if (NUMERIC_IS_SPECIAL(num))
 	{
-		if (have_error)
-		{
-			*have_error = true;
-			return 0;
-		}
+		if (NUMERIC_IS_NAN(num))
+			ereturn(escontext, 0,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert NaN to %s", "bigint")));
 		else
-		{
-			if (NUMERIC_IS_NAN(num))
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("cannot convert NaN to %s", "bigint")));
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("cannot convert infinity to %s", "bigint")));
-		}
+			ereturn(escontext, 0,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot convert infinity to %s", "bigint")));
 	}
 
 	/* Convert to variable format, then convert to int8 */
 	init_var_from_num(num, &x);
 
 	if (!numericvar_to_int64(&x, &result))
-	{
-		if (have_error)
-		{
-			*have_error = true;
-			return 0;
-		}
-		else
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-					 errmsg("bigint out of range")));
-		}
-	}
+		ereturn(escontext, 0,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("bigint out of range")));
 
 	return result;
 }
@@ -4547,7 +4465,7 @@ numeric_int8(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
+	PG_RETURN_INT64(numeric_int8_safe(num, NULL));
 }
 
 
@@ -7583,16 +7501,13 @@ duplicate_numeric(Numeric num)
 }
 
 /*
- * make_result_opt_error() -
+ * make_result_safe() -
  *
  *	Create the packed db numeric format in palloc()'d memory from
  *	a variable.  This will handle NaN and Infinity cases.
- *
- *	If "have_error" isn't NULL, on overflow *have_error is set to true and
- *	NULL is returned.  This is helpful when caller needs to handle errors.
  */
 static Numeric
-make_result_opt_error(const NumericVar *var, bool *have_error)
+make_result_safe(const NumericVar *var, Node *escontext)
 {
 	Numeric		result;
 	NumericDigit *digits = var->digits;
@@ -7601,9 +7516,6 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
 	int			n;
 	Size		len;
 
-	if (have_error)
-		*have_error = false;
-
 	if ((sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL)
 	{
 		/*
@@ -7676,19 +7588,9 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
 	/* Check for overflow of int16 fields */
 	if (NUMERIC_WEIGHT(result) != weight ||
 		NUMERIC_DSCALE(result) != var->dscale)
-	{
-		if (have_error)
-		{
-			*have_error = true;
-			return NULL;
-		}
-		else
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-					 errmsg("value overflows numeric format")));
-		}
-	}
+		ereturn(escontext, NULL,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value overflows numeric format")));
 
 	dump_numeric("make_result()", result);
 	return result;
@@ -7698,12 +7600,12 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
 /*
  * make_result() -
  *
- *	An interface to make_result_opt_error() without "have_error" argument.
+ *	An interface to make_result_safe() without "escontext" argument.
  */
 static Numeric
 make_result(const NumericVar *var)
 {
-	return make_result_opt_error(var, NULL);
+	return make_result_safe(var, NULL);
 }
 
 
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 12de2446f5b..4a465b9c847 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -25,8 +25,11 @@
  * Formatting and conversion routines.
  *---------------------------------------------------------*/
 
+/*
+ * Internal version of pg_lsn_in() supports "soft" error reporting.
+ */
 XLogRecPtr
-pg_lsn_in_internal(const char *str, bool *have_error)
+pg_lsn_in_safe(const char *str, Node *escontext)
 {
 	int			len1,
 				len2;
@@ -34,22 +37,14 @@ pg_lsn_in_internal(const char *str, bool *have_error)
 				off;
 	XLogRecPtr	result;
 
-	Assert(have_error != NULL);
-	*have_error = false;
-
 	/* Sanity check input format. */
 	len1 = strspn(str, "0123456789abcdefABCDEF");
 	if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || str[len1] != '/')
-	{
-		*have_error = true;
-		return InvalidXLogRecPtr;
-	}
+		goto syntax_error;
+
 	len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
 	if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
-	{
-		*have_error = true;
-		return InvalidXLogRecPtr;
-	}
+		goto syntax_error;
 
 	/* Decode result. */
 	id = (uint32) strtoul(str, NULL, 16);
@@ -57,6 +52,12 @@ pg_lsn_in_internal(const char *str, bool *have_error)
 	result = ((uint64) id << 32) | off;
 
 	return result;
+
+syntax_error:
+	ereturn(escontext, InvalidXLogRecPtr,
+			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			 errmsg("invalid input syntax for type %s: \"%s\"",
+					"pg_lsn", str)));
 }
 
 Datum
@@ -64,14 +65,8 @@ pg_lsn_in(PG_FUNCTION_ARGS)
 {
 	char	   *str = PG_GETARG_CSTRING(0);
 	XLogRecPtr	result;
-	bool		have_error = false;
 
-	result = pg_lsn_in_internal(str, &have_error);
-	if (have_error)
-		ereturn(fcinfo->context, (Datum) 0,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("invalid input syntax for type %s: \"%s\"",
-						"pg_lsn", str)));
+	result = pg_lsn_in_safe(str, fcinfo->context);
 
 	PG_RETURN_LSN(result);
 }
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 3e5f9dc1458..156a4830ffd 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -5629,11 +5629,11 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
 
 			case DTK_JULIAN:
 				if (retnumeric)
-					PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
-															numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
-																				  int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
-																				  NULL),
-															NULL));
+					PG_RETURN_NUMERIC(numeric_add_safe(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
+													   numeric_div_safe(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
+																		int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
+																		NULL),
+													   NULL));
 				else
 					PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) +
 									 ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
@@ -5685,11 +5685,11 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
 						result = int64_div_fast_to_numeric(timestamp - epoch, 6);
 					else
 					{
-						result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp),
-																			 int64_to_numeric(epoch),
-																			 NULL),
-													   int64_to_numeric(1000000),
-													   NULL);
+						result = numeric_div_safe(numeric_sub_safe(int64_to_numeric(timestamp),
+																   int64_to_numeric(epoch),
+																   NULL),
+												  int64_to_numeric(1000000),
+												  NULL);
 						result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
 																	 NumericGetDatum(result),
 																	 Int32GetDatum(6)));
@@ -5903,11 +5903,11 @@ timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
 
 			case DTK_JULIAN:
 				if (retnumeric)
-					PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
-															numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
-																				  int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
-																				  NULL),
-															NULL));
+					PG_RETURN_NUMERIC(numeric_add_safe(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
+													   numeric_div_safe(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
+																		int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
+																		NULL),
+													   NULL));
 				else
 					PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) +
 									 ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
@@ -5956,11 +5956,11 @@ timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
 						result = int64_div_fast_to_numeric(timestamp - epoch, 6);
 					else
 					{
-						result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp),
-																			 int64_to_numeric(epoch),
-																			 NULL),
-													   int64_to_numeric(1000000),
-													   NULL);
+						result = numeric_div_safe(numeric_sub_safe(int64_to_numeric(timestamp),
+																   int64_to_numeric(epoch),
+																   NULL),
+												  int64_to_numeric(1000000),
+												  NULL);
 						result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
 																	 NumericGetDatum(result),
 																	 Int32GetDatum(6)));
@@ -6247,9 +6247,9 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
 				result = int64_div_fast_to_numeric(val, 6);
 			else
 				result =
-					numeric_add_opt_error(int64_div_fast_to_numeric(interval->time, 6),
-										  int64_to_numeric(secs_from_day_month),
-										  NULL);
+					numeric_add_safe(int64_div_fast_to_numeric(interval->time, 6),
+									 int64_to_numeric(secs_from_day_month),
+									 NULL);
 
 			PG_RETURN_NUMERIC(result);
 		}
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 9e79fc376cb..215f1ea4f53 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -17,6 +17,9 @@
 #include "common/pg_prng.h"
 #include "fmgr.h"
 
+/* forward declaration to avoid node.h include */
+typedef struct Node Node;
+
 /*
  * Limits on the precision and scale specifiable in a NUMERIC typmod.  The
  * precision is strictly positive, but the scale may be positive or negative.
@@ -91,18 +94,13 @@ extern char *numeric_normalize(Numeric num);
 extern Numeric int64_to_numeric(int64 val);
 extern Numeric int64_div_fast_to_numeric(int64 val1, int log10val2);
 
-extern Numeric numeric_add_opt_error(Numeric num1, Numeric num2,
-									 bool *have_error);
-extern Numeric numeric_sub_opt_error(Numeric num1, Numeric num2,
-									 bool *have_error);
-extern Numeric numeric_mul_opt_error(Numeric num1, Numeric num2,
-									 bool *have_error);
-extern Numeric numeric_div_opt_error(Numeric num1, Numeric num2,
-									 bool *have_error);
-extern Numeric numeric_mod_opt_error(Numeric num1, Numeric num2,
-									 bool *have_error);
-extern int32 numeric_int4_opt_error(Numeric num, bool *have_error);
-extern int64 numeric_int8_opt_error(Numeric num, bool *have_error);
+extern Numeric numeric_add_safe(Numeric num1, Numeric num2, Node *escontext);
+extern Numeric numeric_sub_safe(Numeric num1, Numeric num2, Node *escontext);
+extern Numeric numeric_mul_safe(Numeric num1, Numeric num2, Node *escontext);
+extern Numeric numeric_div_safe(Numeric num1, Numeric num2, Node *escontext);
+extern Numeric numeric_mod_safe(Numeric num1, Numeric num2, Node *escontext);
+extern int32 numeric_int4_safe(Numeric num, Node *escontext);
+extern int64 numeric_int8_safe(Numeric num, Node *escontext);
 
 extern Numeric random_numeric(pg_prng_state *state,
 							  Numeric rmin, Numeric rmax);
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index ae198af7450..461a4fdcba9 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,6 +18,9 @@
 #include "access/xlogdefs.h"
 #include "fmgr.h"
 
+/* forward declaration to avoid node.h include */
+typedef struct Node Node;
+
 static inline XLogRecPtr
 DatumGetLSN(Datum X)
 {
@@ -33,6 +36,6 @@ LSNGetDatum(XLogRecPtr X)
 #define PG_GETARG_LSN(n)	 DatumGetLSN(PG_GETARG_DATUM(n))
 #define PG_RETURN_LSN(x)	 return LSNGetDatum(x)
 
-extern XLogRecPtr pg_lsn_in_internal(const char *str, bool *have_error);
+extern XLogRecPtr pg_lsn_in_safe(const char *str, Node *escontext);
 
 #endif							/* PG_LSN_H */
-- 
2.47.1

Reply via email to