Convert a few datatype input functions to use "soft" error reporting.
authorTom Lane <[email protected]>
Fri, 9 Dec 2022 15:14:53 +0000 (10:14 -0500)
committerTom Lane <[email protected]>
Fri, 9 Dec 2022 15:14:53 +0000 (10:14 -0500)
This patch converts the input functions for bool, int2, int4, int8,
float4, float8, numeric, and contrib/cube to the new soft-error style.
array_in and record_in are also converted.  There's lots more to do,
but this is enough to provide proof-of-concept that the soft-error
API is usable, as well as reference examples for how to convert
input functions.

This patch is mostly by me, but it owes very substantial debt to
earlier work by Nikita Glukhov, Andrew Dunstan, and Amul Sul.
Thanks to Andres Freund for review.

Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru

39 files changed:
contrib/cube/cube.c
contrib/cube/cubedata.h
contrib/cube/cubeparse.y
contrib/cube/cubescan.l
contrib/cube/expected/cube.out
contrib/cube/sql/cube.sql
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/arrayutils.c
src/backend/utils/adt/bool.c
src/backend/utils/adt/float.c
src/backend/utils/adt/geo_ops.c
src/backend/utils/adt/int.c
src/backend/utils/adt/int8.c
src/backend/utils/adt/jsonpath_exec.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/numutils.c
src/backend/utils/adt/rowtypes.c
src/include/utils/array.h
src/include/utils/builtins.h
src/include/utils/float.h
src/test/regress/expected/arrays.out
src/test/regress/expected/boolean.out
src/test/regress/expected/float4-misrounded-input.out
src/test/regress/expected/float4.out
src/test/regress/expected/float8.out
src/test/regress/expected/int2.out
src/test/regress/expected/int4.out
src/test/regress/expected/int8.out
src/test/regress/expected/numeric.out
src/test/regress/expected/rowtypes.out
src/test/regress/sql/arrays.sql
src/test/regress/sql/boolean.sql
src/test/regress/sql/float4.sql
src/test/regress/sql/float8.sql
src/test/regress/sql/int2.sql
src/test/regress/sql/int4.sql
src/test/regress/sql/int8.sql
src/test/regress/sql/numeric.sql
src/test/regress/sql/rowtypes.sql

index 4f32c5dc1d59103757a3f05c61ce652e2b28ecf0..1fc447511a100a37d92ae260064357efd259de0c 100644 (file)
@@ -123,8 +123,9 @@ cube_in(PG_FUNCTION_ARGS)
 
        cube_scanner_init(str, &scanbuflen);
 
-       cube_yyparse(&result, scanbuflen);
+       cube_yyparse(&result, scanbuflen, fcinfo->context);
 
+       /* We might as well run this even on failure. */
        cube_scanner_finish();
 
        PG_RETURN_NDBOX_P(result);
index 640a7ca5800ecc88db3a9b8f36d2654b00b0d7e6..96fa41a04e710bb31380d3caf87ab2ebe1dad437 100644 (file)
@@ -61,9 +61,12 @@ typedef struct NDBOX
 
 /* in cubescan.l */
 extern int     cube_yylex(void);
-extern void cube_yyerror(NDBOX **result, Size scanbuflen, const char *message) pg_attribute_noreturn();
+extern void cube_yyerror(NDBOX **result, Size scanbuflen,
+                                                struct Node *escontext,
+                                                const char *message);
 extern void cube_scanner_init(const char *str, Size *scanbuflen);
 extern void cube_scanner_finish(void);
 
 /* in cubeparse.y */
-extern int     cube_yyparse(NDBOX **result, Size scanbuflen);
+extern int     cube_yyparse(NDBOX **result, Size scanbuflen,
+                                                struct Node *escontext);
index 977dcba965059aaf794e7ca79a5ded6ed0acab4d..44450d102747160e30fc1734606795f58325f9b3 100644 (file)
@@ -7,6 +7,7 @@
 #include "postgres.h"
 
 #include "cubedata.h"
+#include "nodes/miscnodes.h"
 #include "utils/float.h"
 
 /* All grammar constructs return strings */
 #define YYFREE   pfree
 
 static int item_count(const char *s, char delim);
-static NDBOX *write_box(int dim, char *str1, char *str2);
-static NDBOX *write_point_as_box(int dim, char *str);
+static bool write_box(int dim, char *str1, char *str2,
+                                         NDBOX **result, struct Node *escontext);
+static bool write_point_as_box(int dim, char *str,
+                                                          NDBOX **result, struct Node *escontext);
 
 %}
 
 /* BISON Declarations */
 %parse-param {NDBOX **result}
 %parse-param {Size scanbuflen}
+%parse-param {struct Node *escontext}
 %expect 0
 %name-prefix="cube_yy"
 
@@ -45,7 +49,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                dim = item_count($2, ',');
                if (item_count($4, ',') != dim)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("Different point dimensions in (%s) and (%s).",
@@ -54,7 +58,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                }
                if (dim > CUBE_MAX_DIM)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("A cube cannot have more than %d dimensions.",
@@ -62,7 +66,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                        YYABORT;
                }
 
-               *result = write_box( dim, $2, $4 );
+               if (!write_box(dim, $2, $4, result, escontext))
+                       YYABORT;
        }
 
        | paren_list COMMA paren_list
@@ -72,7 +77,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                dim = item_count($1, ',');
                if (item_count($3, ',') != dim)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("Different point dimensions in (%s) and (%s).",
@@ -81,7 +86,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                }
                if (dim > CUBE_MAX_DIM)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("A cube cannot have more than %d dimensions.",
@@ -89,7 +94,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                        YYABORT;
                }
 
-               *result = write_box( dim, $1, $3 );
+               if (!write_box(dim, $1, $3, result, escontext))
+                       YYABORT;
        }
 
        | paren_list
@@ -99,7 +105,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                dim = item_count($1, ',');
                if (dim > CUBE_MAX_DIM)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("A cube cannot have more than %d dimensions.",
@@ -107,7 +113,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                        YYABORT;
                }
 
-               *result = write_point_as_box(dim, $1);
+               if (!write_point_as_box(dim, $1, result, escontext))
+                       YYABORT;
        }
 
        | list
