Convert range_in and multirange_in to report errors softly.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 15 Dec 2022 17:18:36 +0000 (12:18 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 15 Dec 2022 17:18:36 +0000 (12:18 -0500)
This is mostly straightforward, except that if the range type
has a canonical function, that might throw an error during range
input.  (Such errors probably only occur for edge cases: in the
in-core canonical functions, it happens only if a bound has the
maximum valid value for the underlying type.)  Hence, this patch
extends the soft-error regime to allow canonical functions to
return errors softly as well.  Extensions implementing range
canonical functions will need modification anyway because of the
API change for range_serialize(); while at it, they might want
to do something similar to what's been done here in the in-core
canonical functions.

Discussion: https://postgr.es/m/3284599.1671075185@sss.pgh.pa.us

12 files changed:
src/backend/utils/adt/multirangetypes.c
src/backend/utils/adt/multirangetypes_selfuncs.c
src/backend/utils/adt/rangetypes.c
src/backend/utils/adt/rangetypes_gist.c
src/backend/utils/adt/rangetypes_selfuncs.c
src/backend/utils/adt/rangetypes_spgist.c
src/backend/utils/adt/rangetypes_typanalyze.c
src/include/utils/rangetypes.h
src/test/regress/expected/multirangetypes.out
src/test/regress/expected/rangetypes.out
src/test/regress/sql/multirangetypes.sql
src/test/regress/sql/rangetypes.sql

index 307d087c977b543b50a90440d96a4ef37a4dbc41..ed26cfba2dea2e4dbb744d42c93155c73231ca40 100644 (file)
@@ -120,6 +120,7 @@ multirange_in(PG_FUNCTION_ARGS)
        char       *input_str = PG_GETARG_CSTRING(0);
        Oid                     mltrngtypoid = PG_GETARG_OID(1);
        Oid                     typmod = PG_GETARG_INT32(2);
+       Node       *escontext = fcinfo->context;
        TypeCacheEntry *rangetyp;
        int32           ranges_seen = 0;
        int32           range_count = 0;
@@ -133,6 +134,7 @@ multirange_in(PG_FUNCTION_ARGS)
        const char *range_str_begin = NULL;
        int32           range_str_len;
        char       *range_str;
+       Datum           range_datum;
 
        cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
        rangetyp = cache->typcache->rngtype;
@@ -144,7 +146,7 @@ multirange_in(PG_FUNCTION_ARGS)
        if (*ptr == '{')
                ptr++;
        else
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed multirange literal: \"%s\"",
                                                input_str),
@@ -157,7 +159,7 @@ multirange_in(PG_FUNCTION_ARGS)
                char            ch = *ptr;
 
                if (ch == '\0')
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed multirange literal: \"%s\"",
                                                        input_str),
@@ -186,7 +188,7 @@ multirange_in(PG_FUNCTION_ARGS)
                                        parse_state = MULTIRANGE_AFTER_RANGE;
                                }
                                else
-                                       ereport(ERROR,
+                                       ereturn(escontext, (Datum) 0,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed multirange literal: \"%s\"",
                                                                        input_str),
@@ -204,10 +206,14 @@ multirange_in(PG_FUNCTION_ARGS)
                                                        repalloc(ranges, range_capacity * sizeof(RangeType *));
                                        }
                                        ranges_seen++;
-                                       range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
-                                                                                                                                range_str,
-                                                                                                                                cache->typioparam,
-                                                                                                                                typmod));
+                                       if (!InputFunctionCallSafe(&cache->typioproc,
+                                                                                          range_str,
+                                                                                          cache->typioparam,
+                                                                                          typmod,
+                                                                                          escontext,
+                                                                                          &range_datum))
+                                               PG_RETURN_NULL();
+                                       range = DatumGetRangeTypeP(range_datum);
                                        if (!RangeIsEmpty(range))
                                                ranges[range_count++] = range;
                                        parse_state = MULTIRANGE_AFTER_RANGE;
@@ -256,7 +262,7 @@ multirange_in(PG_FUNCTION_ARGS)
                                else if (ch == '}')
                                        parse_state = MULTIRANGE_FINISHED;
                                else
-                                       ereport(ERROR,
+                                       ereturn(escontext, (Datum) 0,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed multirange literal: \"%s\"",
                                                                        input_str),
@@ -280,7 +286,7 @@ multirange_in(PG_FUNCTION_ARGS)
                ptr++;
 
        if (*ptr != '\0')
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed multirange literal: \"%s\"",
                                                input_str),
