]> granicus.if.org Git - postgresql/commitdiff
Numeric error suppression in jsonpath
authorAlexander Korotkov <akorotkov@postgresql.org>
Sat, 16 Mar 2019 09:21:19 +0000 (12:21 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Sat, 16 Mar 2019 09:21:19 +0000 (12:21 +0300)
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
doc/src/sgml/func.sgml
src/backend/utils/adt/float.c
src/backend/utils/adt/jsonpath_exec.c
src/backend/utils/adt/numeric.c
src/include/utils/float.h
src/include/utils/numeric.h
src/test/regress/expected/jsonb_jsonpath.out

index a13b3d6358182fd04a1018d4cbe63f6d9ad46be1..3a99e209a2b0b5df1679e435cf44a4f4b7630771 100644 (file)
@@ -12209,7 +12209,7 @@ table2-mapping
    <para>
     The <literal>@?</literal> and <literal>@@</literal> operators suppress
     errors including: lacking object field or array element, unexpected JSON
-    item type.
+    item type and numeric errors.
     This behavior might be helpful while searching over JSON document
     collections of varying structure.
    </para>
index 8e16d755b042d03030f61c7d2d377c7c229cf7c2..23649c10000cbe21d278f3737fb9edef7af6d01d 100644 (file)
@@ -336,8 +336,19 @@ float8in(PG_FUNCTION_ARGS)
        PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
 }
 
+/* Convenience macro: set *have_error flag (if provided) or throw error */
+#define RETURN_ERROR(throw_error) \
+do { \
+       if (have_error) { \
+               *have_error = true; \
+               return 0.0; \
+       } else { \
+               throw_error; \
+       } \
+} while (0)
+
 /*
- * float8in_internal - guts of float8in()
+ * float8in_internal_opt_error - guts of float8in()
  *
  * This is exposed for use by functions that want a reasonably
  * platform-independent way of inputting doubles.  The behavior is
@@ -353,10 +364,14 @@ float8in(PG_FUNCTION_ARGS)
  *
  * "num" could validly be declared "const char *", but that results in an
  * unreasonable amount of extra casting both here and in callers, so we don't.
+ *
+ * When "*have_error" flag is provided, it's set instead of throwing an
+ * error.  This is helpful when caller need to handle errors by itself.
  */
 double
-float8in_internal(char *num, char **endptr_p,
-                                 const char *type_name, const char *orig_string)
+float8in_internal_opt_error(char *num, char **endptr_p,
+                                                       const char *type_name, const char *orig_string,
+                                                       bool *have_error)
 {
        double          val;
        char       *endptr;
@@ -370,10 +385,10 @@ float8in_internal(char *num, char **endptr_p,
         * strtod() on different platforms.
         */
        if (*num == '\0')
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                errmsg("invalid input syntax for type %s: \"%s\"",
-                                               type_name, orig_string)));
+               RETURN_ERROR(ereport(ERROR,
+                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                                         errmsg("invalid input syntax for type %s: \"%s\"",
+                                                                        type_name, orig_string))));
 
        errno = 0;
        val = strtod(num, &endptr);
@@ -446,17 +461,19 @@ float8in_internal(char *num, char **endptr_p,
                                char       *errnumber = pstrdup(num);
 
                                errnumber[endptr - num] = '\0';
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                                errmsg("\"%s\" is out of range for type double precision",
-                                                               errnumber)));
+                               RETURN_ERROR(ereport(ERROR,
+                                                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                                                         errmsg("\"%s\" is out of range for "
+                                                                                        "type double precision",
+                                                                                        errnumber))));
                        }
                }
                else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("invalid input syntax for type %s: \"%s\"",
-                                                       type_name, orig_string)));
+                       RETURN_ERROR(ereport(ERROR,
+                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                                                 errmsg("invalid input syntax for type "
+                                                                                "%s: \"%s\"",
+                                                                                type_name, orig_string))));
        }
 #ifdef HAVE_BUGGY_SOLARIS_STRTOD
        else
@@ -479,14 +496,27 @@ float8in_internal(char *num, char **endptr_p,
        if (endptr_p)
                *endptr_p = endptr;
        else if (*endptr != '\0')
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                errmsg("invalid input syntax for type %s: \"%s\"",
-                                               type_name, orig_string)));
+               RETURN_ERROR(ereport(ERROR,
+                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                                         errmsg("invalid input syntax for type "
+                                                                        "%s: \"%s\"",
+                                                                        type_name, orig_string))));
 
        return val;
 }
 