@@ -117,7 +124,7 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                dim = item_count($1, ',');
                if (dim > CUBE_MAX_DIM)
                {
-                       ereport(ERROR,
+                       errsave(escontext,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for cube"),
                                         errdetail("A cube cannot have more than %d dimensions.",
@@ -125,7 +132,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
                        YYABORT;
                }
 
-               *result = write_point_as_box(dim, $1);
+               if (!write_point_as_box(dim, $1, result, escontext))
+                       YYABORT;
        }
        ;
 
@@ -173,8 +181,9 @@ item_count(const char *s, char delim)
        return nitems;
 }
 
-static NDBOX *
-write_box(int dim, char *str1, char *str2)
+static bool
+write_box(int dim, char *str1, char *str2,
+                 NDBOX **result, struct Node *escontext)
 {
        NDBOX      *bp;
        char       *s;
@@ -190,18 +199,26 @@ write_box(int dim, char *str1, char *str2)
        s = str1;
        i = 0;
        if (dim > 0)
-               bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
+       {
+               bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
+       }
        while ((s = strchr(s, ',')) != NULL)
        {
                s++;
-               bp->x[i++] = float8in_internal(s, &endptr, "cube", str1);
+               bp->x[i++] = float8in_internal(s, &endptr, "cube", str1, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
        }
        Assert(i == dim);
 
        s = str2;
        if (dim > 0)
        {
-               bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
+               bp->x[i] = float8in_internal(s, &endptr, "cube", str2, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
                /* code this way to do right thing with NaN */
                point &= (bp->x[i] == bp->x[0]);
                i++;
@@ -209,7 +226,9 @@ write_box(int dim, char *str1, char *str2)
        while ((s = strchr(s, ',')) != NULL)
        {
                s++;
-               bp->x[i] = float8in_internal(s, &endptr, "cube", str2);
+               bp->x[i] = float8in_internal(s, &endptr, "cube", str2, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
                point &= (bp->x[i] == bp->x[i - dim]);
                i++;
        }
@@ -229,11 +248,13 @@ write_box(int dim, char *str1, char *str2)
                SET_POINT_BIT(bp);
        }
 
-       return bp;
+       *result = bp;
+       return true;
 }
 
-static NDBOX *
-write_point_as_box(int dim, char *str)
+static bool
+write_point_as_box(int dim, char *str,
+                                  NDBOX **result, struct Node *escontext)
 {
        NDBOX           *bp;
        int                     i,
@@ -250,13 +271,20 @@ write_point_as_box(int dim, char *str)
        s = str;
        i = 0;
        if (dim > 0)
-               bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
+       {
+               bp->x[i++] = float8in_internal(s, &endptr, "cube", str, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
+       }
        while ((s = strchr(s, ',')) != NULL)
        {
                s++;
-               bp->x[i++] = float8in_internal(s, &endptr, "cube", str);
+               bp->x[i++] = float8in_internal(s, &endptr, "cube", str, escontext);
+               if (SOFT_ERROR_OCCURRED(escontext))
+                       return false;
        }
        Assert(i == dim);
 
-       return bp;
+       *result = bp;
+       return true;
 }
index 6b316f2d5457b9c9e1308858dcd8aac826e4fb06..49cb69921653f968c90fcd20e9918b5192b4c62c 100644 (file)
@@ -72,11 +72,13 @@ NaN          [nN][aA][nN]
 
 /* result and scanbuflen are not used, but Bison expects this signature */
 void
-cube_yyerror(NDBOX **result, Size scanbuflen, const char *message)
+cube_yyerror(NDBOX **result, Size scanbuflen,
+                        struct Node *escontext,
+                        const char *message)
 {
        if (*yytext == YY_END_OF_BUFFER_CHAR)
        {
-               ereport(ERROR,
+               errsave(escontext,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for cube"),
                                 /* translator: %s is typically "syntax error" */
@@ -84,7 +86,7 @@ cube_yyerror(NDBOX **result, Size scanbuflen, const char *message)
        }
        else
        {
-               ereport(ERROR,
+               errsave(escontext,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for cube"),
                                 /* translator: first %s is typically "syntax error" */
index 5b89cb1a26b144168a15fd0a9fd1b93fe63db50c..dc23e5ccc01960fa297ec63aa123ec276df489ce 100644 (file)
@@ -325,6 +325,31 @@ SELECT '-1e-700'::cube AS cube; -- out of range
 ERROR:  "-1e-700" is out of range for type double precision
 LINE 1: SELECT '-1e-700'::cube AS cube;
                ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('(1,2)', 'cube');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('[(1),]', 'cube');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('-1e-700', 'cube');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('-1e-700', 'cube');
+               pg_input_error_message                
+-----------------------------------------------------
+ "-1e-700" is out of range for type double precision
+(1 row)
+
 --
 -- Testing building cubes from float8 values
 --
index 7f8b2e3979926c7be168ae6212e4aa0f60098f51..384883d16e97d84a04f029e45ec43b6cec01a681 100644 (file)
@@ -79,6 +79,12 @@ SELECT '1,2a'::cube AS cube; -- 7
 SELECT '1..2'::cube AS cube; -- 7
 SELECT '-1e-700'::cube AS cube; -- out of range
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('(1,2)', 'cube');
+SELECT pg_input_is_valid('[(1),]', 'cube');
+SELECT pg_input_is_valid('-1e-700', 'cube');
+SELECT pg_input_error_message('-1e-700', 'cube');
+
 --
 -- Testing building cubes from float8 values
 --
index 59a0852d07cb3e020c61d4174692c6776dd7850b..0d3d46b9a521825b08a9d1c525c0cc25ff9e36df 100644 (file)
@@ -90,14 +90,15 @@ typedef struct ArrayIteratorData
 }                      ArrayIteratorData;
 
 static bool array_isspace(char ch);
-static int     ArrayCount(const char *str, int *dim, char typdelim);
-static void ReadArrayStr(char *arrayStr, const char *origStr,
+static int     ArrayCount(const char *str, int *dim, char typdelim,
+                                          Node *escontext);
+static bool ReadArrayStr(char *arrayStr, const char *origStr,
                                                 int nitems, int ndim, int *dim,
                                                 FmgrInfo *inputproc, Oid typioparam, int32 typmod,
                                                 char typdelim,
                                                 int typlen, bool typbyval, char typalign,
                                                 Datum *values, bool *nulls,
-                                                bool *hasnulls, int32 *nbytes);
+                                                bool *hasnulls, int32 *nbytes, Node *escontext);
 static void ReadArrayBinary(StringInfo buf, int nitems,
                                                        FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
                                                        int typlen, bool typbyval, char typalign,
@@ -177,6 +178,7 @@ array_in(PG_FUNCTION_ARGS)
        Oid                     element_type = PG_GETARG_OID(1);        /* type of an array
                                                                                                         * element */
        int32           typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
+       Node       *escontext = fcinfo->context;
        int                     typlen;
        bool            typbyval;
        char            typalign;
@@ -258,7 +260,7 @@ array_in(PG_FUNCTION_ARGS)
                        break;                          /* no more dimension items */
                p++;
                if (ndim >= MAXDIM)
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                                                        ndim + 1, MAXDIM)));
@@ -266,7 +268,7 @@ array_in(PG_FUNCTION_ARGS)
                for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
                         /* skip */ ;
                if (q == p)                             /* no digits? */
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
@@ -280,7 +282,7 @@ array_in(PG_FUNCTION_ARGS)
                        for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
                                 /* skip */ ;
                        if (q == p)                     /* no digits? */
-                               ereport(ERROR,
+                               ereturn(escontext, (Datum) 0,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("malformed array literal: \"%s\"", string),
                                                 errdetail("Missing array dimension value.")));
@@ -291,7 +293,7 @@ array_in(PG_FUNCTION_ARGS)
                        lBound[ndim] = 1;
                }
                if (*q != ']')
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("Missing \"%s\" after array dimensions.",
@@ -301,7 +303,7 @@ array_in(PG_FUNCTION_ARGS)
                ub = atoi(p);
                p = q + 1;
                if (ub < lBound[ndim])
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                                         errmsg("upper bound cannot be less than lower bound")));
 
@@ -313,11 +315,13 @@ array_in(PG_FUNCTION_ARGS)
        {
                /* No array dimensions, so intuit dimensions from brace structure */
                if (*p != '{')
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("Array value must start with \"{\" or dimension information.")));
-               ndim = ArrayCount(p, dim, typdelim);
+               ndim = ArrayCount(p, dim, typdelim, escontext);
+               if (ndim < 0)
+                       PG_RETURN_NULL();
                for (i = 0; i < ndim; i++)
                        lBound[i] = 1;
        }
@@ -328,7 +332,7 @@ array_in(PG_FUNCTION_ARGS)
 
                /* If array dimensions are given, expect '=' operator */
                if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("Missing \"%s\" after array dimensions.",
@@ -342,20 +346,22 @@ array_in(PG_FUNCTION_ARGS)
                 * were given
                 */
                if (*p != '{')
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("Array contents must start with \"{\".")));
-               ndim_braces = ArrayCount(p, dim_braces, typdelim);
+               ndim_braces = ArrayCount(p, dim_braces, typdelim, escontext);
+               if (ndim_braces < 0)
+                       PG_RETURN_NULL();
                if (ndim_braces != ndim)
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", string),
                                         errdetail("Specified array dimensions do not match array contents.")));
                for (i = 0; i < ndim; ++i)
                {
                        if (dim[i] != dim_braces[i])
-                               ereport(ERROR,
+                               ereturn(escontext, (Datum) 0,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("malformed array literal: \"%s\"", string),
                                                 errdetail("Specified array dimensions do not match array contents.")));
@@ -372,8 +378,11 @@ array_in(PG_FUNCTION_ARGS)
 #endif
 
        /* This checks for overflow of the array dimensions */
-       nitems = ArrayGetNItems(ndim, dim);
-       ArrayCheckBounds(ndim, dim, lBound);
+       nitems = ArrayGetNItemsSafe(ndim, dim, escontext);
+       if (nitems < 0)
+               PG_RETURN_NULL();
+       if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext))
+               PG_RETURN_NULL();
 
        /* Empty array? */
        if (nitems == 0)
@@ -381,13 +390,14 @@ array_in(PG_FUNCTION_ARGS)
 
        dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
        nullsPtr = (bool *) palloc(nitems * sizeof(bool));