@@ -807,7 +813,7 @@ multirange_get_union_range(TypeCacheEntry *rangetyp,
        multirange_get_bounds(rangetyp, mr, 0, &lower, &tmp);
        multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper);
 
-       return make_range(rangetyp, &lower, &upper, false);
+       return make_range(rangetyp, &lower, &upper, false, NULL);
 }
 
 
@@ -2696,7 +2702,8 @@ range_merge_from_multirange(PG_FUNCTION_ARGS)
                multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
                                                          &lastLower, &lastUpper);
 
-               result = make_range(typcache->rngtype, &firstLower, &lastUpper, false);
+               result = make_range(typcache->rngtype, &firstLower, &lastUpper,
+                                                       false, NULL);
        }
 
        PG_RETURN_RANGE_P(result);
index 919c8889d459f146ac2860c10d5a5094f4b9ebfd..8fd6250e4a398c4d70057380d99e4ab3d4686511 100644 (file)
@@ -221,7 +221,8 @@ multirangesel(PG_FUNCTION_ARGS)
                        upper.val = ((Const *) other)->constvalue;
                        upper.infinite = false;
                        upper.lower = false;
-                       constrange = range_serialize(typcache->rngtype, &lower, &upper, false);
+                       constrange = range_serialize(typcache->rngtype, &lower, &upper,
+                                                                                false, NULL);
                        constmultirange = make_multirange(typcache->type_id, typcache->rngtype,
                                                                                          1, &constrange);
                }
index b09cb49054cc6d0ca1d6b2ea3bcab70aca0c45e4..2817b5e06ee6bfb62ae1de08f57f7dad1d2b11e8 100644 (file)
@@ -35,6 +35,7 @@
 #include "lib/stringinfo.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "nodes/miscnodes.h"
 #include "port/pg_bitutils.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
@@ -55,10 +56,11 @@ typedef struct RangeIOData
 static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
                                                                          IOFuncSelector func);
 static char range_parse_flags(const char *flags_str);
-static void range_parse(const char *string, char *flags, char **lbound_str,
-                                               char **ubound_str);
+static bool range_parse(const char *string, char *flags, char **lbound_str,
+                                               char **ubound_str, Node *escontext);
 static const char *range_parse_bound(const char *string, const char *ptr,
-                                                                        char **bound_str, bool *infinite);
+                                                                        char **bound_str, bool *infinite,
+                                                                        Node *escontext);
 static char *range_deparse(char flags, const char *lbound_str,
                                                   const char *ubound_str);
 static char *range_bound_escape(const char *value);
@@ -80,6 +82,7 @@ range_in(PG_FUNCTION_ARGS)
        char       *input_str = PG_GETARG_CSTRING(0);
        Oid                     rngtypoid = PG_GETARG_OID(1);
        Oid                     typmod = PG_GETARG_INT32(2);
+       Node       *escontext = fcinfo->context;
        RangeType  *range;
        RangeIOData *cache;
        char            flags;
@@ -93,15 +96,20 @@ range_in(PG_FUNCTION_ARGS)
        cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
 
        /* parse */
-       range_parse(input_str, &flags, &lbound_str, &ubound_str);
+       if (!range_parse(input_str, &flags, &lbound_str, &ubound_str, escontext))
+               PG_RETURN_NULL();
 
        /* call element type's input function */
        if (RANGE_HAS_LBOUND(flags))
-               lower.val = InputFunctionCall(&cache->typioproc, lbound_str,
-                                                                         cache->typioparam, typmod);
+               if (!InputFunctionCallSafe(&cache->typioproc, lbound_str,
+                                                                  cache->typioparam, typmod,
+                                                                  escontext, &lower.val))
+                       PG_RETURN_NULL();
        if (RANGE_HAS_UBOUND(flags))
-               upper.val = InputFunctionCall(&cache->typioproc, ubound_str,
-                                                                         cache->typioparam, typmod);
+               if (!InputFunctionCallSafe(&cache->typioproc, ubound_str,
+                                                                  cache->typioparam, typmod,
+                                                                  escontext, &upper.val))
+                       PG_RETURN_NULL();
 
        lower.infinite = (flags & RANGE_LB_INF) != 0;
        lower.inclusive = (flags & RANGE_LB_INC) != 0;
