Switch some numeric-related functions to use soft error reporting

This commit changes some functions related to the data type numeric to
use the soft error reporting rather than a custom boolean flag (called
"have_error") that callers of these functions could rely on to bypass
the generation of ERROR reports, letting the callers do their own error
handling (timestamp, jsonpath and numeric_to_char() require them).

This results in the removal of some boilerplate code that was required
to handle both the ereport() and the "have_error" code paths bypassing
ereport(), unifying everything under the soft error reporting facility.

While on it, some duplicated error messages are removed.  The function
upgraded in this commit were suffixed with "_opt_error" in their names.
They are renamed to "_safe" instead.

This change relies on d9f7f5d32f, that has introduced the soft error
reporting infrastructure.

Author: Amul Sul <sulamul@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/CAAJ_b96No5h5tRuR+KhcC44YcYUCw8WAHuLoqqyyop8_k3+JDQ@mail.gmail.com
master
Michael Paquier 1 week ago
parent ae45312008
commit 4246a977ba
  1. 6
      src/backend/utils/adt/formatting.c
  2. 62
      src/backend/utils/adt/jsonpath_exec.c
  3. 234
      src/backend/utils/adt/numeric.c
  4. 14
      src/backend/utils/adt/timestamp.c
  5. 22
      src/include/utils/numeric.h

@ -6389,12 +6389,12 @@ numeric_to_char(PG_FUNCTION_ARGS)
if (IS_ROMAN(&Num)) if (IS_ROMAN(&Num))
{ {
int32 intvalue; int32 intvalue;
bool err; ErrorSaveContext escontext = {T_ErrorSaveContext};
/* Round and convert to int */ /* 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 */ /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
if (err) if (escontext.error_occurred)
intvalue = PG_INT32_MAX; intvalue = PG_INT32_MAX;
numstr = int_to_roman(intvalue); numstr = int_to_roman(intvalue);
} }