-       ReadArrayStr(p, string,
-                                nitems, ndim, dim,
-                                &my_extra->proc, typioparam, typmod,
-                                typdelim,
-                                typlen, typbyval, typalign,
-                                dataPtr, nullsPtr,
-                                &hasnulls, &nbytes);
+       if (!ReadArrayStr(p, string,
+                                         nitems, ndim, dim,
+                                         &my_extra->proc, typioparam, typmod,
+                                         typdelim,
+                                         typlen, typbyval, typalign,
+                                         dataPtr, nullsPtr,
+                                         &hasnulls, &nbytes, escontext))
+               PG_RETURN_NULL();
        if (hasnulls)
        {
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
@@ -451,9 +461,12 @@ array_isspace(char ch)
  *
  * Returns number of dimensions as function result.  The axis lengths are
  * returned in dim[], which must be of size MAXDIM.
+ *
+ * If we detect an error, fill *escontext with error details and return -1
+ * (unless escontext isn't provided, in which case errors will be thrown).
  */
 static int
-ArrayCount(const char *str, int *dim, char typdelim)
+ArrayCount(const char *str, int *dim, char typdelim, Node *escontext)
 {
        int                     nest_level = 0,
                                i;
@@ -488,11 +501,10 @@ ArrayCount(const char *str, int *dim, char typdelim)
                        {
                                case '\0':
                                        /* Signal a premature end of the string */
-                                       ereport(ERROR,
+                                       ereturn(escontext, -1,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed array literal: \"%s\"", str),
                                                         errdetail("Unexpected end of input.")));
-                                       break;
                                case '\\':
 
                                        /*
@@ -504,7 +516,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                parse_state != ARRAY_ELEM_STARTED &&
                                                parse_state != ARRAY_QUOTED_ELEM_STARTED &&
                                                parse_state != ARRAY_ELEM_DELIMITED)
-                                               ereport(ERROR,
+                                               ereturn(escontext, -1,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                 errmsg("malformed array literal: \"%s\"", str),
                                                                 errdetail("Unexpected \"%c\" character.",
@@ -515,7 +527,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                        if (*(ptr + 1))
                                                ptr++;
                                        else
-                                               ereport(ERROR,
+                                               ereturn(escontext, -1,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                 errmsg("malformed array literal: \"%s\"", str),
                                                                 errdetail("Unexpected end of input.")));
@@ -530,7 +542,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                        if (parse_state != ARRAY_LEVEL_STARTED &&
                                                parse_state != ARRAY_QUOTED_ELEM_STARTED &&
                                                parse_state != ARRAY_ELEM_DELIMITED)
-                                               ereport(ERROR,
+                                               ereturn(escontext, -1,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                 errmsg("malformed array literal: \"%s\"", str),
                                                                 errdetail("Unexpected array element.")));
@@ -551,14 +563,14 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                if (parse_state != ARRAY_NO_LEVEL &&
                                                        parse_state != ARRAY_LEVEL_STARTED &&
                                                        parse_state != ARRAY_LEVEL_DELIMITED)
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, -1,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"", str),
                                                                         errdetail("Unexpected \"%c\" character.",
                                                                                           '{')));
                                                parse_state = ARRAY_LEVEL_STARTED;
                                                if (nest_level >= MAXDIM)
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, -1,
                                                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                                                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                                                                                        nest_level + 1, MAXDIM)));
@@ -581,14 +593,14 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                        parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
                                                        parse_state != ARRAY_LEVEL_COMPLETED &&
                                                        !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, -1,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"", str),
                                                                         errdetail("Unexpected \"%c\" character.",
                                                                                           '}')));
                                                parse_state = ARRAY_LEVEL_COMPLETED;
                                                if (nest_level == 0)
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, -1,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"", str),
                                                                         errdetail("Unmatched \"%c\" character.", '}')));
@@ -596,7 +608,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
 
                                                if (nelems_last[nest_level] != 0 &&
                                                        nelems[nest_level] != nelems_last[nest_level])
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, -1,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"", str),
                                                                         errdetail("Multidimensional arrays must have "
@@ -630,7 +642,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                                parse_state != ARRAY_ELEM_COMPLETED &&
                                                                parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
                                                                parse_state != ARRAY_LEVEL_COMPLETED)
-                                                               ereport(ERROR,
+                                                               ereturn(escontext, -1,
                                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                                 errmsg("malformed array literal: \"%s\"", str),
                                                                                 errdetail("Unexpected \"%c\" character.",
@@ -653,7 +665,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                        if (parse_state != ARRAY_LEVEL_STARTED &&
                                                                parse_state != ARRAY_ELEM_STARTED &&
                                                                parse_state != ARRAY_ELEM_DELIMITED)
-                                                               ereport(ERROR,
+                                                               ereturn(escontext, -1,
                                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                                 errmsg("malformed array literal: \"%s\"", str),
                                                                                 errdetail("Unexpected array element.")));
@@ -673,7 +685,7 @@ ArrayCount(const char *str, int *dim, char typdelim)
        while (*ptr)
        {
                if (!array_isspace(*ptr++))
-                       ereport(ERROR,
+                       ereturn(escontext, -1,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"", str),
                                         errdetail("Junk after closing right brace.")));
@@ -713,11 +725,16 @@ ArrayCount(const char *str, int *dim, char typdelim)
  *     *hasnulls: set true iff there are any null elements.
  *     *nbytes: set to total size of data area needed (including alignment
  *             padding but not including array header overhead).
+ *     *escontext: if this points to an ErrorSaveContext, details of
+ *             any error are reported there.
+ *
+ * Result:
+ *     true for success, false for failure (if escontext is provided).
  *
  * Note that values[] and nulls[] are allocated by the caller, and must have
  * nitems elements.
  */
-static void
+static bool
 ReadArrayStr(char *arrayStr,
                         const char *origStr,
                         int nitems,
@@ -733,7 +750,8 @@ ReadArrayStr(char *arrayStr,
                         Datum *values,
                         bool *nulls,
                         bool *hasnulls,
-                        int32 *nbytes)
+                        int32 *nbytes,
+                        Node *escontext)
 {
        int                     i,
                                nest_level = 0;
@@ -784,7 +802,7 @@ ReadArrayStr(char *arrayStr,
                        {
                                case '\0':
                                        /* Signal a premature end of the string */
-                                       ereport(ERROR,
+                                       ereturn(escontext, false,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed array literal: \"%s\"",
                                                                        origStr)));
@@ -793,7 +811,7 @@ ReadArrayStr(char *arrayStr,
                                        /* Skip backslash, copy next character as-is. */
                                        srcptr++;
                                        if (*srcptr == '\0')
-                                               ereport(ERROR,
+                                               ereturn(escontext, false,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                 errmsg("malformed array literal: \"%s\"",
                                                                                origStr)));
@@ -823,7 +841,7 @@ ReadArrayStr(char *arrayStr,
                                        if (!in_quotes)
                                        {
                                                if (nest_level >= ndim)
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, false,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"",
                                                                                        origStr)));
@@ -838,7 +856,7 @@ ReadArrayStr(char *arrayStr,
                                        if (!in_quotes)
                                        {
                                                if (nest_level == 0)
-                                                       ereport(ERROR,
+                                                       ereturn(escontext, false,
                                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                         errmsg("malformed array literal: \"%s\"",
                                                                                        origStr)));
@@ -891,7 +909,7 @@ ReadArrayStr(char *arrayStr,
                *dstendptr = '\0';
 
                if (i < 0 || i >= nitems)
-                       ereport(ERROR,
+                       ereturn(escontext, false,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("malformed array literal: \"%s\"",
                                                        origStr)));
@@ -900,14 +918,20 @@ ReadArrayStr(char *arrayStr,
                        pg_strcasecmp(itemstart, "NULL") == 0)
                {
                        /* it's a NULL item */
-                       values[i] = InputFunctionCall(inputproc, NULL,
-                                                                                 typioparam, typmod);
+                       if (!InputFunctionCallSafe(inputproc, NULL,
+                                                                          typioparam, typmod,
+                                                                          escontext,
+                                                                          &values[i]))
+                               return false;
                        nulls[i] = true;
                }
                else
                {
-                       values[i] = InputFunctionCall(inputproc, itemstart,
-                                                                                 typioparam, typmod);
+                       if (!InputFunctionCallSafe(inputproc, itemstart,
+                                                                          typioparam, typmod,
+                                                                          escontext,
+                                                                          &values[i]))
+                               return false;
                        nulls[i] = false;
                }
        }
@@ -930,7 +954,7 @@ ReadArrayStr(char *arrayStr,
                        totbytes = att_align_nominal(totbytes, typalign);
                        /* check for overflow of total request */
                        if (!AllocSizeIsValid(totbytes))
-                               ereport(ERROR,
+                               ereturn(escontext, false,
                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                                 errmsg("array size exceeds the maximum allowed (%d)",
                                                                (int) MaxAllocSize)));
@@ -938,6 +962,7 @@ ReadArrayStr(char *arrayStr,
        }
        *hasnulls = hasnull;
        *nbytes = totbytes;
+       return true;
 }
 
 
index 051169a149a32fbb0c7bf833140d7b2121f0c1c4..3821f6637b6a8882621fb082208ed31a0c55b496 100644 (file)
@@ -74,6 +74,16 @@ ArrayGetOffset0(int n, const int *tup, const int *scale)
  */
 int
 ArrayGetNItems(int ndim, const int *dims)
+{
+       return ArrayGetNItemsSafe(ndim, dims, NULL);
+}
+
+/*
+ * This entry point can return the error into an ErrorSaveContext
+ * instead of throwing an exception.  -1 is returned after an error.
+ */
+int
+ArrayGetNItemsSafe(int ndim, const int *dims, struct Node *escontext)
 {
        int32           ret;
        int                     i;
@@ -89,7 +99,7 @@ ArrayGetNItems(int ndim, const int *dims)
 
                /* A negative dimension implies that UB-LB overflowed ... */
                if (dims[i] < 0)
-                       ereport(ERROR,
+                       ereturn(escontext, -1,
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                         errmsg("array size exceeds the maximum allowed (%d)",
                                                        (int) MaxArraySize)));
@@ -98,14 +108,14 @@ ArrayGetNItems(int ndim, const int *dims)
 
                ret = (int32) prod;
                if ((int64) ret != prod)
-                       ereport(ERROR,
+                       ereturn(escontext, -1,
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                         errmsg("array size exceeds the maximum allowed (%d)",
                                                        (int) MaxArraySize)));
        }
        Assert(ret >= 0);
        if ((Size) ret > MaxArraySize)
-               ereport(ERROR,
+               ereturn(escontext, -1,
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                 errmsg("array size exceeds the maximum allowed (%d)",
                                                (int) MaxArraySize)));
@@ -126,6 +136,17 @@ ArrayGetNItems(int ndim, const int *dims)
  */
 void
 ArrayCheckBounds(int ndim, const int *dims, const int *lb)
+{
+       (void) ArrayCheckBoundsSafe(ndim, dims, lb, NULL);
+}
+
+/*
+ * This entry point can return the error into an ErrorSaveContext
+ * instead of throwing an exception.
+ */
+bool
+ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb,
+                                        struct Node *escontext)
 {
        int                     i;
 
@@ -135,11 +156,13 @@ ArrayCheckBounds(int ndim, const int *dims, const int *lb)
                int32           sum PG_USED_FOR_ASSERTS_ONLY;
 
                if (pg_add_s32_overflow(dims[i], lb[i], &sum))
-                       ereport(ERROR,
+                       ereturn(escontext, false,
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                         errmsg("array lower bound is too large: %d",
                                                        lb[i])));
        }
+
+       return true;
 }
 
 /*
index cd7335287f94007765cb2308b0cf6715a64c44fe..e291672ae47524507e2a0716a4137371e720cc07 100644 (file)
@@ -148,13 +148,10 @@ boolin(PG_FUNCTION_ARGS)
        if (parse_bool_with_len(str, len, &result))
                PG_RETURN_BOOL(result);
 
-       ereport(ERROR,
+       ereturn(fcinfo->context, (Datum) 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type %s: \"%s\"",
                                        "boolean", in_str)));
-
-       /* not reached */
-       PG_RETURN_BOOL(false);
 }
 
 /*
index da97538ebe304507aaba3c31387d673b661d2619..b02a19be24da13ccf512aad020e5849f84e67054 100644 (file)
@@ -163,6 +163,7 @@ Datum
 float4in(PG_FUNCTION_ARGS)
 {
        char       *num = PG_GETARG_CSTRING(0);
+       Node       *escontext = fcinfo->context;
        char       *orig_num;
        float           val;
        char       *endptr;
@@ -183,7 +184,7 @@ float4in(PG_FUNCTION_ARGS)
         * strtod() on different platforms.
         */
        if (*num == '\0')
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                "real", orig_num)));
@@ -257,13 +258,13 @@ float4in(PG_FUNCTION_ARGS)
                                (val >= HUGE_VALF || val <= -HUGE_VALF)
 #endif
                                )