@@ -111,7 +119,8 @@ range_in(PG_FUNCTION_ARGS)
        upper.lower = false;
 
        /* serialize and canonicalize */
-       range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY);
+       range = make_range(cache->typcache, &lower, &upper,
+                                          flags & RANGE_EMPTY, escontext);
 
        PG_RETURN_RANGE_P(range);
 }
@@ -234,7 +243,8 @@ range_recv(PG_FUNCTION_ARGS)
        upper.lower = false;
 
        /* serialize and canonicalize */
-       range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY);
+       range = make_range(cache->typcache, &lower, &upper,
+                                          flags & RANGE_EMPTY, NULL);
 
        PG_RETURN_RANGE_P(range);
 }
@@ -378,7 +388,7 @@ range_constructor2(PG_FUNCTION_ARGS)
        upper.inclusive = false;
        upper.lower = false;
 
-       range = make_range(typcache, &lower, &upper, false);
+       range = make_range(typcache, &lower, &upper, false, NULL);
 
        PG_RETURN_RANGE_P(range);
 }
@@ -415,7 +425,7 @@ range_constructor3(PG_FUNCTION_ARGS)
        upper.inclusive = (flags & RANGE_UB_INC) != 0;
        upper.lower = false;
 
-       range = make_range(typcache, &lower, &upper, false);
+       range = make_range(typcache, &lower, &upper, false, NULL);
 
        PG_RETURN_RANGE_P(range);
 }
@@ -766,7 +776,7 @@ bounds_adjacent(TypeCacheEntry *typcache, RangeBound boundA, RangeBound boundB)
                /* change upper/lower labels to avoid Assert failures */
                boundA.lower = true;
                boundB.lower = false;
-               r = make_range(typcache, &boundA, &boundB, false);
+               r = make_range(typcache, &boundA, &boundB, false, NULL);
                return RangeIsEmpty(r);
        }
        else if (cmp == 0)
@@ -1012,14 +1022,14 @@ range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
        {
                lower2.inclusive = !lower2.inclusive;
                lower2.lower = false;   /* it will become the upper bound */
-               return make_range(typcache, &lower1, &lower2, false);
+               return make_range(typcache, &lower1, &lower2, false, NULL);
        }
 
        if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
        {
                upper2.inclusive = !upper2.inclusive;
                upper2.lower = true;    /* it will become the lower bound */
-               return make_range(typcache, &upper2, &upper1, false);
+               return make_range(typcache, &upper2, &upper1, false, NULL);
        }
 
        elog(ERROR, "unexpected case in range_minus");
@@ -1073,7 +1083,7 @@ range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
        else
                result_upper = &upper2;
 
-       return make_range(typcache, result_lower, result_upper, false);
+       return make_range(typcache, result_lower, result_upper, false, NULL);
 }
 
 Datum
@@ -1149,7 +1159,7 @@ range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const Ra
        else
                result_upper = &upper2;
 
-       return make_range(typcache, result_lower, result_upper, false);
+       return make_range(typcache, result_lower, result_upper, false, NULL);
 }
 
 /* range, range -> range, range functions */
@@ -1187,8 +1197,8 @@ range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeT
                upper2.inclusive = !upper2.inclusive;
                upper2.lower = true;
 
-               *output1 = make_range(typcache, &lower1, &lower2, false);
-               *output2 = make_range(typcache, &upper2, &upper1, false);
+               *output1 = make_range(typcache, &lower1, &lower2, false, NULL);
+               *output2 = make_range(typcache, &upper2, &upper1, false, NULL);
                return true;
        }
 
@@ -1446,6 +1456,7 @@ Datum
 int4range_canonical(PG_FUNCTION_ARGS)
 {
        RangeType  *r = PG_GETARG_RANGE_P(0);
+       Node       *escontext = fcinfo->context;
        TypeCacheEntry *typcache;
        RangeBound      lower;
        RangeBound      upper;
@@ -1460,23 +1471,39 @@ int4range_canonical(PG_FUNCTION_ARGS)
 
        if (!lower.infinite && !lower.inclusive)
        {
-               lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1));
+               int32           bnd = DatumGetInt32(lower.val);
+
+               /* Handle possible overflow manually */
+               if (unlikely(bnd == PG_INT32_MAX))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("integer out of range")));
+               lower.val = Int32GetDatum(bnd + 1);
                lower.inclusive = true;
        }
 
        if (!upper.infinite && upper.inclusive)
        {
-               upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1));
+               int32           bnd = DatumGetInt32(upper.val);
+
+               /* Handle possible overflow manually */
+               if (unlikely(bnd == PG_INT32_MAX))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("integer out of range")));
+               upper.val = Int32GetDatum(bnd + 1);
                upper.inclusive = false;
        }
 