+/*
+ * Interfact to float8in_internal_opt_error() without "have_error" argument.
+ */
+double
+float8in_internal(char *num, char **endptr_p,
+                                 const char *type_name, const char *orig_string)
+{
+       return float8in_internal_opt_error(num, endptr_p, type_name,
+                                                                          orig_string, NULL);
+}
+
+
 /*
  *             float8out               - converts float8 number to a string
  *                                               using a standard output format
index 0717071188f0f6b3aa80e5cdce01f7011c4eeeeb..4fc441f11e7f14472e81587a8302fdb78ac4ef5c 100644 (file)
@@ -179,6 +179,7 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
                                                                                                   JsonbValue *larg,
                                                                                                   JsonbValue *rarg,
                                                                                                   void *param);
+typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
 
 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
                                Jsonb *json, bool throwErrors, JsonValueList *result);
@@ -212,8 +213,8 @@ static JsonPathBool executePredicate(JsonPathExecContext *cxt,
                                 JsonbValue *jb, bool unwrapRightArg,
                                 JsonPathPredicateCallback exec, void *param);
 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
-                                               JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
-                                               JsonValueList *found);
+                                               JsonPathItem *jsp, JsonbValue *jb,
+                                               BinaryArithmFunc func, JsonValueList *found);
 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
                                           JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
                                           JsonValueList *found);
@@ -830,23 +831,23 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
                case jpiAdd:
                        return executeBinaryArithmExpr(cxt, jsp, jb,
-                                                                                  numeric_add, found);
+                                                                                  numeric_add_opt_error, found);
 
                case jpiSub:
                        return executeBinaryArithmExpr(cxt, jsp, jb,
-                                                                                  numeric_sub, found);
+                                                                                  numeric_sub_opt_error, found);
 
                case jpiMul:
                        return executeBinaryArithmExpr(cxt, jsp, jb,
-                                                                                  numeric_mul, found);
+                                                                                  numeric_mul_opt_error, found);
 
                case jpiDiv:
                        return executeBinaryArithmExpr(cxt, jsp, jb,
-                                                                                  numeric_div, found);
+                                                                                  numeric_div_opt_error, found);
 
                case jpiMod:
                        return executeBinaryArithmExpr(cxt, jsp, jb,
-                                                                                  numeric_mod, found);
+                                                                                  numeric_mod_opt_error, found);
 
                case jpiPlus:
                        return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
@@ -999,12 +1000,22 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                {
                                        char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
                                                                                                                                                  NumericGetDatum(jb->val.numeric)));
+                                       bool            have_error = false;
 
-                                       (void) float8in_internal(tmp,
-                                                                                        NULL,
-                                                                                        "double precision",
-                                                                                        tmp);
+                                       (void) float8in_internal_opt_error(tmp,
+                                                                                                          NULL,
+                                                                                                          "double precision",
+                                                                                                          tmp,
+                                                                                                          &have_error);
 
+                                       if (have_error)
+                                               RETURN_ERROR(ereport(ERROR,
+                                                                                        (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
+                                                                                         errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
+                                                                                         errdetail("jsonpath item method .%s() "
+                                                                                                               "can only be applied to "
+                                                                                                               "a numeric value",
+                                                                                                               jspOperationName(jsp->type)))));
                                        res = jperOk;
                                }
                                else if (jb->type == jbvString)
@@ -1013,13 +1024,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                        double          val;
                                        char       *tmp = pnstrdup(jb->val.string.val,
                                                                                           jb->val.string.len);
+                                       bool            have_error = false;
 
-                                       val = float8in_internal(tmp,
-                                                                                       NULL,
-                                                                                       "double precision",
-                                                                                       tmp);
+                                       val = float8in_internal_opt_error(tmp,
+                                                                                                         NULL,
+                                                                                                         "double precision",
+                                                                                                         tmp,
+                                                                                                         &have_error);
 
-                                       if (isinf(val))
+                                       if (have_error || isinf(val))
                                                RETURN_ERROR(ereport(ERROR,
                                                                                         (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
                                                                                          errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
@@ -1497,7 +1510,7 @@ executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
  */
 static JsonPathExecResult
 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
-                                               JsonbValue *jb, PGFunction func,
+                                               JsonbValue *jb, BinaryArithmFunc func,
                                                JsonValueList *found)
 {
        JsonPathExecResult jper;
@@ -1506,7 +1519,7 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
        JsonValueList rseq = {0};
        JsonbValue *lval;
        JsonbValue *rval;
-       Datum           res;
+       Numeric         res;
 
        jspGetLeftArg(jsp, &elem);
 
@@ -1542,16 +1555,26 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                                                                "is not a singleton numeric value",
                                                                                jspOperationName(jsp->type)))));
 
-       res = DirectFunctionCall2(func,
-                                                         NumericGetDatum(lval->val.numeric),
-                                                         NumericGetDatum(rval->val.numeric));
+       if (jspThrowErrors(cxt))
+       {
+               res = func(lval->val.numeric, rval->val.numeric, NULL);
+       }
+       else
+       {
+               bool            error = false;
+
+               res = func(lval->val.numeric, rval->val.numeric, &error);
+
+               if (error)
+                       return jperError;
+       }
 
        if (!jspGetNext(jsp, &elem) && !found)
                return jperOk;
 
        lval = palloc(sizeof(*lval));
        lval->type = jbvNumeric;