-                               ereport(ERROR,
+                               ereturn(escontext, (Datum) 0,
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                 errmsg("\"%s\" is out of range for type real",
                                                                orig_num)));
                }
                else
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for type %s: \"%s\"",
                                                        "real", orig_num)));
@@ -275,7 +276,7 @@ float4in(PG_FUNCTION_ARGS)
 
        /* if there is any junk left at the end of the string, bail out */
        if (*endptr != '\0')
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                "real", orig_num)));
@@ -337,52 +338,40 @@ float8in(PG_FUNCTION_ARGS)
 {
        char       *num = PG_GETARG_CSTRING(0);
 
-       PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
+       PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num,
+                                                                          fcinfo->context));
 }
 
-/* Convenience macro: set *have_error flag (if provided) or throw error */
-#define RETURN_ERROR(throw_error, have_error) \
-do { \
-       if (have_error) { \
-               *have_error = true; \
-               return 0.0; \
-       } else { \
-               throw_error; \
-       } \
-} while (0)
-
 /*
- * float8in_internal_opt_error - guts of float8in()
+ * float8in_internal - guts of float8in()
  *
  * This is exposed for use by functions that want a reasonably
  * platform-independent way of inputting doubles.  The behavior is
- * essentially like strtod + ereport on error, but note the following
+ * essentially like strtod + ereturn on error, but note the following
  * differences:
  * 1. Both leading and trailing whitespace are skipped.
- * 2. If endptr_p is NULL, we throw error if there's trailing junk.
+ * 2. If endptr_p is NULL, we report error if there's trailing junk.
  * Otherwise, it's up to the caller to complain about trailing junk.
  * 3. In event of a syntax error, the report mentions the given type_name
  * and prints orig_string as the input; this is meant to support use of
  * this function with types such as "box" and "point", where what we are
  * parsing here is just a substring of orig_string.
  *
+ * If escontext points to an ErrorSaveContext node, that is filled instead
+ * of throwing an error; the caller must check SOFT_ERROR_OCCURRED()
+ * to detect errors.
+ *
  * "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_opt_error(char *num, char **endptr_p,
-                                                       const char *type_name, const char *orig_string,
-                                                       bool *have_error)
+float8
+float8in_internal(char *num, char **endptr_p,
+                                 const char *type_name, const char *orig_string,
+                                 struct Node *escontext)
 {
        double          val;
        char       *endptr;
 
-       if (have_error)
-               *have_error = false;
-
        /* skip leading whitespace */
        while (*num != '\0' && isspace((unsigned char) *num))
                num++;
@@ -392,11 +381,10 @@ float8in_internal_opt_error(char *num, char **endptr_p,
         * strtod() on different platforms.
         */
        if (*num == '\0')
-               RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                         errmsg("invalid input syntax for type %s: \"%s\"",
-                                                                        type_name, orig_string))),
-                                        have_error);
+               ereturn(escontext, 0,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for type %s: \"%s\"",
+                                               type_name, orig_string)));
 
        errno = 0;
        val = strtod(num, &endptr);
@@ -469,20 +457,17 @@ float8in_internal_opt_error(char *num, char **endptr_p,
                                char       *errnumber = pstrdup(num);
 
                                errnumber[endptr - num] = '\0';
-                               RETURN_ERROR(ereport(ERROR,
-                                                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                                                                         errmsg("\"%s\" is out of range for type double precision",
-                                                                                        errnumber))),
-                                                        have_error);
+                               ereturn(escontext, 0,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                                errmsg("\"%s\" is out of range for type double precision",
+                                                               errnumber)));
                        }
                }
                else
-                       RETURN_ERROR(ereport(ERROR,
-                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                                 errmsg("invalid input syntax for type "
-                                                                                "%s: \"%s\"",
-                                                                                type_name, orig_string))),
-                                                have_error);
+                       ereturn(escontext, 0,
+                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                        errmsg("invalid input syntax for type %s: \"%s\"",
+                                                       type_name, orig_string)));
        }
 
        /* skip trailing whitespace */
@@ -493,27 +478,14 @@ float8in_internal_opt_error(char *num, char **endptr_p,
        if (endptr_p)
                *endptr_p = endptr;
        else if (*endptr != '\0')
-               RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                         errmsg("invalid input syntax for type "
-                                                                        "%s: \"%s\"",
-                                                                        type_name, orig_string))),
-                                        have_error);
+               ereturn(escontext, 0,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("invalid input syntax for type %s: \"%s\"",
+                                               type_name, orig_string)));
 
        return val;
 }
 
-/*
- * Interface 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
index d78002b901d806fd012e3a1bad0272e217cd6c32..721ce6634f85f13204c49131720bc3a4720d57a8 100644 (file)
@@ -189,7 +189,7 @@ static float8
 single_decode(char *num, char **endptr_p,
                          const char *type_name, const char *orig_string)
 {
-       return float8in_internal(num, endptr_p, type_name, orig_string);
+       return float8in_internal(num, endptr_p, type_name, orig_string, NULL);
 }                                                              /* single_decode() */
 
 static void
@@ -212,7 +212,7 @@ pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
        if ((has_delim = (*str == LDELIM)))
                str++;
 
-       *x = float8in_internal(str, &str, type_name, orig_string);
+       *x = float8in_internal(str, &str, type_name, orig_string, NULL);
 
        if (*str++ != DELIM)
                ereport(ERROR,
@@ -220,7 +220,7 @@ pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                type_name, orig_string)));
 