@ -252,7 +252,8 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
JsonbValue *larg, JsonbValue *larg,
JsonbValue *rarg, JsonbValue *rarg,
void *param); 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, static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
JsonPathGetVarCallback getVar, JsonPathGetVarCallback getVar,
@ -808,23 +809,23 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
case jpiAdd: case jpiAdd:
return executeBinaryArithmExpr(cxt, jsp, jb, return executeBinaryArithmExpr(cxt, jsp, jb,
numeric_add_opt_error, found); numeric_add_safe, found);
case jpiSub: case jpiSub:
return executeBinaryArithmExpr(cxt, jsp, jb, return executeBinaryArithmExpr(cxt, jsp, jb,
numeric_sub_opt_error, found); numeric_sub_safe, found);
case jpiMul: case jpiMul:
return executeBinaryArithmExpr(cxt, jsp, jb, return executeBinaryArithmExpr(cxt, jsp, jb,
numeric_mul_opt_error, found); numeric_mul_safe, found);
case jpiDiv: case jpiDiv:
return executeBinaryArithmExpr(cxt, jsp, jb, return executeBinaryArithmExpr(cxt, jsp, jb,
numeric_div_opt_error, found); numeric_div_safe, found);
case jpiMod: case jpiMod:
return executeBinaryArithmExpr(cxt, jsp, jb, return executeBinaryArithmExpr(cxt, jsp, jb,
numeric_mod_opt_error, found); numeric_mod_safe, found);
case jpiPlus: case jpiPlus:
return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found); return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
@ -1269,11 +1270,12 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (jb->type == jbvNumeric) if (jb->type == jbvNumeric)
{ {
bool have_error; ErrorSaveContext escontext = {T_ErrorSaveContext};
int64 val; int64 val;
val = numeric_int8_opt_error(jb->val.numeric, &have_error); val = numeric_int8_safe(jb->val.numeric,
if (have_error) (Node *) &escontext);
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM), (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s", errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
@ -1466,7 +1468,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
Datum dtypmod; Datum dtypmod;
int32 precision; int32 precision;
int32 scale = 0; int32 scale = 0;
bool have_error;
bool noerr; bool noerr;
ArrayType *arrtypmod; ArrayType *arrtypmod;
Datum datums[2]; Datum datums[2];
@ -1478,9 +1479,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (elem.type != jpiNumeric) if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() precision"); elog(ERROR, "invalid jsonpath item type for .decimal() precision");
precision = numeric_int4_opt_error(jspGetNumeric(&elem), precision = numeric_int4_safe(jspGetNumeric(&elem),
&have_error); (Node *) &escontext);
if (have_error) if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM), (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("precision of jsonpath item method .%s() is out of range for type integer", 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) if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() scale"); elog(ERROR, "invalid jsonpath item type for .decimal() scale");
scale = numeric_int4_opt_error(jspGetNumeric(&elem), scale = numeric_int4_safe(jspGetNumeric(&elem),
&have_error); (Node *) &escontext);
if (have_error) if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM), (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("scale of jsonpath item method .%s() is out of range for type integer", 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) if (jb->type == jbvNumeric)
{ {
bool have_error;
int32 val; int32 val;
ErrorSaveContext escontext = {T_ErrorSaveContext};
val = numeric_int4_opt_error(jb->val.numeric, &have_error); val = numeric_int4_safe(jb->val.numeric,
if (have_error) (Node *) &escontext);
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM), (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s", errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
@ -2149,11 +2151,11 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
} }
else 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; return jperError;
} }
@ -2433,7 +2435,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (jsp->type != jpiDatetime && jsp->type != jpiDate && if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
jsp->content.arg) jsp->content.arg)
{ {
bool have_error; ErrorSaveContext escontext = {T_ErrorSaveContext};
jspGetArg(jsp, &elem); jspGetArg(jsp, &elem);
@ -2441,9 +2443,9 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
elog(ERROR, "invalid jsonpath item type for %s argument", elog(ERROR, "invalid jsonpath item type for %s argument",
jspOperationName(jsp->type)); jspOperationName(jsp->type));
time_precision = numeric_int4_opt_error(jspGetNumeric(&elem), time_precision = numeric_int4_safe(jspGetNumeric(&elem),
&have_error); (Node *) &escontext);
if (have_error) if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION), (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("time precision of jsonpath item method .%s() is out of range for type integer", 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}; JsonValueList found = {0};
JsonPathExecResult res = executeItem(cxt, jsp, jb, &found); JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
Datum numeric_index; Datum numeric_index;
bool have_error = false; ErrorSaveContext escontext = {T_ErrorSaveContext};
if (jperIsError(res)) if (jperIsError(res))
return res; return res;
@ -3477,10 +3479,10 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
NumericGetDatum(jbv->val.numeric), NumericGetDatum(jbv->val.numeric),
Int32GetDatum(0)); Int32GetDatum(0));
*index = numeric_int4_opt_error(DatumGetNumeric(numeric_index), *index = numeric_int4_safe(DatumGetNumeric(numeric_index),
&have_error); (Node *) &escontext);
if (have_error) if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR, RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT), (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
errmsg("jsonpath array subscript is out of integer range")))); errmsg("jsonpath array subscript is out of integer range"))));