-       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+                                                                         false, escontext));
 }
 
 Datum
 int8range_canonical(PG_FUNCTION_ARGS)
 {
        RangeType  *r = PG_GETARG_RANGE_P(0);
+       Node       *escontext = fcinfo->context;
        TypeCacheEntry *typcache;
        RangeBound      lower;
        RangeBound      upper;
@@ -1491,23 +1518,39 @@ int8range_canonical(PG_FUNCTION_ARGS)
 
        if (!lower.infinite && !lower.inclusive)
        {
-               lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1));
+               int64           bnd = DatumGetInt64(lower.val);
+
+               /* Handle possible overflow manually */
+               if (unlikely(bnd == PG_INT64_MAX))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("bigint out of range")));
+               lower.val = Int64GetDatum(bnd + 1);
                lower.inclusive = true;
        }
 
        if (!upper.infinite && upper.inclusive)
        {
-               upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1));
+               int64           bnd = DatumGetInt64(upper.val);
+
+               /* Handle possible overflow manually */
+               if (unlikely(bnd == PG_INT64_MAX))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("bigint out of range")));
+               upper.val = Int64GetDatum(bnd + 1);
                upper.inclusive = false;
        }
 
-       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+                                                                         false, escontext));
 }
 
 Datum
 daterange_canonical(PG_FUNCTION_ARGS)
 {
        RangeType  *r = PG_GETARG_RANGE_P(0);
+       Node       *escontext = fcinfo->context;
        TypeCacheEntry *typcache;
        RangeBound      lower;
        RangeBound      upper;
@@ -1523,18 +1566,35 @@ daterange_canonical(PG_FUNCTION_ARGS)
        if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) &&
                !lower.inclusive)
        {
-               lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1));
+               DateADT         bnd = DatumGetDateADT(lower.val);
+
+               /* Check for overflow -- note we already eliminated PG_INT32_MAX */
+               bnd++;
+               if (unlikely(!IS_VALID_DATE(bnd)))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("date out of range")));
+               lower.val = DateADTGetDatum(bnd);
                lower.inclusive = true;
        }
 
        if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) &&
                upper.inclusive)
        {
-               upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1));
+               DateADT         bnd = DatumGetDateADT(upper.val);
+
+               /* Check for overflow -- note we already eliminated PG_INT32_MAX */
+               bnd++;
+               if (unlikely(!IS_VALID_DATE(bnd)))
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                        errmsg("date out of range")));
+               upper.val = DateADTGetDatum(bnd);
                upper.inclusive = false;
        }
 
-       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+       PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+                                                                         false, escontext));
 }
 
 /*
@@ -1657,7 +1717,7 @@ range_get_typcache(FunctionCallInfo fcinfo, Oid rngtypid)
  */
 RangeType *
 range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
-                               bool empty)
+                               bool empty, struct Node *escontext)
 {
        RangeType  *range;
        int                     cmp;
@@ -1684,7 +1744,7 @@ range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
 
                /* error check: if lower bound value is above upper, it's wrong */
                if (cmp > 0)
-                       ereport(ERROR,
+                       ereturn(escontext, NULL,
                                        (errcode(ERRCODE_DATA_EXCEPTION),
                                         errmsg("range lower bound must be less than or equal to range upper bound")));
 
@@ -1882,17 +1942,41 @@ range_set_contain_empty(RangeType *range)
  */
 RangeType *
 make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
-                  bool empty)
+                  bool empty, struct Node *escontext)
 {
        RangeType  *range;
 
-       range = range_serialize(typcache, lower, upper, empty);
+       range = range_serialize(typcache, lower, upper, empty, escontext);
+
+       if (SOFT_ERROR_OCCURRED(escontext))
+               return NULL;
 
        /* no need to call canonical on empty ranges ... */
        if (OidIsValid(typcache->rng_canonical_finfo.fn_oid) &&
                !RangeIsEmpty(range))
-               range = DatumGetRangeTypeP(FunctionCall1(&typcache->rng_canonical_finfo,
-                                                                                                RangeTypePGetDatum(range)));
+       {
+               /* Do this the hard way so that we can pass escontext */
+               LOCAL_FCINFO(fcinfo, 1);
+               Datum           result;
+
+               InitFunctionCallInfoData(*fcinfo, &typcache->rng_canonical_finfo, 1,
+                                                                InvalidOid, escontext, NULL);
+
+               fcinfo->args[0].value = RangeTypePGetDatum(range);
+               fcinfo->args[0].isnull = false;
+
+               result = FunctionCallInvoke(fcinfo);
+
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return NULL;
+
+               /* Should not get a null result if there was no error */
+               if (fcinfo->isnull)
+                       elog(ERROR, "function %u returned NULL",
+                                typcache->rng_canonical_finfo.fn_oid);
+
+               range = DatumGetRangeTypeP(result);
+       }
 
        return range;
 }