-       *y = float8in_internal(str, &str, type_name, orig_string);
+       *y = float8in_internal(str, &str, type_name, orig_string, NULL);
 
        if (has_delim)
        {
index 42ddae99ef45a30fe36dba97ca478cfdb4bb5817..8de38abd11dc0244007c80eb6ee139bd0b6021e7 100644 (file)
@@ -64,7 +64,7 @@ int2in(PG_FUNCTION_ARGS)
 {
        char       *num = PG_GETARG_CSTRING(0);
 
-       PG_RETURN_INT16(pg_strtoint16(num));
+       PG_RETURN_INT16(pg_strtoint16_safe(num, fcinfo->context));
 }
 
 /*
@@ -291,7 +291,7 @@ int4in(PG_FUNCTION_ARGS)
 {
        char       *num = PG_GETARG_CSTRING(0);
 
-       PG_RETURN_INT32(pg_strtoint32(num));
+       PG_RETURN_INT32(pg_strtoint32_safe(num, fcinfo->context));
 }
 
 /*
index 98d432375566bf2a630432af6ddf3176c0727c2a..7d1767ce0f144d53e94840336f8d967fa1680531 100644 (file)
@@ -52,7 +52,7 @@ int8in(PG_FUNCTION_ARGS)
 {
        char       *num = PG_GETARG_CSTRING(0);
 
-       PG_RETURN_INT64(pg_strtoint64(num));
+       PG_RETURN_INT64(pg_strtoint64_safe(num, fcinfo->context));
 }
 
 
index 8d83b2edb357be4cb353abf4996505663073e38c..930bd265842470c75611e2b50a07e68ea1c64d31 100644 (file)
@@ -64,6 +64,7 @@
 #include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "nodes/miscnodes.h"
 #include "regex/regex.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
@@ -1041,15 +1042,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                        char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
                                                                                                                                                  NumericGetDatum(jb->val.numeric)));
                                        double          val;
-                                       bool            have_error = false;
+                                       ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-                                       val = float8in_internal_opt_error(tmp,
-                                                                                                         NULL,
-                                                                                                         "double precision",
-                                                                                                         tmp,
-                                                                                                         &have_error);
+                                       val = float8in_internal(tmp,
+                                                                                       NULL,
+                                                                                       "double precision",
+                                                                                       tmp,
+                                                                                       (Node *) &escontext);
 
-                                       if (have_error || isinf(val) || isnan(val))
+                                       if (escontext.error_occurred || isinf(val) || isnan(val))
                                                RETURN_ERROR(ereport(ERROR,
                                                                                         (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
                                                                                          errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
@@ -1062,15 +1063,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                        double          val;
                                        char       *tmp = pnstrdup(jb->val.string.val,
                                                                                           jb->val.string.len);
-                                       bool            have_error = false;
+                                       ErrorSaveContext escontext = {T_ErrorSaveContext};
 
-                                       val = float8in_internal_opt_error(tmp,
-                                                                                                         NULL,
-                                                                                                         "double precision",
-                                                                                                         tmp,
-                                                                                                         &have_error);
+                                       val = float8in_internal(tmp,
+                                                                                       NULL,
+                                                                                       "double precision",
+                                                                                       tmp,
+                                                                                       (Node *) &escontext);
 
-                                       if (have_error || isinf(val) || isnan(val))
+                                       if (escontext.error_occurred || isinf(val) || isnan(val))
                                                RETURN_ERROR(ereport(ERROR,
                                                                                         (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
                                                                                          errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
index 7f0e93aa803dda8a298391b81d3d226f49bdc9b0..c024928bc8d6039bd2efd010efd08e6729490665 100644 (file)
@@ -497,8 +497,9 @@ static void alloc_var(NumericVar *var, int ndigits);
 static void free_var(NumericVar *var);
 static void zero_var(NumericVar *var);
 
-static const char *set_var_from_str(const char *str, const char *cp,
-                                                                       NumericVar *dest);
+static bool set_var_from_str(const char *str, const char *cp,
+                                                        NumericVar *dest, const char **endptr,
+                                                        Node *escontext);
 static void set_var_from_num(Numeric num, NumericVar *dest);
 static void init_var_from_num(Numeric num, NumericVar *dest);
 static void set_var_from_var(const NumericVar *value, NumericVar *dest);
@@ -512,8 +513,8 @@ static Numeric duplicate_numeric(Numeric num);
 static Numeric make_result(const NumericVar *var);
 static Numeric make_result_opt_error(const NumericVar *var, bool *have_error);
 
-static void apply_typmod(NumericVar *var, int32 typmod);
-static void apply_typmod_special(Numeric num, int32 typmod);
+static bool apply_typmod(NumericVar *var, int32 typmod, Node *escontext);
+static bool apply_typmod_special(Numeric num, int32 typmod, Node *escontext);
 
 static bool numericvar_to_int32(const NumericVar *var, int32 *result);
 static bool numericvar_to_int64(const NumericVar *var, int64 *result);
@@ -617,11 +618,11 @@ Datum
 numeric_in(PG_FUNCTION_ARGS)
 {
        char       *str = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
        Oid                     typelem = PG_GETARG_OID(1);
 #endif
        int32           typmod = PG_GETARG_INT32(2);
+       Node       *escontext = fcinfo->context;
        Numeric         res;
        const char *cp;
 
@@ -679,10 +680,12 @@ numeric_in(PG_FUNCTION_ARGS)
                 * Use set_var_from_str() to parse a normal numeric value
                 */
                NumericVar      value;
+               bool            have_error;
 
                init_var(&value);
 
-               cp = set_var_from_str(str, cp, &value);
+               if (!set_var_from_str(str, cp, &value, &cp, escontext))
+                       PG_RETURN_NULL();
 
                /*
                 * We duplicate a few lines of code here because we would like to
@@ -693,16 +696,23 @@ numeric_in(PG_FUNCTION_ARGS)
                while (*cp)
                {
                        if (!isspace((unsigned char) *cp))
-                               ereport(ERROR,
+                               ereturn(escontext, (Datum) 0,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                                "numeric", str)));
                        cp++;
                }
 
-               apply_typmod(&value, typmod);
+               if (!apply_typmod(&value, typmod, escontext))
+                       PG_RETURN_NULL();
+
+               res = make_result_opt_error(&value, &have_error);
+
+               if (have_error)
+                       ereturn(escontext, (Datum) 0,
+                                       (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                        errmsg("value overflows numeric format")));
 
-               res = make_result(&value);
                free_var(&value);
 
                PG_RETURN_NUMERIC(res);
@@ -712,7 +722,7 @@ numeric_in(PG_FUNCTION_ARGS)
        while (*cp)
        {
                if (!isspace((unsigned char) *cp))
-                       ereport(ERROR,
+                       ereturn(escontext, (Datum) 0,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for type %s: \"%s\"",
                                                        "numeric", str)));
@@ -720,7 +730,8 @@ numeric_in(PG_FUNCTION_ARGS)
        }
 
        /* As above, throw any typmod error after finishing syntax check */
-       apply_typmod_special(res, typmod);
+       if (!apply_typmod_special(res, typmod, escontext))
+               PG_RETURN_NULL();
 
        PG_RETURN_NUMERIC(res);
 }
@@ -1058,7 +1069,7 @@ numeric_recv(PG_FUNCTION_ARGS)
        {
                trunc_var(&value, value.dscale);
 
-               apply_typmod(&value, typmod);
+               (void) apply_typmod(&value, typmod, NULL);
 
                res = make_result(&value);
        }
@@ -1067,7 +1078,7 @@ numeric_recv(PG_FUNCTION_ARGS)
                /* apply_typmod_special wants us to make the Numeric first */
                res = make_result(&value);
 
-               apply_typmod_special(res, typmod);
+               (void) apply_typmod_special(res, typmod, NULL);
        }
 
        free_var(&value);
@@ -1180,7 +1191,7 @@ numeric           (PG_FUNCTION_ARGS)
         */
        if (NUMERIC_IS_SPECIAL(num))
        {
-               apply_typmod_special(num, typmod);
+               (void) apply_typmod_special(num, typmod, NULL);
                PG_RETURN_NUMERIC(duplicate_numeric(num));
        }
 
@@ -1231,7 +1242,7 @@ numeric           (PG_FUNCTION_ARGS)
        init_var(&var);
 
        set_var_from_num(num, &var);
-       apply_typmod(&var, typmod);
+       (void) apply_typmod(&var, typmod, NULL);
        new = make_result(&var);
 
        free_var(&var);
@@ -4395,6 +4406,7 @@ float8_numeric(PG_FUNCTION_ARGS)
        Numeric         res;
        NumericVar      result;
        char            buf[DBL_DIG + 100];
+       const char *endptr;
 
        if (isnan(val))
                PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4412,7 +4424,7 @@ float8_numeric(PG_FUNCTION_ARGS)
        init_var(&result);
 
        /* Assume we need not worry about leading/trailing spaces */
