Refactoring: Use soft error reporting for *_opt_error functions
Hi,
While reviewing version v6 of the CAST(... ON DEFAULT) patch [1], I
noticed that it attempts to change the type conversion function to use
soft error reporting. However, some of the underlying functions of the
type conversion, such as *_opt_error, still rely on a boolean argument
passed by the caller. This results in an error flag being set rather
than a proper error being thrown.
I believe we should update all *_opt_error functions to use the new
soft error reporting infrastructure instead of boolean flags -- did
the same in the attached patch. I am not sure if this patch should be
part of that thread[1]. It's a significant improvement in itself, as
it would make the code more compact and consistent.
1]/messages/by-id/CACJufxE053=bO3pDUpGba6Yz3VGpU_XCbg4HO6Rew5EJ7k7VnQ@mail.gmail.com
--
Regards,
Amul Sul
EDB: http://www.enterprisedb.com
Attachments:
0001-Change-_opt_error-to-soft-error-reporting.patchapplication/x-patch; name=0001-Change-_opt_error-to-soft-error-reporting.patchDownload
From 6f5b4217aa74f4d3a9e16e7d438dbd2fb557ef0a Mon Sep 17 00:00:00 2001
From: Amul Sul <sulamul@gmail.com>
Date: Mon, 1 Sep 2025 14:53:47 +0530
Subject: [PATCH] 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.
---
src/backend/utils/adt/formatting.c | 6 +-
src/backend/utils/adt/jsonpath_exec.c | 62 +++----
src/backend/utils/adt/numeric.c | 248 ++++++++------------------
src/backend/utils/adt/timestamp.c | 46 ++---
src/include/utils/numeric.h | 22 ++-
5 files changed, 146 insertions(+), 238 deletions(-)
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..fd246575788 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,12 +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,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
@@ -3212,12 +3190,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,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
@@ -3252,20 +3225,19 @@ 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 "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"));
/*
* 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);
@@ -3374,30 +3346,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 *econtext)
{
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,17 +3377,11 @@ 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,
+ ereturn(econtext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- }
/* Inf % any nonzero = NaN */
+
return make_result(&const_nan);
}
/* num2 must be [-]Inf; result is num1 regardless of sign of num2 */
@@ -3433,17 +3394,16 @@ 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 "econtext" 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 (econtext && (arg2.ndigits == 0 || arg2.digits[0] == 0))
+ ereturn(econtext, NULL,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
mod_var(&arg1, &arg2, &result);
- res = make_result_opt_error(&result, NULL);
+ res = make_result_safe(&result, NULL);
free_var(&result);
@@ -4404,52 +4364,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 +4401,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 +4434,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 +4471,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 +7507,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 +7522,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 +7594,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 +7606,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/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..f5172228dfd 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 declarations 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);
--
2.47.1
On Mon, 1 Sept 2025 at 10:36, Amul Sul <sulamul@gmail.com> wrote:
I believe we should update all *_opt_error functions to use the new
soft error reporting infrastructure instead of boolean flags -- did
the same in the attached patch. I am not sure if this patch should be
part of that thread[1]. It's a significant improvement in itself, as
it would make the code more compact and consistent.
Agreed. That does look neater.
Regards,
Dean
On Mon, Sep 01, 2025 at 12:21:18PM +0100, Dean Rasheed wrote:
On Mon, 1 Sept 2025 at 10:36, Amul Sul <sulamul@gmail.com> wrote:
I believe we should update all *_opt_error functions to use the new
soft error reporting infrastructure instead of boolean flags -- did
the same in the attached patch. I am not sure if this patch should be
part of that thread[1]. It's a significant improvement in itself, as
it would make the code more compact and consistent.
Handling that as a separate patch seems OK here. Thanks for caring.
Agreed. That does look neater.
Yep. More consistent.
+/* forward declarations to avoid node.h include */
+typedef struct Node Node;
s/declarations/declaration/. Singular required, only one declaration.
Looking at the surroundings, would it make sense to do the same for
pg_lsn_in_internal()? It requires a safe fallback when parsing the
LSN from the recovery_target_lsn GUC.
--
Michael
On Tue, Sep 2, 2025 at 9:46 AM Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Sep 01, 2025 at 12:21:18PM +0100, Dean Rasheed wrote:
On Mon, 1 Sept 2025 at 10:36, Amul Sul <sulamul@gmail.com> wrote:
I believe we should update all *_opt_error functions to use the new
soft error reporting infrastructure instead of boolean flags -- did
the same in the attached patch. I am not sure if this patch should be
part of that thread[1]. It's a significant improvement in itself, as
it would make the code more compact and consistent.Handling that as a separate patch seems OK here. Thanks for caring.
Agreed. That does look neater.
Yep. More consistent.
+/* forward declarations to avoid node.h include */ +typedef struct Node Node;s/declarations/declaration/. Singular required, only one declaration.
Ok, will fix that.
Looking at the surroundings, would it make sense to do the same for
pg_lsn_in_internal()? It requires a safe fallback when parsing the
LSN from the recovery_target_lsn GUC.
Yeah, it makes sense, will change in the next version.
Just a quick question regarding the naming conventions. It looks like
we have a choice between two options for consistency. Should we rename
the pg_lsn_in_internal function by replacing "_internal" with "_safe",
or should we rename all of the *_opt_error functions by replacing
"_opt_error" with "_internal"?
I would choose the latter option.
Regards,
Amul
On Tue, Sep 02, 2025 at 12:40:25PM +0530, Amul Sul wrote:
Just a quick question regarding the naming conventions. It looks like
we have a choice between two options for consistency. Should we rename
the pg_lsn_in_internal function by replacing "_internal" with "_safe",
or should we rename all of the *_opt_error functions by replacing
"_opt_error" with "_internal"?I would choose the latter option.
Applying "_safe" seems a bit more consistent to me, as per past
changes like ccff2d20ed96, also looking at the functions that are
given a ErrorSaveContext in input. I am ready to believe that there
are not a lot of callers of the existing _opt_error() routines listed
in numeric.h, so a renaming may be better to let existing callers know
about the change. These predate the introduction of the "_safe"
functions, introduced in 16d489b0fe05.
--
Michael
On Tue, Sep 2, 2025 at 12:59 PM Michael Paquier <michael@paquier.xyz> wrote:
On Tue, Sep 02, 2025 at 12:40:25PM +0530, Amul Sul wrote:
Just a quick question regarding the naming conventions. It looks like
we have a choice between two options for consistency. Should we rename
the pg_lsn_in_internal function by replacing "_internal" with "_safe",
or should we rename all of the *_opt_error functions by replacing
"_opt_error" with "_internal"?I would choose the latter option.
Applying "_safe" seems a bit more consistent to me, as per past
changes like ccff2d20ed96, also looking at the functions that are
given a ErrorSaveContext in input. I am ready to believe that there
are not a lot of callers of the existing _opt_error() routines listed
in numeric.h, so a renaming may be better to let existing callers know
about the change. These predate the introduction of the "_safe"
functions, introduced in 16d489b0fe05.
Understood, thanks.
The updated version is attached. In addition to the *_opt_error()
functions, it also renames pg_lsn_in_internal to pg_lsn_in_safe and
incorporates soft error handling.
Regards,
Amul
Attachments:
v2-0001-Change-_opt_error-to-soft-error-reporting.patchapplication/x-patch; name=v2-0001-Change-_opt_error-to-soft-error-reporting.patchDownload
From fbb92d00bcec35466d61501d0fa8e800deae82e7 Mon Sep 17 00:00:00 2001
From: Amul Sul <sulamul@gmail.com>
Date: Tue, 2 Sep 2025 14:31:37 +0530
Subject: [PATCH v2] 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 | 248 +++++++---------------
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, 167 insertions(+), 261 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..fd246575788 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,12 +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,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
@@ -3212,12 +3190,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,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
@@ -3252,20 +3225,19 @@ 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 "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"));
/*
* 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);
@@ -3374,30 +3346,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 *econtext)
{
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,17 +3377,11 @@ 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,
+ ereturn(econtext, NULL,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- }
/* Inf % any nonzero = NaN */
+
return make_result(&const_nan);
}
/* num2 must be [-]Inf; result is num1 regardless of sign of num2 */
@@ -3433,17 +3394,16 @@ 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 "econtext" 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 (econtext && (arg2.ndigits == 0 || arg2.digits[0] == 0))
+ ereturn(econtext, NULL,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
mod_var(&arg1, &arg2, &result);
- res = make_result_opt_error(&result, NULL);
+ res = make_result_safe(&result, NULL);
free_var(&result);
@@ -4404,52 +4364,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 +4401,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 +4434,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 +4471,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 +7507,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 +7522,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 +7594,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 +7606,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..16001ff3ace 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,20 @@ 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;
- }
+ ereturn(escontext, InvalidXLogRecPtr,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "pg_lsn", str)));
+
len2 = strspn(str + len1 + 1, "0123456789abcdefABCDEF");
if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || str[len1 + 1 + len2] != '\0')
- {
- *have_error = true;
- return InvalidXLogRecPtr;
- }
+ ereturn(escontext, InvalidXLogRecPtr,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "pg_lsn", str)));
/* Decode result. */
id = (uint32) strtoul(str, NULL, 16);
@@ -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
On Tue, Sep 02, 2025 at 02:41:23PM +0530, Amul Sul wrote:
The updated version is attached. In addition to the *_opt_error()
functions, it also renames pg_lsn_in_internal to pg_lsn_in_safe and
incorporates soft error handling.
Looks globally sensible to me. I was wondering for a bit if the JSON
path parts should be fed pieces of the soft errors that could be
retrieved when the numeric value parsing fails, but that does not seem
worth the extra information.
-pg_lsn_in_internal(const char *str, bool *have_error)
+pg_lsn_in_safe(const char *str, Node *escontext)
[...]
+ ereturn(escontext, InvalidXLogRecPtr,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "pg_lsn", str)));
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.
@@ -5629,11 +5629,11 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
[...]
+ PG_RETURN_NUMERIC(numeric_add_safe(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
This part with DTK_JULIAN is hard to parse. Not your responsibility
here as you just replace a function name, just a remark in passing.
--
Michael
On Wed, 3 Sept 2025 at 07:47, Michael Paquier <michael@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.
Regards,
Dean
On Wed, Sep 3, 2025 at 1:04 PM Dean Rasheed <dean.a.rasheed@gmail.com> wrote:
On Wed, 3 Sept 2025 at 07:47, Michael Paquier <michael@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
Attachments:
v3-0001-Change-_opt_error-to-soft-error-reporting.patchapplication/x-patch; name=v3-0001-Change-_opt_error-to-soft-error-reporting.patchDownload
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
On Wed, Sep 3, 2025 at 7:52 PM Amul Sul <sulamul@gmail.com> wrote:
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
-extern int32 numeric_int4_opt_error(Numeric num, bool *have_error);
.....
+extern int32 numeric_int4_safe(Numeric num, Node *escontext);
would any extensions using these functions (such as
numeric_int4_opt_error) may encounter upgrade compatibility issues in
the future?
On Wed, Sep 03, 2025 at 10:41:14PM +0800, jian he wrote:
would any extensions using these functions (such as
numeric_int4_opt_error) may encounter upgrade compatibility issues in
the future?
That would be also the point, so as callers are made aware of these
"upgraded" versions.
I have found one call outside of core here, so the practive is rare,
so doing the switch does not worry me much at the end:
https://github.com/siilike/postgresql-vpack
pg_lsn_in_internal() was worrying me a little bit more, because it
could impact backend-side code that wants to parse LSN strings with
the extra error handling, like potentially some HA tools, but looking
around I am just seeing references based on forked code of the core
repository. There's no way to know about closed source code, of
course, but I'm not going to qualify that as something that's worth
the cost of a compatibility layer with equivalent macros.
+ * Internal version of numeric_add() supports "soft" error reporting.
These comments still read incorrect to me (no point in quoting the
soft part, incorrect grammar). I'd suggest the following wording,
applied to all the new functions:
"Internal version of xxx_safe with support for soft error reporting."
I'd be pretty much OK with this version of the patch, plus a few
tweaks.
Dean, you have commented on this patch before me and the numeric code
is something you have touched more than me lately (the LSN part is
tainted with my fingerprints, but it's minimal here). Would you
prefer handle this patch yourself? I'm OK to perform a final lookup
of it for commit, just won't get in your way if you have been looking
at it seriously.
--
Michael
On Thu, 4 Sept 2025 at 01:18, Michael Paquier <michael@paquier.xyz> wrote:
I'd be pretty much OK with this version of the patch, plus a few
tweaks.
Agreed.
In numeric_div_safe():
+div_error:
+ ereturn(escontext, NULL,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
I would make that label something more specific like "division_by_zero".
Dean, you have commented on this patch before me and the numeric code
is something you have touched more than me lately (the LSN part is
tainted with my fingerprints, but it's minimal here). Would you
prefer handle this patch yourself? I'm OK to perform a final lookup
of it for commit, just won't get in your way if you have been looking
at it seriously.
I don't mind. I haven't looked at it too closely, but I'm broadly
happy with it. I think any issues are probably now minor cosmetic
things, so if you've been looking in more detail and are happy to
commit it, then go ahead. Otherwise, I can take it if you prefer.
Regards,
Dean
On Thu, Sep 04, 2025 at 10:50:38AM +0100, Dean Rasheed wrote:
I don't mind. I haven't looked at it too closely, but I'm broadly
happy with it. I think any issues are probably now minor cosmetic
things, so if you've been looking in more detail and are happy to
commit it, then go ahead. Otherwise, I can take it if you prefer.
Sure. I'll handle it. Thanks.
--
Michael
On Fri, Sep 05, 2025 at 08:10:00AM +0900, Michael Paquier wrote:
Sure. I'll handle it. Thanks.
Applied after a few tweaks, including changes to the comments, the
suggestion of "division_by_zero" for the goto labels, and splitting
the patch into two parts for pg_lsn and numeric.
--
Michael
On Fri, Sep 5, 2025 at 10:28 AM Michael Paquier <michael@paquier.xyz> wrote:
On Fri, Sep 05, 2025 at 08:10:00AM +0900, Michael Paquier wrote:
Sure. I'll handle it. Thanks.
Applied after a few tweaks, including changes to the comments, the
suggestion of "division_by_zero" for the goto labels, and splitting
the patch into two parts for pg_lsn and numeric.
Thanks for improving and committing the patch. I see now that two
separate commits were definitely needed, and I should have submitted
them that way. Thank you again for splitting it into two parts.
Regards,
Amul
On Fri, Sep 5, 2025 at 12:58 PM Michael Paquier <michael@paquier.xyz> wrote:
Applied after a few tweaks, including changes to the comments, the
suggestion of "division_by_zero" for the goto labels, and splitting
the patch into two parts for pg_lsn and numeric.
/*
* Internal version of int8_numeric() with support for soft error reporting.
*/
int64
numeric_int8_safe(Numeric num, Node *escontext)
the above comment "int8_numeric" should be "numeric_int8"?
On Mon, Oct 06, 2025 at 06:19:49AM +0800, jian he wrote:
/*
* Internal version of int8_numeric() with support for soft error reporting.
*/
int64
numeric_int8_safe(Numeric num, Node *escontext)the above comment "int8_numeric" should be "numeric_int8"?
Yep. The same is true about numeric_int4_safe() where int4_numeric()
is referenced. Will fix, thanks.
--
Michael