-       lval->val.numeric = DatumGetNumeric(res);
+       lval->val.numeric = res;
 
        return executeNextItem(cxt, jsp, &elem, lval, found, false);
 }
@@ -2108,6 +2131,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;
 
        if (jperIsError(res))
                return res;
@@ -2124,7 +2148,15 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
                                                                                NumericGetDatum(jbv->val.numeric),
                                                                                Int32GetDatum(0));
 
-       *index = DatumGetInt32(DirectFunctionCall1(numeric_int4, numeric_index));
+       *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
+                                                                       &have_error);
+
+       if (have_error)
+               RETURN_ERROR(ereport(ERROR,
+                                                        (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
+                                                         errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
+                                                         errdetail("jsonpath array subscript is "
+                                                                               "out of integer range"))));
 
        return jperOk;
 }
index 0765f2cdb597d51c74ddd17880065af056259f97..fbc2863622e37ef77993e700c62db71b64840c1c 100644 (file)
@@ -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() -
  *
index 0f82a25edeae73f927719a1fe7498bd0d6f97552..5d935bb032dfa01dc5981f48fc2248d5d361f23a 100644 (file)
@@ -40,6 +40,9 @@ extern PGDLLIMPORT int extra_float_digits;
 extern int     is_infinite(float8 val);
 extern float8 float8in_internal(char *num, char **endptr_p,
                                  const char *type_name, const char *orig_string);
+extern float8 float8in_internal_opt_error(char *num, char **endptr_p,
+                                                       const char *type_name, const char *orig_string,
+                                                       bool *have_error);
 extern char *float8out_internal(float8 num);
 extern int     float4_cmp_internal(float4 a, float4 b);
 extern int     float8_cmp_internal(float8 a, float8 b);
index 9109cff98eba61341c616daa354e06cf35c8b1df..b475c93e04738effc10ec23c5a3b27d8eddcf109 100644 (file)
@@ -61,4 +61,16 @@ int32                numeric_maximum_size(int32 typmod);
 extern char *numeric_out_sci(Numeric num, int scale);
 extern char *numeric_normalize(Numeric num);
 
+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 *error);
+
 #endif                                                 /* _PG_NUMERIC_H_ */
index c12dfd6b9248d18a193ecbc22c078c84d1b25cd0..0e2e2c474c06968356890662ae4315a1ad360293 100644 (file)
@@ -127,13 +127,23 @@ select jsonb_path_query('[1]', 'strict $[1]', silent => true);
 (0 rows)
 
 select jsonb '[1]' @? 'lax $[10000000000000000]';
-ERROR:  integer out of range
+ ?column? 
+----------
+(1 row)
+
 select jsonb '[1]' @? 'strict $[10000000000000000]';
-ERROR:  integer out of range
+ ?column? 
+----------
+(1 row)
+
 select jsonb_path_query('[1]', 'lax $[10000000000000000]');
-ERROR:  integer out of range
+ERROR:  invalid SQL/JSON subscript
+DETAIL:  jsonpath array subscript is out of integer range
 select jsonb_path_query('[1]', 'strict $[10000000000000000]');
-ERROR:  integer out of range
+ERROR:  invalid SQL/JSON subscript
+DETAIL:  jsonpath array subscript is out of integer range
 select jsonb '[1]' @? '$[0]';
  ?column? 
 ----------
@@ -1037,9 +1047,19 @@ select jsonb '1' @? '$ ? ($ > 0)';
 
 -- arithmetic errors
 select jsonb_path_query('[1,2,0,3]', '$[*] ? (2 / @ > 0)');
-ERROR:  division by zero
+ jsonb_path_query 
+------------------
+ 1
+ 2
+ 3
+(3 rows)
+
 select jsonb_path_query('[1,2,0,3]', '$[*] ? ((2 / @ > 0) is unknown)');
-ERROR:  division by zero
+ jsonb_path_query 
+------------------
+ 0
+(1 row)
+
 select jsonb_path_query('0', '1 / $');
 ERROR:  division by zero
 select jsonb_path_query('0', '1 / $ + 2');
@@ -1502,7 +1522,8 @@ select jsonb_path_query('"1.23"', '$.double()');
 (1 row)
 
 select jsonb_path_query('"1.23aaa"', '$.double()');
-ERROR:  invalid input syntax for type double precision: "1.23aaa"
+ERROR:  non-numeric SQL/JSON item
+DETAIL:  jsonpath item method .double() can only be applied to a numeric value
 select jsonb_path_query('"nan"', '$.double()');
  jsonb_path_query 
 ------------------