-       (void) set_var_from_str(buf, buf, &result);
+       (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
 
        res = make_result(&result);
 
@@ -4488,6 +4500,7 @@ float4_numeric(PG_FUNCTION_ARGS)
        Numeric         res;
        NumericVar      result;
        char            buf[FLT_DIG + 100];
+       const char *endptr;
 
        if (isnan(val))
                PG_RETURN_NUMERIC(make_result(&const_nan));
@@ -4505,7 +4518,7 @@ float4_numeric(PG_FUNCTION_ARGS)
        init_var(&result);
 
        /* Assume we need not worry about leading/trailing spaces */
-       (void) set_var_from_str(buf, buf, &result);
+       (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
 
        res = make_result(&result);
 
@@ -6804,14 +6817,19 @@ zero_var(NumericVar *var)
  *     Parse a string and put the number into a variable
  *
  * This function does not handle leading or trailing spaces.  It returns
- * the end+1 position parsed, so that caller can check for trailing
- * spaces/garbage if deemed necessary.
+ * the end+1 position parsed into *endptr, so that caller can check for
+ * trailing spaces/garbage if deemed necessary.
  *
  * cp is the place to actually start parsing; str is what to use in error
  * reports.  (Typically cp would be the same except advanced over spaces.)
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static const char *
-set_var_from_str(const char *str, const char *cp, NumericVar *dest)
+static bool
+set_var_from_str(const char *str, const char *cp,
+                                NumericVar *dest, const char **endptr,
+                                Node *escontext)
 {
        bool            have_dp = false;
        int                     i;
@@ -6849,7 +6867,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
        }
 
        if (!isdigit((unsigned char) *cp))
-               ereport(ERROR,
+               ereturn(escontext, false,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                "numeric", str)));
@@ -6873,7 +6891,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
                else if (*cp == '.')
                {
                        if (have_dp)
-                               ereport(ERROR,
+                               ereturn(escontext, false,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("invalid input syntax for type %s: \"%s\"",
                                                                "numeric", str)));
@@ -6897,7 +6915,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
                cp++;
                exponent = strtol(cp, &endptr, 10);
                if (endptr == cp)
-                       ereport(ERROR,
+                       ereturn(escontext, false,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                         errmsg("invalid input syntax for type %s: \"%s\"",
                                                        "numeric", str)));
@@ -6912,7 +6930,7 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
                 * for consistency use the same ereport errcode/text as make_result().
                 */
                if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
-                       ereport(ERROR,
+                       ereturn(escontext, false,
                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                         errmsg("value overflows numeric format")));
                dweight += (int) exponent;
@@ -6963,7 +6981,9 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
        strip_var(dest);
 
        /* Return end+1 position for caller */
-       return cp;
+       *endptr = cp;
+
+       return true;
 }
 
 
@@ -7455,9 +7475,12 @@ make_result(const NumericVar *var)
  *
  *     Do bounds checking and rounding according to the specified typmod.
  *     Note that this is only applied to normal finite values.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static void
-apply_typmod(NumericVar *var, int32 typmod)
+static bool
+apply_typmod(NumericVar *var, int32 typmod, Node *escontext)
 {
        int                     precision;
        int                     scale;
@@ -7467,7 +7490,7 @@ apply_typmod(NumericVar *var, int32 typmod)
 
        /* Do nothing if we have an invalid typmod */
        if (!is_valid_numeric_typmod(typmod))
-               return;
+               return true;
 
        precision = numeric_typmod_precision(typmod);
        scale = numeric_typmod_scale(typmod);
@@ -7514,7 +7537,7 @@ apply_typmod(NumericVar *var, int32 typmod)
 #error unsupported NBASE
 #endif
                                if (ddigits > maxdigits)
-                                       ereport(ERROR,
+                                       ereturn(escontext, false,
                                                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                                         errmsg("numeric field overflow"),
                                                         errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.",
@@ -7528,6 +7551,8 @@ apply_typmod(NumericVar *var, int32 typmod)
                        ddigits -= DEC_DIGITS;
                }
        }
+
+       return true;
 }
 
 /*
@@ -7535,9 +7560,12 @@ apply_typmod(NumericVar *var, int32 typmod)
  *
  *     Do bounds checking according to the specified typmod, for an Inf or NaN.
  *     For convenience of most callers, the value is presented in packed form.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
  */
-static void
-apply_typmod_special(Numeric num, int32 typmod)
+static bool
+apply_typmod_special(Numeric num, int32 typmod, Node *escontext)
 {
        int                     precision;
        int                     scale;
@@ -7551,16 +7579,16 @@ apply_typmod_special(Numeric num, int32 typmod)
         * any finite number of digits.
         */
        if (NUMERIC_IS_NAN(num))
-               return;
+               return true;
 
        /* Do nothing if we have a default typmod (-1) */
        if (!is_valid_numeric_typmod(typmod))
-               return;
+               return true;
 
        precision = numeric_typmod_precision(typmod);
        scale = numeric_typmod_scale(typmod);
 
-       ereport(ERROR,
+       ereturn(escontext, false,
                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                         errmsg("numeric field overflow"),
                         errdetail("A field with precision %d, scale %d cannot hold an infinite value.",
index a64422c8d066a330c3ec33ec1e2d6d4642b5cdb1..ab1564f22dabf368f64d803dd3655ae16df3f589 100644 (file)
@@ -88,15 +88,24 @@ decimalLength64(const uint64 v)
 /*
  * Convert input string to a signed 16 bit integer.
  *
- * Allows any number of leading or trailing whitespace characters. Will throw
- * ereport() upon bad input format or overflow.
+ * Allows any number of leading or trailing whitespace characters.
  *
+ * pg_strtoint16() will throw ereport() upon bad input format or overflow;
+ * while pg_strtoint16_safe() instead returns such complaints in *escontext,
+ * if it's an ErrorSaveContext.
+*
  * NB: Accumulate input as an unsigned number, to deal with two's complement
  * representation of the most negative number, which can't be represented as a
  * signed positive number.
  */
 int16
 pg_strtoint16(const char *s)
+{
+       return pg_strtoint16_safe(s, NULL);
+}
+
+int16
+pg_strtoint16_safe(const char *s, Node *escontext)
 {
        const char *ptr = s;
        uint16          tmp = 0;
@@ -149,25 +158,26 @@ pg_strtoint16(const char *s)
        return (int16) tmp;
 
 out_of_range:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                         errmsg("value \"%s\" is out of range for type %s",
                                        s, "smallint")));
 
 invalid_syntax:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type %s: \"%s\"",
                                        "smallint", s)));
-
-       return 0;                                       /* keep compiler quiet */
 }
 
 /*
  * Convert input string to a signed 32 bit integer.
  *
- * Allows any number of leading or trailing whitespace characters. Will throw
- * ereport() upon bad input format or overflow.
+ * Allows any number of leading or trailing whitespace characters.
+ *
+ * pg_strtoint32() will throw ereport() upon bad input format or overflow;
+ * while pg_strtoint32_safe() instead returns such complaints in *escontext,
+ * if it's an ErrorSaveContext.
  *
  * NB: Accumulate input as an unsigned number, to deal with two's complement
  * representation of the most negative number, which can't be represented as a
@@ -175,6 +185,12 @@ invalid_syntax:
  */
 int32
 pg_strtoint32(const char *s)
+{
+       return pg_strtoint32_safe(s, NULL);
+}
+
+int32
+pg_strtoint32_safe(const char *s, Node *escontext)
 {
        const char *ptr = s;
        uint32          tmp = 0;
@@ -227,25 +243,26 @@ pg_strtoint32(const char *s)
        return (int32) tmp;
 
 out_of_range:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                         errmsg("value \"%s\" is out of range for type %s",
                                        s, "integer")));
 
 invalid_syntax:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type %s: \"%s\"",
                                        "integer", s)));
-
-       return 0;                                       /* keep compiler quiet */
 }
 
 /*
  * Convert input string to a signed 64 bit integer.
  *
- * Allows any number of leading or trailing whitespace characters. Will throw
- * ereport() upon bad input format or overflow.
+ * Allows any number of leading or trailing whitespace characters.
+ *
+ * pg_strtoint64() will throw ereport() upon bad input format or overflow;
+ * while pg_strtoint64_safe() instead returns such complaints in *escontext,
+ * if it's an ErrorSaveContext.
  *
  * NB: Accumulate input as an unsigned number, to deal with two's complement
  * representation of the most negative number, which can't be represented as a
@@ -253,6 +270,12 @@ invalid_syntax:
  */
 int64
 pg_strtoint64(const char *s)
+{
+       return pg_strtoint64_safe(s, NULL);
+}
+
+int64
+pg_strtoint64_safe(const char *s, Node *escontext)
 {
        const char *ptr = s;
        uint64          tmp = 0;
@@ -305,18 +328,16 @@ pg_strtoint64(const char *s)
        return (int64) tmp;
 
 out_of_range:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                         errmsg("value \"%s\" is out of range for type %s",
                                        s, "bigint")));
 
 invalid_syntax:
-       ereport(ERROR,
+       ereturn(escontext, 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("invalid input syntax for type %s: \"%s\"",
                                        "bigint", s)));
-
-       return 0;                                       /* keep compiler quiet */
 }
 
 /*
index db843a0fbf074937d5a2bfae8918bfc34c197f47..bdafcff02de48ec14473b77270bc27e8213c90e3 100644 (file)
@@ -77,6 +77,7 @@ record_in(PG_FUNCTION_ARGS)
        char       *string = PG_GETARG_CSTRING(0);
        Oid                     tupType = PG_GETARG_OID(1);
        int32           tupTypmod = PG_GETARG_INT32(2);
+       Node       *escontext = fcinfo->context;
        HeapTupleHeader result;
        TupleDesc       tupdesc;
        HeapTuple       tuple;
@@ -100,7 +101,7 @@ record_in(PG_FUNCTION_ARGS)
         * supply a valid typmod, and then we can do something useful for RECORD.
         */
        if (tupType == RECORDOID && tupTypmod < 0)
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("input of anonymous composite types is not implemented")));
 
@@ -152,10 +153,13 @@ record_in(PG_FUNCTION_ARGS)
        while (*ptr && isspace((unsigned char) *ptr))
                ptr++;
        if (*ptr++ != '(')
-               ereport(ERROR,
+       {
+               errsave(escontext,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed record literal: \"%s\"", string),
                                 errdetail("Missing left parenthesis.")));
+               goto fail;
+       }
 
        initStringInfo(&buf);
 
@@ -181,10 +185,13 @@ record_in(PG_FUNCTION_ARGS)
                                ptr++;
                        else
                                /* *ptr must be ')' */
