mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Numeric error suppression in jsonpath
Add support of numeric error suppression to jsonpath as it's required by standard. This commit doesn't use PG_TRY()/PG_CATCH() in order to implement that. Instead, it provides internal versions of numeric functions used, which support error suppression. Discussion: https://postgr.es/m/fcc6fc6a-b497-f39a-923d-aa34d0c588e8%402ndQuadrant.com Author: Alexander Korotkov, Nikita Glukhov Reviewed-by: Tomas Vondra
This commit is contained in:
@ -475,10 +475,11 @@ static char *get_str_from_var(const NumericVar *var);
|
||||
static char *get_str_from_var_sci(const NumericVar *var, int rscale);
|
||||
|
||||
static Numeric make_result(const NumericVar *var);
|
||||
static Numeric make_result_opt_error(const NumericVar *var, bool *error);
|
||||
|
||||
static void apply_typmod(NumericVar *var, int32 typmod);
|
||||
|
||||
static int32 numericvar_to_int32(const NumericVar *var);
|
||||
static bool numericvar_to_int32(const NumericVar *var, int32 *result);
|
||||
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
|
||||
static void int64_to_numericvar(int64 val, NumericVar *var);
|
||||
#ifdef HAVE_INT128
|
||||
@ -1558,7 +1559,10 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/* if result exceeds the range of a legal int4, we ereport here */
|
||||
result = numericvar_to_int32(&result_var);
|
||||
if (!numericvar_to_int32(&result_var, &result))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
|
||||
free_var(&count_var);
|
||||
free_var(&result_var);
|
||||
@ -2406,6 +2410,23 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
Numeric res;
|
||||
|
||||
res = numeric_add_opt_error(num1, num2, NULL);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* numeric_add_opt_error() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Numeric
|
||||
numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||
{
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
@ -2415,7 +2436,7 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
return make_result(&const_nan);
|
||||
|
||||
/*
|
||||
* Unpack the values, let add_var() compute the result and return it.
|
||||
@ -2426,11 +2447,11 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||
init_var(&result);
|
||||
add_var(&arg1, &arg2, &result);
|
||||
|
||||
res = make_result(&result);
|
||||
res = make_result_opt_error(&result, have_error);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -2444,6 +2465,24 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
Numeric res;
|
||||
|
||||
res = numeric_sub_opt_error(num1, num2, NULL);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_sub_opt_error() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Numeric
|
||||
numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||
{
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
@ -2453,7 +2492,7 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
return make_result(&const_nan);
|
||||
|
||||
/*
|
||||
* Unpack the values, let sub_var() compute the result and return it.
|
||||
@ -2464,11 +2503,11 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||
init_var(&result);
|
||||
sub_var(&arg1, &arg2, &result);
|
||||
|
||||
res = make_result(&result);
|
||||
res = make_result_opt_error(&result, have_error);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -2482,6 +2521,24 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
Numeric res;
|
||||
|
||||
res = numeric_mul_opt_error(num1, num2, NULL);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_mul_opt_error() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Numeric
|
||||
numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||
{
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
@ -2491,7 +2548,7 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
return make_result(&const_nan);
|
||||
|
||||
/*
|
||||
* Unpack the values, let mul_var() compute the result and return it.
|
||||
@ -2506,11 +2563,11 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||
init_var(&result);
|
||||
mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
|
||||
|
||||
res = make_result(&result);
|
||||
res = make_result_opt_error(&result, have_error);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -2524,6 +2581,24 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
Numeric res;
|
||||
|
||||
res = numeric_div_opt_error(num1, num2, NULL);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_div_opt_error() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Numeric
|
||||
numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||
{
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
@ -2534,7 +2609,7 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||
* Handle NaN
|
||||
*/
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
return make_result(&const_nan);
|
||||
|
||||
/*
|
||||
* Unpack the arguments
|
||||
@ -2549,16 +2624,25 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the divide and return the result
|
||||
*/
|
||||
div_var(&arg1, &arg2, &result, rscale, true);
|
||||
|
||||
res = make_result(&result);
|
||||
res = make_result_opt_error(&result, have_error);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -2615,25 +2699,52 @@ numeric_mod(PG_FUNCTION_ARGS)
|
||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||
Numeric res;
|
||||
|
||||
res = numeric_mod_opt_error(num1, num2, NULL);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* numeric_mod_opt_error() -
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Numeric
|
||||
numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||
{
|
||||
Numeric res;
|
||||
NumericVar arg1;
|
||||
NumericVar arg2;
|
||||
NumericVar result;
|
||||
|
||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
||||
return make_result(&const_nan);
|
||||
|
||||
init_var_from_num(num1, &arg1);
|
||||
init_var_from_num(num2, &arg2);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mod_var(&arg1, &arg2, &result);
|
||||
|
||||
res = make_result(&result);
|
||||
res = make_result_opt_error(&result, NULL);
|
||||
|
||||
free_var(&result);
|
||||
|
||||
PG_RETURN_NUMERIC(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -3090,52 +3201,75 @@ int4_numeric(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NUMERIC(res);
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
numeric_int4(PG_FUNCTION_ARGS)
|
||||
int32
|
||||
numeric_int4_opt_error(Numeric num, bool *have_error)
|
||||
{
|
||||
Numeric num = PG_GETARG_NUMERIC(0);
|
||||
NumericVar x;
|
||||
int32 result;
|
||||
|
||||
/* XXX would it be better to return NULL? */
|
||||
if (NUMERIC_IS_NAN(num))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot convert NaN to integer")));
|
||||
{
|
||||
if (have_error)
|
||||
{
|
||||
*have_error = true;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot convert NaN to integer")));
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert to variable format, then convert to int4 */
|
||||
init_var_from_num(num, &x);
|
||||
result = numericvar_to_int32(&x);
|
||||
PG_RETURN_INT32(result);
|
||||
|
||||
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")));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Datum
|
||||
numeric_int4(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Numeric num = PG_GETARG_NUMERIC(0);
|
||||
|
||||
PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a NumericVar, convert it to an int32. If the NumericVar
|
||||
* exceeds the range of an int32, raise the appropriate error via
|
||||
* ereport(). The input NumericVar is *not* free'd.
|
||||
* exceeds the range of an int32, false is returned, otherwise true is returned.
|
||||
* The input NumericVar is *not* free'd.
|
||||
*/
|
||||
static int32
|
||||
numericvar_to_int32(const NumericVar *var)
|
||||
static bool
|
||||
numericvar_to_int32(const NumericVar *var, int32 *result)
|
||||
{
|
||||
int32 result;
|
||||
int64 val;
|
||||
|
||||
if (!numericvar_to_int64(var, &val))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
return false;
|
||||
|
||||
/* Down-convert to int4 */
|
||||
result = (int32) val;
|
||||
*result = (int32) val;
|
||||
|
||||
/* Test for overflow by reverse-conversion. */
|
||||
if ((int64) result != val)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("integer out of range")));
|
||||
|
||||
return result;
|
||||
return ((int64) *result == val);
|
||||
}
|
||||
|
||||
Datum
|
||||
@ -6098,13 +6232,15 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
|
||||
|
||||
|
||||
/*
|
||||
* make_result() -
|
||||
* make_result_opt_error() -
|
||||
*
|
||||
* Create the packed db numeric format in palloc()'d memory from
|
||||
* a variable.
|
||||
* a variable. 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.
|
||||
*/
|
||||
static Numeric
|
||||
make_result(const NumericVar *var)
|
||||
make_result_opt_error(const NumericVar *var, bool *have_error)
|
||||
{
|
||||
Numeric result;
|
||||
NumericDigit *digits = var->digits;
|
||||
@ -6175,15 +6311,37 @@ make_result(const NumericVar *var)
|
||||
/* Check for overflow of int16 fields */
|
||||
if (NUMERIC_WEIGHT(result) != weight ||
|
||||
NUMERIC_DSCALE(result) != var->dscale)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value overflows numeric format")));
|
||||
{
|
||||
if (have_error)
|
||||
{
|
||||
*have_error = true;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value overflows numeric format")));
|
||||
}
|
||||
}
|
||||
|
||||
dump_numeric("make_result()", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_result() -
|
||||
*
|
||||
* An interface to make_result_opt_error() without "have_error" argument.
|
||||
*/
|
||||
static Numeric
|
||||
make_result(const NumericVar *var)
|
||||
{
|
||||
return make_result_opt_error(var, NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* apply_typmod() -
|
||||
*
|
||||
|
Reference in New Issue
Block a user