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);
/* 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);
#include "postgres.h"
#include "cubedata.h"
+#include "nodes/miscnodes.h"
#include "utils/float.h"
/* All grammar constructs return strings */
#define YYFREE pfree
static int item_count(const char *s, char delim);
-static NDBOX *write_box(int dim, char *str1, char *str2);
-static NDBOX *write_point_as_box(int dim, char *str);
+static bool write_box(int dim, char *str1, char *str2,
+ NDBOX **result, struct Node *escontext);
+static bool write_point_as_box(int dim, char *str,
+ NDBOX **result, struct Node *escontext);
%}
/* BISON Declarations */
%parse-param {NDBOX **result}
%parse-param {Size scanbuflen}
+%parse-param {struct Node *escontext}
%expect 0
%name-prefix="cube_yy"
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).",
}
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.",
YYABORT;
}
- *result = write_box( dim, $2, $4 );
+ if (!write_box(dim, $2, $4, result, escontext))
+ YYABORT;
}
| paren_list COMMA paren_list
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).",
}
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.",
YYABORT;
}
- *result = write_box( dim, $1, $3 );
+ if (!write_box(dim, $1, $3, result, escontext))
+ YYABORT;
}
| paren_list
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.",
YYABORT;
}
- *result = write_point_as_box(dim, $1);
+ if (!write_point_as_box(dim, $1, result, escontext))
+ YYABORT;
}
| list
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.",
YYABORT;
}
- *result = write_point_as_box(dim, $1);
+ if (!write_point_as_box(dim, $1, result, escontext))
+ YYABORT;
}
;
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;
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++;
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++;
}
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,
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;
}
/* 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" */
}
else
{
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for cube"),
/* translator: first %s is typically "syntax error" */
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
--
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
--
} 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,
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;
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)));
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.")));
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.")));
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.",
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")));
{
/* 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;
}
/* 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.",
* 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.")));
#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)
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);
*
* 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;
{
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 '\\':
/*
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.",
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.")));
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.")));
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)));
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.", '}')));
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 "
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.",
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.")));
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.")));
* *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,
Datum *values,
bool *nulls,
bool *hasnulls,
- int32 *nbytes)
+ int32 *nbytes,
+ Node *escontext)
{
int i,
nest_level = 0;
{
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)));
/* 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)));
if (!in_quotes)
{
if (nest_level >= ndim)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"",
origStr)));
if (!in_quotes)
{
if (nest_level == 0)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"",
origStr)));
*dstendptr = '\0';
if (i < 0 || i >= nitems)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"",
origStr)));
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;
}
}
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)));
}
*hasnulls = hasnull;
*nbytes = totbytes;
+ return true;
}
*/
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;
/* 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)));
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)));
*/
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;
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;
}
/*
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);
}
/*
float4in(PG_FUNCTION_ARGS)
{
char *num = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
char *orig_num;
float val;
char *endptr;
* 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)));
(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)));
/* 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)));
{
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++;
* 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);
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 */
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
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
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,
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)
{
{
char *num = PG_GETARG_CSTRING(0);
- PG_RETURN_INT16(pg_strtoint16(num));
+ PG_RETURN_INT16(pg_strtoint16_safe(num, fcinfo->context));
}
/*
{
char *num = PG_GETARG_CSTRING(0);
- PG_RETURN_INT32(pg_strtoint32(num));
+ PG_RETURN_INT32(pg_strtoint32_safe(num, fcinfo->context));
}
/*
{
char *num = PG_GETARG_CSTRING(0);
- PG_RETURN_INT64(pg_strtoint64(num));
+ PG_RETURN_INT64(pg_strtoint64_safe(num, fcinfo->context));
}
#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"
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",
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",
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);
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);
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;
* 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
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);
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)));
}
/* 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);
}
{
trunc_var(&value, value.dscale);
- apply_typmod(&value, typmod);
+ (void) apply_typmod(&value, typmod, NULL);
res = make_result(&value);
}
/* 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);
*/
if (NUMERIC_IS_SPECIAL(num))
{
- apply_typmod_special(num, typmod);
+ (void) apply_typmod_special(num, typmod, NULL);
PG_RETURN_NUMERIC(duplicate_numeric(num));
}
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);
Numeric res;
NumericVar result;
char buf[DBL_DIG + 100];
+ const char *endptr;
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
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);
Numeric res;
NumericVar result;
char buf[FLT_DIG + 100];
+ const char *endptr;
if (isnan(val))
PG_RETURN_NUMERIC(make_result(&const_nan));
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);
* 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;
}
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)));
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)));
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)));
* 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;
strip_var(dest);
/* Return end+1 position for caller */
- return cp;
+ *endptr = cp;
+
+ return true;
}
*
* 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;
/* 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);
#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.",
ddigits -= DEC_DIGITS;
}
}
+
+ return true;
}
/*
*
* 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;
* 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.",
/*
* 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;
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
*/
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;
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
*/
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;
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 */
}
/*
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;
* 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")));
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);
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 */
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 == '"')
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
}
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);
ReleaseTupleDesc(tupdesc);
PG_RETURN_HEAPTUPLEHEADER(result);
+
+ /* exit here once we've done lookup_rowtype_tupdesc */
+fail:
+ ReleaseTupleDesc(tupdesc);
+ PG_RETURN_NULL();
}
/*
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);
/* 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);
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);
[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
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
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
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
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
-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);
-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
-------------
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
-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
--
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)));
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];
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;
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;
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;
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);
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';
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;
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
--
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)));