-                               ereport(ERROR,
+                       {
+                               errsave(escontext,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                 errmsg("malformed record literal: \"%s\"", string),
                                                 errdetail("Too few columns.")));
+                               goto fail;
+                       }
                }
 
                /* Check for null: completely empty input means null */
@@ -204,19 +211,25 @@ record_in(PG_FUNCTION_ARGS)
                                char            ch = *ptr++;
 
                                if (ch == '\0')
-                                       ereport(ERROR,
+                               {
+                                       errsave(escontext,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                         errmsg("malformed record literal: \"%s\"",
                                                                        string),
                                                         errdetail("Unexpected end of input.")));
+                                       goto fail;
+                               }
                                if (ch == '\\')
                                {
                                        if (*ptr == '\0')
-                                               ereport(ERROR,
+                                       {
+                                               errsave(escontext,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                                                 errmsg("malformed record literal: \"%s\"",
                                                                                string),
                                                                 errdetail("Unexpected end of input.")));
+                                               goto fail;
+                                       }
                                        appendStringInfoChar(&buf, *ptr++);
                                }
                                else if (ch == '"')
@@ -252,10 +265,13 @@ record_in(PG_FUNCTION_ARGS)
                        column_info->column_type = column_type;
                }
 
-               values[i] = InputFunctionCall(&column_info->proc,
-                                                                         column_data,
-                                                                         column_info->typioparam,
-                                                                         att->atttypmod);
+               if (!InputFunctionCallSafe(&column_info->proc,
+                                                                  column_data,
+                                                                  column_info->typioparam,
+                                                                  att->atttypmod,
+                                                                  escontext,
+                                                                  &values[i]))
+                       goto fail;
 
                /*
                 * Prep for next column
@@ -264,18 +280,24 @@ record_in(PG_FUNCTION_ARGS)
        }
 
        if (*ptr++ != ')')
-               ereport(ERROR,
+       {
+               errsave(escontext,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed record literal: \"%s\"", string),
                                 errdetail("Too many columns.")));
+               goto fail;
+       }
        /* Allow trailing whitespace */
        while (*ptr && isspace((unsigned char) *ptr))
                ptr++;
        if (*ptr)
-               ereport(ERROR,
+       {
+               errsave(escontext,
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                 errmsg("malformed record literal: \"%s\"", string),
                                 errdetail("Junk after right parenthesis.")));
+               goto fail;
+       }
 
        tuple = heap_form_tuple(tupdesc, values, nulls);
 
@@ -294,6 +316,11 @@ record_in(PG_FUNCTION_ARGS)
        ReleaseTupleDesc(tupdesc);
 
        PG_RETURN_HEAPTUPLEHEADER(result);
+
+       /* exit here once we've done lookup_rowtype_tupdesc */
+fail:
+       ReleaseTupleDesc(tupdesc);
+       PG_RETURN_NULL();
 }
 
 /*
index 2f794d116826220348419981a65b4aa8233c7616..3f6319aed5a9f3b61164e687fc6ae2ec2c3dae42 100644 (file)
@@ -447,7 +447,11 @@ extern void array_free_iterator(ArrayIterator iterator);
 extern int     ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
 extern int     ArrayGetOffset0(int n, const int *tup, const int *scale);
 extern int     ArrayGetNItems(int ndim, const int *dims);
+extern int     ArrayGetNItemsSafe(int ndim, const int *dims,
+                                                          struct Node *escontext);
 extern void ArrayCheckBounds(int ndim, const int *dims, const int *lb);
+extern bool ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb,
+                                                                struct Node *escontext);
 extern void mda_get_range(int n, int *span, const int *st, const int *endp);
 extern void mda_get_prod(int n, const int *range, int *prod);
 extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);
index 81631f16454889b98c18280e6b6e9b1fa45e9eae..10d13b0f1e97e24f4964c0a927f85caf0faa53c4 100644 (file)
@@ -44,8 +44,11 @@ extern int   namestrcmp(Name name, const char *str);
 
 /* numutils.c */
 extern int16 pg_strtoint16(const char *s);
+extern int16 pg_strtoint16_safe(const char *s, Node *escontext);
 extern int32 pg_strtoint32(const char *s);
+extern int32 pg_strtoint32_safe(const char *s, Node *escontext);
 extern int64 pg_strtoint64(const char *s);
+extern int64 pg_strtoint64_safe(const char *s, Node *escontext);
 extern int     pg_itoa(int16 i, char *a);
 extern int     pg_ultoa_n(uint32 value, char *a);
 extern int     pg_ulltoa_n(uint64 value, char *a);
index 4bf0e3ac07afe94b0a6eb34a3e600141f5b84d6c..f92860b4a4601e5bb0d33dc4e6a06ba525cd61bb 100644 (file)
@@ -42,10 +42,8 @@ extern void float_underflow_error(void) pg_attribute_noreturn();
 extern void float_zero_divide_error(void) pg_attribute_noreturn();
 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);
+                                                               const char *type_name, const char *orig_string,
+                                                               struct Node *escontext);
 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 97920f38c2113cafa07d37427cd2ec2a6a441929..a2f9d7ed16960397e41434212bba79ffb422edf8 100644 (file)
@@ -182,6 +182,31 @@ SELECT a,b,c FROM arrtest;
  [4:4]={NULL}  | {3,4}                 | {foo,new_word}
 (3 rows)
 
+-- test non-error-throwing API
+SELECT pg_input_is_valid('{1,2,3}', 'integer[]');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('{1,2', 'integer[]');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('{1,zed}', 'integer[]');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('{1,zed}', 'integer[]');
+            pg_input_error_message            
+----------------------------------------------
+ invalid input syntax for type integer: "zed"
+(1 row)
+
 -- test mixed slice/scalar subscripting
 select '{{1,2,3},{4,5,6},{7,8,9}}'::int[];
            int4            
index 4728fe2dfdf4af676c1b9b59f6a3f7b212dd3d7f..977124b20b1f3f4685f03c2a88b83425c98a7202 100644 (file)
@@ -142,6 +142,25 @@ SELECT bool '' AS error;
 ERROR:  invalid input syntax for type boolean: ""
 LINE 1: SELECT bool '' AS error;
                     ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('true', 'bool');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'bool');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('junk', 'bool');
+            pg_input_error_message             
+-----------------------------------------------
+ invalid input syntax for type boolean: "junk"
+(1 row)
+
 -- and, or, not in qualifications
 SELECT bool 't' or bool 'f' AS true;
  true 
index 3d5d298b73a023d005635b2685d7923682f2cb07..24fde6cc9f103456b601819d68b41c920a1bd92c 100644 (file)
@@ -81,6 +81,31 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 ERROR:  invalid input syntax for type real: "123            5"
 LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400', 'float4');
+        pg_input_error_message         
+---------------------------------------
+ "1e400" is out of range for type real
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float4;
  float4 
index 6ad5d00aa2d6f417235385919ee42cb5ce1840eb..1d7090a90dcb4cbd3d568f73439cd0f852a11df6 100644 (file)
@@ -81,6 +81,31 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 ERROR:  invalid input syntax for type real: "123            5"
 LINE 1: INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400', 'float4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400', 'float4');
+        pg_input_error_message         
+---------------------------------------
+ "1e400" is out of range for type real
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float4;
  float4 
index de4d57ec9ff724b9be1bc95208cfc3b544a509f8..2b25784f7f6d5f65e3fba9bad3d03235fac00a6e 100644 (file)
@@ -68,6 +68,31 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
 ERROR:  invalid input syntax for type double precision: "123           5"
 LINE 1: INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
                                            ^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float8');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('xyz', 'float8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e4000', 'float8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e4000', 'float8');
+               pg_input_error_message               
+----------------------------------------------------
+ "1e4000" is out of range for type double precision
+(1 row)
+
 -- special inputs
 SELECT 'NaN'::float8;
  float8 