@@ -2085,7 +2169,7 @@ make_empty_range(TypeCacheEntry *typcache)
        upper.inclusive = false;
        upper.lower = false;
 
-       return make_range(typcache, &lower, &upper, true);
+       return make_range(typcache, &lower, &upper, true, NULL);
 }
 
 
@@ -2170,10 +2254,13 @@ range_parse_flags(const char *flags_str)
  * Within a <string>, special characters (such as comma, parenthesis, or
  * brackets) can be enclosed in double-quotes or escaped with backslash. Within
  * double-quotes, a double-quote can be escaped with double-quote or backslash.
+ *
+ * Returns true on success, false on failure (but failures will return only if
+ * escontext is an ErrorSaveContext).
  */
-static void
+static bool
 range_parse(const char *string, char *flags, char **lbound_str,
-                       char **ubound_str)
+                       char **ubound_str, Node *escontext)
 {
        const char *ptr = string;
        bool            infinite;
@@ -2200,13 +2287,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
 
                /* should have consumed everything */
                if (*ptr != '\0')
-                       ereport(ERROR,
+                       ereturn(escontext, false,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed range literal: \"%s\"",
                                                        string),
                                         errdetail("Junk after \"empty\" key word.")));
 
-               return;
+               return true;
        }
 
        if (*ptr == '[')
@@ -2217,26 +2304,30 @@ range_parse(const char *string, char *flags, char **lbound_str,
        else if (*ptr == '(')
                ptr++;
        else
-               ereport(ERROR,
+               ereturn(escontext, false,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed range literal: \"%s\"",
                                                string),
                                 errdetail("Missing left parenthesis or bracket.")));
 
-       ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
+       ptr = range_parse_bound(string, ptr, lbound_str, &infinite, escontext);
+       if (ptr == NULL)
+               return false;
        if (infinite)
                *flags |= RANGE_LB_INF;
 
        if (*ptr == ',')
                ptr++;
        else
-               ereport(ERROR,
+               ereturn(escontext, false,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed range literal: \"%s\"",
                                                string),
                                 errdetail("Missing comma after lower bound.")));
 
-       ptr = range_parse_bound(string, ptr, ubound_str, &infinite);
+       ptr = range_parse_bound(string, ptr, ubound_str, &infinite, escontext);
+       if (ptr == NULL)
+               return false;
        if (infinite)
                *flags |= RANGE_UB_INF;
 
