aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/numeric.c
diff options
context:
space:
mode:
authorAlexander Korotkov <akorotkov@postgresql.org>2019-03-16 12:21:19 +0300
committerAlexander Korotkov <akorotkov@postgresql.org>2019-03-16 12:21:19 +0300
commit16d489b0fe058e527619f5e9d92fd7ca3c6c2994 (patch)
treed0f82a4250a33db045574fc69137eb32d52d04f9 /src/backend/utils/adt/numeric.c
parent72b6460336e86ad5cafd3426af6013c7d8457367 (diff)
downloadpostgresql-16d489b0fe058e527619f5e9d92fd7ca3c6c2994.tar.gz
postgresql-16d489b0fe058e527619f5e9d92fd7ca3c6c2994.zip
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
Diffstat (limited to 'src/backend/utils/adt/numeric.c')
-rw-r--r--src/backend/utils/adt/numeric.c252
1 files changed, 205 insertions, 47 deletions
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 0765f2cdb59..fbc2863622e 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -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
@@ -2550,15 +2625,24 @@ 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,9 +6311,19 @@ 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;
@@ -6185,6 +6331,18 @@ make_result(const NumericVar *var)
/*
+ * 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() -
*
* Do bounds checking and rounding according to the attributes