@ -517,7 +517,7 @@ static void numericvar_deserialize(StringInfo buf, NumericVar *var);
static Numeric duplicate_numeric(Numeric num); static Numeric duplicate_numeric(Numeric num);
static Numeric make_result(const NumericVar *var); 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(NumericVar *var, int32 typmod, Node *escontext);
static bool apply_typmod_special(Numeric num, 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; NumericVar value;
int base; int base;
bool have_error;
init_var(&value); init_var(&value);
@ -776,12 +775,7 @@ numeric_in(PG_FUNCTION_ARGS)
if (!apply_typmod(&value, typmod, escontext)) if (!apply_typmod(&value, typmod, escontext))
PG_RETURN_NULL(); PG_RETURN_NULL();
res = make_result_opt_error(&value, &have_error); res = make_result_safe(&value, escontext);
if (have_error)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value overflows numeric format")));
free_var(&value); free_var(&value);
} }
@ -2874,20 +2868,18 @@ numeric_add(PG_FUNCTION_ARGS)
Numeric num2 = PG_GETARG_NUMERIC(1); Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric res; Numeric res;
res = numeric_add_opt_error(num1, num2, NULL); res = numeric_add_safe(num1, num2, NULL);
PG_RETURN_NUMERIC(res); PG_RETURN_NUMERIC(res);
} }
/* /*
* numeric_add_opt_error() - * numeric_add_safe() -
* *
* Internal version of numeric_add(). If "*have_error" flag is provided, * Internal version of numeric_add() with support for soft error reporting.
* on error it's set to true, NULL returned. This is helpful when caller
* need to handle errors by itself.
*/ */
Numeric Numeric
numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error) numeric_add_safe(Numeric num1, Numeric num2, Node *escontext)
{ {
NumericVar arg1; NumericVar arg1;
NumericVar arg2; NumericVar arg2;
@ -2931,7 +2923,7 @@ numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
init_var(&result); init_var(&result);
add_var(&arg1, &arg2, &result); add_var(&arg1, &arg2, &result);
res = make_result_opt_error(&result, have_error); res = make_result_safe(&result, escontext);
free_var(&result); free_var(&result);
@ -2951,21 +2943,19 @@ numeric_sub(PG_FUNCTION_ARGS)
Numeric num2 = PG_GETARG_NUMERIC(1); Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric res; Numeric res;
res = numeric_sub_opt_error(num1, num2, NULL); res = numeric_sub_safe(num1, num2, NULL);
PG_RETURN_NUMERIC(res); PG_RETURN_NUMERIC(res);
} }
/* /*
* numeric_sub_opt_error() - * numeric_sub_safe() -
* *
* Internal version of numeric_sub(). If "*have_error" flag is provided, * Internal version of numeric_sub() with support for soft error reporting.
* on error it's set to true, NULL returned. This is helpful when caller
* need to handle errors by itself.
*/ */
Numeric Numeric
numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error) numeric_sub_safe(Numeric num1, Numeric num2, Node *escontext)
{ {
NumericVar arg1; NumericVar arg1;
NumericVar arg2; NumericVar arg2;
@ -3009,7 +2999,7 @@ numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
init_var(&result); init_var(&result);
sub_var(&arg1, &arg2, &result); sub_var(&arg1, &arg2, &result);
res = make_result_opt_error(&result, have_error); res = make_result_safe(&result, escontext);
free_var(&result); free_var(&result);
@ -3029,21 +3019,19 @@ numeric_mul(PG_FUNCTION_ARGS)
Numeric num2 = PG_GETARG_NUMERIC(1); Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric res; Numeric res;
res = numeric_mul_opt_error(num1, num2, NULL); res = numeric_mul_safe(num1, num2, NULL);
PG_RETURN_NUMERIC(res); PG_RETURN_NUMERIC(res);
} }
/* /*
* numeric_mul_opt_error() - * numeric_mul_safe() -
* *
* Internal version of numeric_mul(). If "*have_error" flag is provided, * Internal version of numeric_mul() with support for soft error reporting.
* on error it's set to true, NULL returned. This is helpful when caller
* need to handle errors by itself.
*/ */
Numeric Numeric
numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error) numeric_mul_safe(Numeric num1, Numeric num2, Node *escontext)
{ {
NumericVar arg1; NumericVar arg1;
NumericVar arg2; NumericVar arg2;
@ -3130,7 +3118,7 @@ numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
if (result.dscale > NUMERIC_DSCALE_MAX) if (result.dscale > NUMERIC_DSCALE_MAX)
round_var(&result, 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); free_var(&result);
@ -3150,21 +3138,19 @@ numeric_div(PG_FUNCTION_ARGS)
Numeric num2 = PG_GETARG_NUMERIC(1); Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric res; Numeric res;
res = numeric_div_opt_error(num1, num2, NULL); res = numeric_div_safe(num1, num2, NULL);
PG_RETURN_NUMERIC(res); PG_RETURN_NUMERIC(res);
} }
/* /*
* numeric_div_opt_error() - * numeric_div_safe() -
* *
* Internal version of numeric_div(). If "*have_error" flag is provided, * Internal version of numeric_div() with support for soft error reporting.
* on error it's set to true, NULL returned. This is helpful when caller
* need to handle errors by itself.
*/ */
Numeric Numeric
numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error) numeric_div_safe(Numeric num1, Numeric num2, Node *escontext)
{ {
NumericVar arg1; NumericVar arg1;
NumericVar arg2; NumericVar arg2;
@ -3172,9 +3158,6 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
Numeric res; Numeric res;
int rscale; int rscale;
if (have_error)
*have_error = false;
/* /*
* Handle NaN and infinities * Handle NaN and infinities
*/ */
@ -3189,15 +3172,7 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
switch (numeric_sign_internal(num2)) switch (numeric_sign_internal(num2))
{ {
case 0: case 0:
if (have_error) goto division_by_zero;
{
*have_error = true;
return NULL;
}
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
case 1: case 1:
return make_result(&const_pinf); return make_result(&const_pinf);
case -1: case -1:
@ -3212,15 +3187,7 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
switch (numeric_sign_internal(num2)) switch (numeric_sign_internal(num2))
{ {
case 0: case 0:
if (have_error) goto division_by_zero;
{
*have_error = true;
return NULL;
}
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
break;
case 1: case 1:
return make_result(&const_ninf); return make_result(&const_ninf);
case -1: case -1:
@ -3251,25 +3218,25 @@ numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
*/ */
rscale = select_div_scale(&arg1, &arg2); rscale = select_div_scale(&arg1, &arg2);
/* /* Check for division by zero */
* If "have_error" is provided, check for division by zero here if (arg2.ndigits == 0 || arg2.digits[0] == 0)
*/ goto division_by_zero;
if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
{
*have_error = true;
return NULL;
}
/* /*
* Do the divide and return the result * Do the divide and return the result
*/ */
div_var(&arg1, &arg2, &result, rscale, true, true); 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); free_var(&result);
return res; return res;
division_by_zero:
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 num2 = PG_GETARG_NUMERIC(1);
Numeric res; Numeric res;
res = numeric_mod_opt_error(num1, num2, NULL); res = numeric_mod_safe(num1, num2, NULL);
PG_RETURN_NUMERIC(res); PG_RETURN_NUMERIC(res);
} }
/* /*
* numeric_mod_opt_error() - * numeric_mod_safe() -
* *
* Internal version of numeric_mod(). If "*have_error" flag is provided, * Internal version of numeric_mod() with support for soft error reporting.
* on error it's set to true, NULL returned. This is helpful when caller
* need to handle errors by itself.
*/ */
Numeric Numeric
numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error) numeric_mod_safe(Numeric num1, Numeric num2, Node *escontext)
{ {
Numeric res; Numeric res;
NumericVar arg1; NumericVar arg1;
NumericVar arg2; NumericVar arg2;
NumericVar result; NumericVar result;
if (have_error)
*have_error = false;
/* /*
* Handle NaN and infinities. We follow POSIX fmod() on this, except that * 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 * 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_IS_INF(num1))
{ {
if (numeric_sign_internal(num2) == 0) if (numeric_sign_internal(num2) == 0)
{ goto division_by_zero;
if (have_error)
{
*have_error = true;
return NULL;
}
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
}
/* Inf % any nonzero = NaN */ /* Inf % any nonzero = NaN */
return make_result(&const_nan); return make_result(&const_nan);
} }
@ -3432,22 +3386,22 @@ numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
init_var(&result); init_var(&result);
/* /* Check for division by zero */
* If "have_error" is provided, check for division by zero here if (arg2.ndigits == 0 || arg2.digits[0] == 0)
*/ goto division_by_zero;
if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
{
*have_error = true;
return NULL;
}
mod_var(&arg1, &arg2, &result); mod_var(&arg1, &arg2, &result);
res = make_result_opt_error(&result, NULL); res = make_result_safe(&result, escontext);
free_var(&result); free_var(&result);
return res; return res;
division_by_zero:
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)); PG_RETURN_NUMERIC(int64_to_numeric(val));
} }
/*
* Internal version of int4_numeric() with support for soft error reporting.
*/
int32 int32
numeric_int4_opt_error(Numeric num, bool *have_error) numeric_int4_safe(Numeric num, Node *escontext)
{ {
NumericVar x; NumericVar x;
int32 result; int32 result;
if (have_error)
*have_error = false;
if (NUMERIC_IS_SPECIAL(num)) if (NUMERIC_IS_SPECIAL(num))
{
if (have_error)
{
*have_error = true;
return 0;
}
else
{ {
if (NUMERIC_IS_NAN(num)) if (NUMERIC_IS_NAN(num))
ereport(ERROR, ereturn(escontext, 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert NaN to %s", "integer"))); errmsg("cannot convert NaN to %s", "integer")));
else else
ereport(ERROR, ereturn(escontext, 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert infinity to %s", "integer"))); errmsg("cannot convert infinity to %s", "integer")));
} }
}
/* Convert to variable format, then convert to int4 */ /* Convert to variable format, then convert to int4 */
init_var_from_num(num, &x); init_var_from_num(num, &x);
if (!numericvar_to_int32(&x, &result)) if (!numericvar_to_int32(&x, &result))
{ ereturn(escontext, 0,
if (have_error)
{
*have_error = true;
return 0;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"))); errmsg("integer out of range")));
}
}
return result; return result;
} }
@ -4459,7 +4395,7 @@ numeric_int4(PG_FUNCTION_ARGS)
{ {
Numeric num = PG_GETARG_NUMERIC(0); 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)); PG_RETURN_NUMERIC(int64_to_numeric(val));
} }
/*
* Internal version of int8_numeric() with support for soft error reporting.
*/
int64 int64
numeric_int8_opt_error(Numeric num, bool *have_error) numeric_int8_safe(Numeric num, Node *escontext)
{ {
NumericVar x; NumericVar x;
int64 result; int64 result;
if (have_error)
*have_error = false;
if (NUMERIC_IS_SPECIAL(num)) if (NUMERIC_IS_SPECIAL(num))
{
if (have_error)
{
*have_error = true;
return 0;
}
else
{ {
if (NUMERIC_IS_NAN(num)) if (NUMERIC_IS_NAN(num))
ereport(ERROR, ereturn(escontext, 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert NaN to %s", "bigint"))); errmsg("cannot convert NaN to %s", "bigint")));
else else
ereport(ERROR, ereturn(escontext, 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert infinity to %s", "bigint"))); errmsg("cannot convert infinity to %s", "bigint")));
} }
}
/* Convert to variable format, then convert to int8 */ /* Convert to variable format, then convert to int8 */
init_var_from_num(num, &x); init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &result)) if (!numericvar_to_int64(&x, &result))
{ ereturn(escontext, 0,
if (have_error)
{
*have_error = true;
return 0;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range"))); errmsg("bigint out of range")));
}
}
return result; return result;
} }
@ -4547,7 +4465,7 @@ numeric_int8(PG_FUNCTION_ARGS)
{ {
Numeric num = PG_GETARG_NUMERIC(0); 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 * Create the packed db numeric format in palloc()'d memory from
* a variable. This will handle NaN and Infinity cases. * 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 static Numeric
make_result_opt_error(const NumericVar *var, bool *have_error) make_result_safe(const NumericVar *var, Node *escontext)
{ {
Numeric result; Numeric result;
NumericDigit *digits = var->digits; NumericDigit *digits = var->digits;
@ -7601,9 +7516,6 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
int n; int n;
Size len; Size len;
if (have_error)
*have_error = false;
if ((sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL) 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 */ /* Check for overflow of int16 fields */
if (NUMERIC_WEIGHT(result) != weight || if (NUMERIC_WEIGHT(result) != weight ||
NUMERIC_DSCALE(result) != var->dscale) NUMERIC_DSCALE(result) != var->dscale)
{ ereturn(escontext, NULL,
if (have_error)
{
*have_error = true;
return NULL;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value overflows numeric format"))); errmsg("value overflows numeric format")));
}
}
dump_numeric("make_result()", result); dump_numeric("make_result()", result);
return result; return result;
@ -7698,12 +7600,12 @@ make_result_opt_error(const NumericVar *var, bool *have_error)
/* /*
* make_result() - * make_result() -
* *
* An interface to make_result_opt_error() without "have_error" argument. * An interface to make_result_safe() without "escontext" argument.
*/ */
static Numeric static Numeric
make_result(const NumericVar *var) make_result(const NumericVar *var)
{ {
return make_result_opt_error(var, NULL); return make_result_safe(var, NULL);
} }

@ -5629,8 +5629,8 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
case DTK_JULIAN: case DTK_JULIAN:
if (retnumeric) if (retnumeric)
PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)), PG_RETURN_NUMERIC(numeric_add_safe(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), 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)), int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
NULL), NULL),
NULL)); NULL));
@ -5685,7 +5685,7 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
result = int64_div_fast_to_numeric(timestamp - epoch, 6); result = int64_div_fast_to_numeric(timestamp - epoch, 6);
else else
{ {
result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp), result = numeric_div_safe(numeric_sub_safe(int64_to_numeric(timestamp),
int64_to_numeric(epoch), int64_to_numeric(epoch),
NULL), NULL),
int64_to_numeric(1000000), int64_to_numeric(1000000),
@ -5903,8 +5903,8 @@ timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
case DTK_JULIAN: case DTK_JULIAN:
if (retnumeric) if (retnumeric)
PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)), PG_RETURN_NUMERIC(numeric_add_safe(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), 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)), int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
NULL), NULL),
NULL)); NULL));
@ -5956,7 +5956,7 @@ timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
result = int64_div_fast_to_numeric(timestamp - epoch, 6); result = int64_div_fast_to_numeric(timestamp - epoch, 6);
else else
{ {
result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp), result = numeric_div_safe(numeric_sub_safe(int64_to_numeric(timestamp),
int64_to_numeric(epoch), int64_to_numeric(epoch),
NULL), NULL),
int64_to_numeric(1000000), int64_to_numeric(1000000),
@ -6247,7 +6247,7 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
result = int64_div_fast_to_numeric(val, 6); result = int64_div_fast_to_numeric(val, 6);
else else
result = result =
numeric_add_opt_error(int64_div_fast_to_numeric(interval->time, 6), numeric_add_safe(int64_div_fast_to_numeric(interval->time, 6),
int64_to_numeric(secs_from_day_month), int64_to_numeric(secs_from_day_month),
NULL); NULL);

@ -17,6 +17,9 @@
#include "common/pg_prng.h" #include "common/pg_prng.h"
#include "fmgr.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 * Limits on the precision and scale specifiable in a NUMERIC typmod. The
* precision is strictly positive, but the scale may be positive or negative. * 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_to_numeric(int64 val);
extern Numeric int64_div_fast_to_numeric(int64 val1, int log10val2); extern Numeric int64_div_fast_to_numeric(int64 val1, int log10val2);
extern Numeric numeric_add_opt_error(Numeric num1, Numeric num2, extern Numeric numeric_add_safe(Numeric num1, Numeric num2, Node *escontext);
bool *have_error); extern Numeric numeric_sub_safe(Numeric num1, Numeric num2, Node *escontext);
extern Numeric numeric_sub_opt_error(Numeric num1, Numeric num2, extern Numeric numeric_mul_safe(Numeric num1, Numeric num2, Node *escontext);
bool *have_error); extern Numeric numeric_div_safe(Numeric num1, Numeric num2, Node *escontext);
extern Numeric numeric_mul_opt_error(Numeric num1, Numeric num2, extern Numeric numeric_mod_safe(Numeric num1, Numeric num2, Node *escontext);
bool *have_error); extern int32 numeric_int4_safe(Numeric num, Node *escontext);
extern Numeric numeric_div_opt_error(Numeric num1, Numeric num2, extern int64 numeric_int8_safe(Numeric num, Node *escontext);
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 random_numeric(pg_prng_state *state, extern Numeric random_numeric(pg_prng_state *state,
Numeric rmin, Numeric rmax); Numeric rmin, Numeric rmax);

Loading…
Cancel
Save