diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 4f32c5dc1d..1fc447511a 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -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); diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h index 640a7ca580..96fa41a04e 100644 --- a/contrib/cube/cubedata.h +++ b/contrib/cube/cubedata.h @@ -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); diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y index 977dcba965..44450d1027 100644 --- a/contrib/cube/cubeparse.y +++ b/contrib/cube/cubeparse.y @@ -7,6 +7,7 @@ #include "postgres.h" #include "cubedata.h" +#include "nodes/miscnodes.h" #include "utils/float.h" /* All grammar constructs return strings */ @@ -21,14 +22,17 @@ #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; } diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l index 6b316f2d54..49cb699216 100644 --- a/contrib/cube/cubescan.l +++ b/contrib/cube/cubescan.l @@ -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" */ diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out index 5b89cb1a26..dc23e5ccc0 100644 --- a/contrib/cube/expected/cube.out +++ b/contrib/cube/expected/cube.out @@ -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 -- diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql index 7f8b2e3979..384883d16e 100644 --- a/contrib/cube/sql/cube.sql +++ b/contrib/cube/sql/cube.sql @@ -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 -- diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 59a0852d07..0d3d46b9a5 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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; } diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c index 051169a149..3821f6637b 100644 --- a/src/backend/utils/adt/arrayutils.c +++ b/src/backend/utils/adt/arrayutils.c @@ -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; } /* diff --git a/src/backend/utils/adt/bool.c b/src/backend/utils/adt/bool.c index cd7335287f..e291672ae4 100644 --- a/src/backend/utils/adt/bool.c +++ b/src/backend/utils/adt/bool.c @@ -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); } /* diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index da97538ebe..b02a19be24 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -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 diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index d78002b901..721ce6634f 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -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) { diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 42ddae99ef..8de38abd11 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -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)); } /* diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 98d4323755..7d1767ce0f 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -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)); } diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 8d83b2edb3..930bd26584 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -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", diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 7f0e93aa80..c024928bc8 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -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.", diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index a64422c8d0..ab1564f22d 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -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 */ } /* diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c index db843a0fbf..bdafcff02d 100644 --- a/src/backend/utils/adt/rowtypes.c +++ b/src/backend/utils/adt/rowtypes.c @@ -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(); } /* diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 2f794d1168..3f6319aed5 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -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); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 81631f1645..10d13b0f1e 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -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); diff --git a/src/include/utils/float.h b/src/include/utils/float.h index 4bf0e3ac07..f92860b4a4 100644 --- a/src/include/utils/float.h +++ b/src/include/utils/float.h @@ -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); diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 97920f38c2..a2f9d7ed16 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -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 diff --git a/src/test/regress/expected/boolean.out b/src/test/regress/expected/boolean.out index 4728fe2dfd..977124b20b 100644 --- a/src/test/regress/expected/boolean.out +++ b/src/test/regress/expected/boolean.out @@ -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 diff --git a/src/test/regress/expected/float4-misrounded-input.out b/src/test/regress/expected/float4-misrounded-input.out index 3d5d298b73..24fde6cc9f 100644 --- a/src/test/regress/expected/float4-misrounded-input.out +++ b/src/test/regress/expected/float4-misrounded-input.out @@ -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 diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out index 6ad5d00aa2..1d7090a90d 100644 --- a/src/test/regress/expected/float4.out +++ b/src/test/regress/expected/float4.out @@ -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 diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index de4d57ec9f..2b25784f7f 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -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 diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out index 109cf9baaa..6a23567b67 100644 --- a/src/test/regress/expected/int2.out +++ b/src/test/regress/expected/int2.out @@ -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); diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out index fbcc0e8d9e..b98007bd7a 100644 --- a/src/test/regress/expected/int4.out +++ b/src/test/regress/expected/int4.out @@ -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 ------------- diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out index 1ae23cf3f9..90ed061249 100644 --- a/src/test/regress/expected/int8.out +++ b/src/test/regress/expected/int8.out @@ -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 diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index 3c610646dc..30a5613ed7 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -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 -- diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index a4cc2d8c12..801d9e556b 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -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))); diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 791af5c0ce..38e8dd440b 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -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]; diff --git a/src/test/regress/sql/boolean.sql b/src/test/regress/sql/boolean.sql index 4dd47aaf9d..dfaa55dd0f 100644 --- a/src/test/regress/sql/boolean.sql +++ b/src/test/regress/sql/boolean.sql @@ -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; diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql index 612486ecbd..061477726b 100644 --- a/src/test/regress/sql/float4.sql +++ b/src/test/regress/sql/float4.sql @@ -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; diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql index 03c134b078..c276a5324c 100644 --- a/src/test/regress/sql/float8.sql +++ b/src/test/regress/sql/float8.sql @@ -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; diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql index ea29066b78..98a761a24a 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -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); diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql index f19077f3da..54420818de 100644 --- a/src/test/regress/sql/int4.sql +++ b/src/test/regress/sql/int4.sql @@ -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'; diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql index 38b771964d..76007b692b 100644 --- a/src/test/regress/sql/int8.sql +++ b/src/test/regress/sql/int8.sql @@ -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; diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index 93bb0996be..7bb34e5021 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -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 -- diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql index ad5b7e128f..0844e7488d 100644 --- a/src/test/regress/sql/rowtypes.sql +++ b/src/test/regress/sql/rowtypes.sql @@ -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)));