Convert a few datatype input functions to use "soft" error reporting.

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
This commit is contained in:
Tom Lane 2022-12-09 10:14:53 -05:00
parent 1939d26282
commit ccff2d20ed
39 changed files with 727 additions and 238 deletions

View 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);

View 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);

View File

@ -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;
}

View 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" */

View 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
--

View 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
--

View 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;
}

View 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;
}
/*

View 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);
}
/*

View 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

View 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)
{

View 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));
}
/*

View 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));
}

View 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",

View 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.",

View 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 */
}
/*

View 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();
}
/*

View 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);

View 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);

View 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);

View 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

View 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

View 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

View 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

View 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

View 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);

View 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
-------------

View 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

View 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
--

View 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)));

View 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];

View 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;

View 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;

View 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;

View 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);

View 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';

View 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;

View 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
--

View 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)));