@@ -2248,7 +2339,7 @@ range_parse(const char *string, char *flags, char **lbound_str,
        else if (*ptr == ')')
                ptr++;
        else                                            /* must be a comma */
-               ereport(ERROR,
+               ereturn(escontext, false,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed range literal: \"%s\"",
                                                string),
@@ -2259,11 +2350,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
                ptr++;
 
        if (*ptr != '\0')
-               ereport(ERROR,
+               ereturn(escontext, false,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed range literal: \"%s\"",
                                                string),
                                 errdetail("Junk after right parenthesis or bracket.")));
+
+       return true;
 }
 
 /*
@@ -2279,10 +2372,11 @@ range_parse(const char *string, char *flags, char **lbound_str,
  *     *infinite: set true if no bound, else false
  *
  * The return value is the scan ptr, advanced past the bound string.
+ * However, if escontext is an ErrorSaveContext, we return NULL on failure.
  */
 static const char *
 range_parse_bound(const char *string, const char *ptr,
-                                 char **bound_str, bool *infinite)
+                                 char **bound_str, bool *infinite, Node *escontext)
 {
        StringInfoData buf;
 
@@ -2303,7 +2397,7 @@ range_parse_bound(const char *string, const char *ptr,
                        char            ch = *ptr++;
 
                        if (ch == '\0')
-                               ereport(ERROR,
+                               ereturn(escontext, NULL,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("malformed range literal: \"%s\"",
                                                                string),
@@ -2311,7 +2405,7 @@ range_parse_bound(const char *string, const char *ptr,
                        if (ch == '\\')
                        {
                                if (*ptr == '\0')
-                                       ereport(ERROR,
+                                       ereturn(escontext, NULL,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed range literal: \"%s\"",
                                                                        string),
index 5996de417de5f3788be6ae1bf5b634260a15e527..771c81f67b5a4edf7230b81ba130e70e0f4326ed 100644 (file)
@@ -876,7 +876,7 @@ range_super_union(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
                ((flags2 & RANGE_CONTAIN_EMPTY) || !(flags1 & RANGE_CONTAIN_EMPTY)))
                return r2;
 
-       result = make_range(typcache, result_lower, result_upper, false);
+       result = make_range(typcache, result_lower, result_upper, false, NULL);
 
        if ((flags1 & RANGE_CONTAIN_EMPTY) || (flags2 & RANGE_CONTAIN_EMPTY))
                range_set_contain_empty(result);
index c2795f45936f9a9bd31a3bf49aa17299a7628191..89114eba719ac3857c27f6b06573f2280d9e0e8f 100644 (file)
@@ -190,7 +190,7 @@ rangesel(PG_FUNCTION_ARGS)
                        upper.val = ((Const *) other)->constvalue;
                        upper.infinite = false;
                        upper.lower = false;
-                       constrange = range_serialize(typcache, &lower, &upper, false);
+                       constrange = range_serialize(typcache, &lower, &upper, false, NULL);
                }
        }
        else if (operator == OID_RANGE_ELEM_CONTAINED_OP)
index a47f04d975a15460e3f6adf1ecd82f7645ccfed2..d4a4d35e60e78cf3f700cf28bad490ddddd2d52b 100644 (file)
@@ -265,7 +265,7 @@ spg_range_quad_picksplit(PG_FUNCTION_ARGS)
 
        /* Construct "centroid" range from medians of lower and upper bounds */
        centroid = range_serialize(typcache, &lowerBounds[nonEmptyCount / 2],
-                                                          &upperBounds[nonEmptyCount / 2], false);
+                                                          &upperBounds[nonEmptyCount / 2], false, NULL);
        out->hasPrefix = true;
        out->prefixDatum = RangeTypePGetDatum(centroid);
 
index 2043d3f98be434f7ef730bb92a5d13a7a1c82d21..f73c904b903e714a935b8cafd47e6823af1a866d 100644 (file)
@@ -311,7 +311,8 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                                bound_hist_values[i] = PointerGetDatum(range_serialize(typcache,
                                                                                                                                           &lowers[pos],
                                                                                                                                           &uppers[pos],
-                                                                                                                                          false));
+                                                                                                                                          false,
+                                                                                                                                          NULL));
                                pos += delta;
                                posfrac += deltafrac;
                                if (posfrac >= (num_hist - 1))
index 2eee6a6c1f88e45716115e3e1c313455673f4105..3ae13cb9fb636cd6bf2c6b31cc93751477858c8b 100644 (file)
@@ -143,14 +143,16 @@ extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const Range
 extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
                                                                                  Oid rngtypid);
 extern RangeType *range_serialize(TypeCacheEntry *typcache, RangeBound *lower,
-                                                                 RangeBound *upper, bool empty);
+                                                                 RangeBound *upper, bool empty,
+                                                                 struct Node *escontext);
 extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
                                                          RangeBound *lower, RangeBound *upper,
                                                          bool *empty);
 extern char range_get_flags(const RangeType *range);
 extern void range_set_contain_empty(RangeType *range);
 extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
-                                                        RangeBound *upper, bool empty);
+                                                        RangeBound *upper, bool empty,
+                                                        struct Node *escontext);
 extern int     range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
                                                         const RangeBound *b2);
 extern int     range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