index 109cf9baaacaf473f9e3b2cf264108325472cc3c..6a23567b6796365f78e00fb8e7da7db517b2a622 100644 (file)
@@ -45,6 +45,31 @@ SELECT * FROM INT2_TBL;
  -32767
 (5 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int2');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'int2');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('50000', 'int2');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('50000', 'int2');
+             pg_input_error_message              
+-------------------------------------------------
+ value "50000" is out of range for type smallint
+(1 row)
+
 SELECT * FROM INT2_TBL AS f(a, b);
 ERROR:  table "f" has 1 columns available but 2 columns specified
 SELECT * FROM (TABLE int2_tbl) AS s (a, b);
index fbcc0e8d9e6845f4545318c8a708a5e300c408ea..b98007bd7a2f1b73b2825287b36eafc310bdf717 100644 (file)
@@ -45,6 +45,31 @@ SELECT * FROM INT4_TBL;
  -2147483647
 (5 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int4');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'int4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1000000000000', 'int4');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1000000000000', 'int4');
+                 pg_input_error_message                 
+--------------------------------------------------------
+ value "1000000000000" is out of range for type integer
+(1 row)
+
 SELECT i.* FROM INT4_TBL i WHERE i.f1 <> int2 '0';
      f1      
 -------------
index 1ae23cf3f94f14635b3545579e7cb8282f44074e..90ed06124986a6596aeaa1aefa1b4a89b68d3e04 100644 (file)
@@ -42,6 +42,31 @@ SELECT * FROM INT8_TBL;
  4567890123456789 | -4567890123456789
 (5 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int8');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('asdf', 'int8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('10000000000000000000', 'int8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('10000000000000000000', 'int8');
+                    pg_input_error_message                    
+--------------------------------------------------------------
+ value "10000000000000000000" is out of range for type bigint
+(1 row)
+
 -- int8/int8 cmp
 SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789;
         q1        |        q2        
index 3c610646dc5a258e071abd55e42937fcf3afd589..30a5613ed74b412fffa294b7bce287216c3fa529 100644 (file)
@@ -2199,6 +2199,49 @@ SELECT * FROM num_input_test;
  -Infinity
 (13 rows)
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'numeric');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('34xyz', 'numeric');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('1e400000', 'numeric');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1e400000', 'numeric');
+     pg_input_error_message     
+--------------------------------
+ value overflows numeric format
+(1 row)
+
+SELECT pg_input_is_valid('1234.567', 'numeric(8,4)');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('1234.567', 'numeric(7,4)');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1234.567', 'numeric(7,4)');
+ pg_input_error_message 
+------------------------
+ numeric field overflow
+(1 row)
+
 --
 -- Test precision and scale typemods
 --
index a4cc2d8c126341dcef8bea1c8ab65e21baa080b9..801d9e556b69c377e980902f3617359d43bb7795 100644 (file)
@@ -69,6 +69,37 @@ ERROR:  malformed record literal: "(Joe,Blow) /"
 LINE 1: select '(Joe,Blow) /'::fullname;
                ^
 DETAIL:  Junk after right parenthesis.
+-- test non-error-throwing API
+SELECT pg_input_is_valid('(1,2)', 'complex');
+ pg_input_is_valid 
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('(1,2', 'complex');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('(1,zed)', 'complex');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('(1,zed)', 'complex');
+                pg_input_error_message                 
+-------------------------------------------------------
+ invalid input syntax for type double precision: "zed"
+(1 row)
+
+SELECT pg_input_error_message('(1,1e400)', 'complex');
+              pg_input_error_message               
+---------------------------------------------------
+ "1e400" is out of range for type double precision
+(1 row)
+
 create temp table quadtable(f1 int, q quad);
 insert into quadtable values (1, ((3.3,4.4),(5.5,6.6)));
 insert into quadtable values (2, ((null,4.4),(5.5,6.6)));
index 791af5c0ce1f36e59afeb41c98a477fe479f7768..38e8dd440b8cbafc5bdccd11062d00be953216e2 100644 (file)
@@ -113,6 +113,12 @@ SELECT a FROM arrtest WHERE a[2] IS NULL;
 DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
 SELECT a,b,c FROM arrtest;
 
+-- test non-error-throwing API
+SELECT pg_input_is_valid('{1,2,3}', 'integer[]');
+SELECT pg_input_is_valid('{1,2', 'integer[]');
+SELECT pg_input_is_valid('{1,zed}', 'integer[]');
+SELECT pg_input_error_message('{1,zed}', 'integer[]');
+
 -- test mixed slice/scalar subscripting
 select '{{1,2,3},{4,5,6},{7,8,9}}'::int[];
 select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
index 4dd47aaf9d8a71e3471d6731bf22848e24e24b74..dfaa55dd0fff3ae0b6380b82678e4502a0cb8a2b 100644 (file)
@@ -62,6 +62,11 @@ SELECT bool '000' AS error;
 
 SELECT bool '' AS error;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('true', 'bool');
+SELECT pg_input_is_valid('asdf', 'bool');
+SELECT pg_input_error_message('junk', 'bool');
+
 -- and, or, not in qualifications
 
 SELECT bool 't' or bool 'f' AS true;
index 612486ecbd2ee30fd771910f830b8cf1e49ee7c6..061477726b91d52e4ddf2c317b3fd330008a7212 100644 (file)
@@ -36,6 +36,12 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('5.   0');
 INSERT INTO FLOAT4_TBL(f1) VALUES ('     - 3.0');
 INSERT INTO FLOAT4_TBL(f1) VALUES ('123            5');
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float4');
+SELECT pg_input_is_valid('xyz', 'float4');
+SELECT pg_input_is_valid('1e400', 'float4');
+SELECT pg_input_error_message('1e400', 'float4');
+
 -- special inputs
 SELECT 'NaN'::float4;
 SELECT 'nan'::float4;
index 03c134b07854b0968253fa58ebb96604149f7c51..c276a5324c46009f2b5a3b62baa3d66ed9fdd699 100644 (file)
@@ -34,6 +34,12 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('5.   0');
 INSERT INTO FLOAT8_TBL(f1) VALUES ('    - 3');
 INSERT INTO FLOAT8_TBL(f1) VALUES ('123           5');
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'float8');
+SELECT pg_input_is_valid('xyz', 'float8');
+SELECT pg_input_is_valid('1e4000', 'float8');
+SELECT pg_input_error_message('1e4000', 'float8');
+
 -- special inputs
 SELECT 'NaN'::float8;
 SELECT 'nan'::float8;
index ea29066b78eeadd52677ac786f15ae7fe05c83de..98a761a24a361a29d34f30ea7f4ccea603f388e1 100644 (file)
@@ -17,6 +17,12 @@ INSERT INTO INT2_TBL(f1) VALUES ('');
 
 SELECT * FROM INT2_TBL;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int2');
+SELECT pg_input_is_valid('asdf', 'int2');
+SELECT pg_input_is_valid('50000', 'int2');
+SELECT pg_input_error_message('50000', 'int2');
+
 SELECT * FROM INT2_TBL AS f(a, b);
 
 SELECT * FROM (TABLE int2_tbl) AS s (a, b);
index f19077f3da21ef52f824248ea0162ef0ed0d40e3..54420818de5680ff298a69d9f2c0fbf43ae51ea1 100644 (file)
@@ -17,6 +17,12 @@ INSERT INTO INT4_TBL(f1) VALUES ('');
 
 SELECT * FROM INT4_TBL;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int4');
+SELECT pg_input_is_valid('asdf', 'int4');
+SELECT pg_input_is_valid('1000000000000', 'int4');
+SELECT pg_input_error_message('1000000000000', 'int4');
+
 SELECT i.* FROM INT4_TBL i WHERE i.f1 <> int2 '0';
 
 SELECT i.* FROM INT4_TBL i WHERE i.f1 <> int4 '0';
index 38b771964d79873fa7104473fa2cfff63901c234..76007b692b2bb6fa9d3d5aa1fc052374de0649b4 100644 (file)
@@ -16,6 +16,12 @@ INSERT INTO INT8_TBL(q1) VALUES ('');
 
 SELECT * FROM INT8_TBL;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34', 'int8');
+SELECT pg_input_is_valid('asdf', 'int8');
+SELECT pg_input_is_valid('10000000000000000000', 'int8');
+SELECT pg_input_error_message('10000000000000000000', 'int8');
+
 -- int8/int8 cmp
 SELECT * FROM INT8_TBL WHERE q2 = 4567890123456789;
 SELECT * FROM INT8_TBL WHERE q2 <> 4567890123456789;
index 93bb0996be57418f5b779f32a38094977ce5af24..7bb34e5021aa811f7b154330e952105c1d6e16a3 100644 (file)
@@ -1053,6 +1053,15 @@ INSERT INTO num_input_test(n1) VALUES ('+ infinity');
 
 SELECT * FROM num_input_test;
 
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('34.5', 'numeric');
+SELECT pg_input_is_valid('34xyz', 'numeric');
+SELECT pg_input_is_valid('1e400000', 'numeric');
+SELECT pg_input_error_message('1e400000', 'numeric');
+SELECT pg_input_is_valid('1234.567', 'numeric(8,4)');
+SELECT pg_input_is_valid('1234.567', 'numeric(7,4)');
+SELECT pg_input_error_message('1234.567', 'numeric(7,4)');
+
 --
 -- Test precision and scale typemods
 --
index ad5b7e128f956ba1bb6b6f74023fba0e3b0bd80e..0844e7488d0506acc9294c7f4d098a365ba89791 100644 (file)
@@ -31,6 +31,13 @@ select '[]'::fullname;          -- bad
 select ' (Joe,Blow)  '::fullname;  -- ok, extra whitespace
 select '(Joe,Blow) /'::fullname;  -- bad
 
+-- test non-error-throwing API
+SELECT pg_input_is_valid('(1,2)', 'complex');
+SELECT pg_input_is_valid('(1,2', 'complex');
+SELECT pg_input_is_valid('(1,zed)', 'complex');
+SELECT pg_input_error_message('(1,zed)', 'complex');
+SELECT pg_input_error_message('(1,1e400)', 'complex');
+
 create temp table quadtable(f1 int, q quad);
 
 insert into quadtable values (1, ((3.3,4.4),(5.5,6.6)));