index ac2eb84c3af90db38a14609749a5607653d0b26e..14aa4c46cde8b9851e4a07042ec54a7bd61e6fe5 100644 (file)
@@ -274,6 +274,37 @@ select '{(a,a)}'::textmultirange;
  {}
 (1 row)
 
+-- Also try it with non-error-throwing API
+select pg_input_is_valid('{[1,2], [4,5]}', 'int4multirange');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('{[1,2], [4,5]', 'int4multirange');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('{[1,2], [4,5]', 'int4multirange');
+            pg_input_error_message             
+-----------------------------------------------
+ malformed multirange literal: "{[1,2], [4,5]"
+(1 row)
+
+select pg_input_is_valid('{[1,2], [4,zed]}', 'int4multirange');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('{[1,2], [4,zed]}', 'int4multirange');
+            pg_input_error_message            
+----------------------------------------------
+ invalid input syntax for type integer: "zed"
+(1 row)
+
 --
 -- test the constructor
 ---
index 04ccd5d451e9994f54eaa8839dc6c9b134318ebb..9eb31aecfe125da11cc6c6d6a9584bb78452008a 100644 (file)
@@ -175,6 +175,73 @@ select '(a,a)'::textrange;
  empty
 (1 row)
 
+-- Also try it with non-error-throwing API
+select pg_input_is_valid('(1,4)', 'int4range');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('(1,4', 'int4range');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('(1,4', 'int4range');
+     pg_input_error_message      
+---------------------------------
+ malformed range literal: "(1,4"
+(1 row)
+
+select pg_input_is_valid('(4,1)', 'int4range');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('(4,1)', 'int4range');
+                      pg_input_error_message                       
+-------------------------------------------------------------------
+ range lower bound must be less than or equal to range upper bound
+(1 row)
+
+select pg_input_is_valid('(4,zed)', 'int4range');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('(4,zed)', 'int4range');
+            pg_input_error_message            
+----------------------------------------------
+ invalid input syntax for type integer: "zed"
+(1 row)
+
+select pg_input_is_valid('[1,2147483647]', 'int4range');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('[1,2147483647]', 'int4range');
+ pg_input_error_message 
+------------------------
+ integer out of range
+(1 row)
+
+select pg_input_is_valid('[2000-01-01,5874897-12-31]', 'daterange');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('[2000-01-01,5874897-12-31]', 'daterange');
+ pg_input_error_message 
+------------------------
+ date out of range
+(1 row)
+
 --
 -- create some test data and test the operators
 --
index 1abcaeddb5c490798f58e29fe8211e694d8108cb..78a650eb0fbf97937465ae62bc5837b359e2490e 100644 (file)
@@ -58,6 +58,13 @@ select '{[a,a)}'::textmultirange;
 select '{(a,a]}'::textmultirange;
 select '{(a,a)}'::textmultirange;
 
+-- Also try it with non-error-throwing API
+select pg_input_is_valid('{[1,2], [4,5]}', 'int4multirange');
+select pg_input_is_valid('{[1,2], [4,5]', 'int4multirange');
+select pg_input_error_message('{[1,2], [4,5]', 'int4multirange');
+select pg_input_is_valid('{[1,2], [4,zed]}', 'int4multirange');
+select pg_input_error_message('{[1,2], [4,zed]}', 'int4multirange');
+
 --
 -- test the constructor
 ---
index 1a10f67f1992f308b300fb8fd2acc1dee0e67daf..798cd23910369066bc9cfd06a6750c248395bea2 100644 (file)
@@ -40,6 +40,19 @@ select '[a,a)'::textrange;
 select '(a,a]'::textrange;
 select '(a,a)'::textrange;
 
+-- Also try it with non-error-throwing API
+select pg_input_is_valid('(1,4)', 'int4range');
+select pg_input_is_valid('(1,4', 'int4range');
+select pg_input_error_message('(1,4', 'int4range');
+select pg_input_is_valid('(4,1)', 'int4range');
+select pg_input_error_message('(4,1)', 'int4range');
+select pg_input_is_valid('(4,zed)', 'int4range');
+select pg_input_error_message('(4,zed)', 'int4range');
+select pg_input_is_valid('[1,2147483647]', 'int4range');
+select pg_input_error_message('[1,2147483647]', 'int4range');
+select pg_input_is_valid('[2000-01-01,5874897-12-31]', 'daterange');
+select pg_input_error_message('[2000-01-01,5874897-12-31]', 'daterange');
+
 --
 -- create some test data and test the operators
 --