static uint32 cached_db_hash;
-static const char *getid(const char *s, char *n);
+static const char *getid(const char *s, char *n, Node *escontext);
static void putid(char *p, const char *s);
static Acl *allocacl(int n);
static void check_acl(const Acl *acl);
-static const char *aclparse(const char *s, AclItem *aip);
+static const char *aclparse(const char *s, AclItem *aip, Node *escontext);
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
static int aclitemComparator(const void *arg1, const void *arg2);
static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
* in 's', after any quotes. Also:
* - loads the identifier into 'n'. (If no identifier is found, 'n'
* contains an empty string.) 'n' must be NAMEDATALEN bytes.
+ *
+ * Errors are reported via ereport, unless escontext is an ErrorSaveData node,
+ * in which case we log the error there and return NULL.
*/
static const char *
-getid(const char *s, char *n)
+getid(const char *s, char *n, Node *escontext)
{
int len = 0;
bool in_quotes = false;
/* Add the character to the string */
if (len >= NAMEDATALEN - 1)
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("identifier too long"),
errdetail("Identifier must be less than %d characters.",
* specification. Also:
* - loads the structure pointed to by 'aip' with the appropriate
* UID/GID, id type identifier and mode type values.
+ *
+ * Errors are reported via ereport, unless escontext is an ErrorSaveData node,
+ * in which case we log the error there and return NULL.
*/
static const char *
-aclparse(const char *s, AclItem *aip)
+aclparse(const char *s, AclItem *aip, Node *escontext)
{
AclMode privs,
goption,
Assert(s && aip);
- s = getid(s, name);
+ s = getid(s, name, escontext);
+ if (s == NULL)
+ return NULL;
if (*s != '=')
{
/* we just read a keyword, not a name */
if (strcmp(name, "group") != 0 && strcmp(name, "user") != 0)
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("unrecognized key word: \"%s\"", name),
errhint("ACL key word must be \"group\" or \"user\".")));
- s = getid(s, name); /* move s to the name beyond the keyword */
+ /* move s to the name beyond the keyword */
+ s = getid(s, name, escontext);
+ if (s == NULL)
+ return NULL;
if (name[0] == '\0')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing name"),
errhint("A name must follow the \"group\" or \"user\" key word.")));
}
if (*s != '=')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing \"=\" sign")));
read = 0;
break;
default:
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid mode character: must be one of \"%s\"",
ACL_ALL_RIGHTS_STR)));
if (name[0] == '\0')
aip->ai_grantee = ACL_ID_PUBLIC;
else
- aip->ai_grantee = get_role_oid(name, false);
+ {
+ aip->ai_grantee = get_role_oid(name, true);
+ if (!OidIsValid(aip->ai_grantee))
+ ereturn(escontext, NULL,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role \"%s\" does not exist", name)));
+ }
/*
* XXX Allow a degree of backward compatibility by defaulting the grantor
*/
if (*s == '/')
{
- s = getid(s + 1, name2);
+ s = getid(s + 1, name2, escontext);
+ if (s == NULL)
+ return NULL;
if (name2[0] == '\0')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("a name must follow the \"/\" sign")));
- aip->ai_grantor = get_role_oid(name2, false);
+ aip->ai_grantor = get_role_oid(name2, true);
+ if (!OidIsValid(aip->ai_grantor))
+ ereturn(escontext, NULL,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role \"%s\" does not exist", name2)));
}
else
{
aclitemin(PG_FUNCTION_ARGS)
{
const char *s = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
AclItem *aip;
aip = (AclItem *) palloc(sizeof(AclItem));
- s = aclparse(s, aip);
+
+ s = aclparse(s, aip, escontext);
+ if (s == NULL)
+ PG_RETURN_NULL();
+
while (isspace((unsigned char) *s))
++s;
if (*s)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("extra garbage at the end of the ACL specification")));
int2vectorin(PG_FUNCTION_ARGS)
{
char *intString = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
int2vector *result;
int n;
l = strtol(intString, &endp, 10);
if (intString == endp)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"smallint", intString)));
if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s", intString,
"smallint")));
if (*endp && *endp != ' ')
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"integer", intString)));
while (*intString && isspace((unsigned char) *intString))
intString++;
if (*intString)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("int2vector has too many elements")));
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
+#include "nodes/miscnodes.h"
#include "nodes/value.h"
#include "utils/array.h"
#include "utils/builtins.h"
* USER I/O ROUTINES *
*****************************************************************************/
+/*
+ * Parse a single OID and return its value.
+ *
+ * If endloc isn't NULL, store a pointer to the rest of the string there,
+ * so that caller can parse the rest. Otherwise, it's an error if anything
+ * but whitespace follows.
+ *
+ * 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.
+ */
static Oid
-oidin_subr(const char *s, char **endloc)
+oidin_subr(const char *s, char **endloc, Node *escontext)
{
unsigned long cvt;
char *endptr;
Oid result;
if (*s == '\0')
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"oid", s)));
* handled by the second "if" consistent across platforms.
*/
if (errno && errno != ERANGE && errno != EINVAL)
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"oid", s)));
if (endptr == s && *s != '\0')
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"oid", s)));
if (errno == ERANGE)
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s",
s, "oid")));
while (*endptr && isspace((unsigned char) *endptr))
endptr++;
if (*endptr)
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"oid", s)));
#if OID_MAX != ULONG_MAX
if (cvt != (unsigned long) result &&
cvt != (unsigned long) ((int) result))
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for type %s",
s, "oid")));
char *s = PG_GETARG_CSTRING(0);
Oid result;
- result = oidin_subr(s, NULL);
+ result = oidin_subr(s, NULL, fcinfo->context);
PG_RETURN_OID(result);
}
oidvectorin(PG_FUNCTION_ARGS)
{
char *oidString = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
oidvector *result;
int n;
oidString++;
if (*oidString == '\0')
break;
- result->values[n] = oidin_subr(oidString, &oidString);
+ result->values[n] = oidin_subr(oidString, &oidString, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ PG_RETURN_NULL();
}
while (*oidString && isspace((unsigned char) *oidString))
oidString++;
if (*oidString)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("oidvector has too many elements")));
* constants by the lexer. Accept these if they are valid OID
* strings.
*/
- return oidin_subr(castNode(Float, node)->fval, NULL);
+ return oidin_subr(castNode(Float, node)->fval, NULL, NULL);
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
}
result = pg_lsn_in_internal(str, &have_error);
if (have_error)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"pg_lsn", str)));
tidin(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
char *p,
*coord[NTIDARGS];
int i;
coord[i++] = p + 1;
if (i < NTIDARGS)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"tid", str)));
errno = 0;
cvt = strtoul(coord[0], &badp, 10);
if (errno || *badp != DELIM)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"tid", str)));
#if SIZEOF_LONG > 4
if (cvt != (unsigned long) blockNumber &&
cvt != (unsigned long) ((int32) blockNumber))
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"tid", str)));
cvt = strtoul(coord[1], &badp, 10);
if (errno || *badp != RDELIM ||
cvt > USHRT_MAX)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"tid", str)));
* parse snapshot from cstring
*/
static pg_snapshot *
-parse_snapshot(const char *str)
+parse_snapshot(const char *str, Node *escontext)
{
FullTransactionId xmin;
FullTransactionId xmax;
return buf_finalize(buf);
bad_format:
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"pg_snapshot", str_start)));
- return NULL; /* keep compiler quiet */
}
/*
char *str = PG_GETARG_CSTRING(0);
pg_snapshot *snap;
- snap = parse_snapshot(str);
+ snap = parse_snapshot(str, fcinfo->context);
PG_RETURN_POINTER(snap);
}
value "50000" is out of range for type smallint
(1 row)
+-- While we're here, check int2vector as well
+SELECT pg_input_is_valid(' 1 3 5 ', 'int2vector');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_error_message('1 asdf', 'int2vector');
+ pg_input_error_message
+------------------------------------------------
+ invalid input syntax for type smallint: "asdf"
+(1 row)
+
+SELECT pg_input_error_message('50000', 'int2vector');
+ 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);
15
(8 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('1234', 'oid');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('01XYZ', 'oid');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01XYZ', 'oid');
+ pg_input_error_message
+--------------------------------------------
+ invalid input syntax for type oid: "01XYZ"
+(1 row)
+
+SELECT pg_input_is_valid('9999999999', 'oid');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('9999999999', 'oid');
+ pg_input_error_message
+-------------------------------------------------
+ value "9999999999" is out of range for type oid
+(1 row)
+
+-- While we're here, check oidvector as well
+SELECT pg_input_is_valid(' 1 2 4 ', 'oidvector');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('01 01XYZ', 'oidvector');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01 01XYZ', 'oidvector');
+ pg_input_error_message
+------------------------------------------
+ invalid input syntax for type oid: "XYZ"
+(1 row)
+
+SELECT pg_input_is_valid('01 9999999999', 'oidvector');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01 9999999999', 'oidvector');
+ pg_input_error_message
+-------------------------------------------------
+ value "9999999999" is out of range for type oid
+(1 row)
+
SELECT o.* FROM OID_TBL o WHERE o.f1 = 1234;
f1
------
ERROR: invalid input syntax for type pg_lsn: "/ABCD"
LINE 1: INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('16AE7F7', 'pg_lsn');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('16AE7F7', 'pg_lsn');
+ pg_input_error_message
+-------------------------------------------------
+ invalid input syntax for type pg_lsn: "16AE7F7"
+(1 row)
+
-- Min/Max aggregation
SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
min | max
SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole,
'SELECT, fake_privilege', FALSE); -- error
ERROR: unrecognized privilege type: "fake_privilege"
+-- Test non-throwing aclitem I/O
+SELECT pg_input_is_valid('regress_priv_user1=r/regress_priv_user2', 'aclitem');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('regress_priv_user1=r/', 'aclitem');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('regress_priv_user1=r/', 'aclitem');
+ pg_input_error_message
+---------------------------------
+ a name must follow the "/" sign
+(1 row)
+
+SELECT pg_input_is_valid('regress_priv_user1=r/regress_no_such_user', 'aclitem');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('regress_priv_user1=r/regress_no_such_user', 'aclitem');
+ pg_input_error_message
+--------------------------------------------
+ role "regress_no_such_user" does not exist
+(1 row)
+
+SELECT pg_input_is_valid('regress_priv_user1=rY', 'aclitem');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('regress_priv_user1=rY', 'aclitem');
+ pg_input_error_message
+----------------------------------------------------------
+ invalid mode character: must be one of "arwdDxtXUCTcsAm"
+(1 row)
+
--
-- Testing blanket default grants is very hazardous since it might change
-- the privileges attached to objects created by concurrent regression tests.
ERROR: invalid input syntax for type tid: "(1,65536)"
LINE 1: SELECT '(1,65536)'::tid;
^
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('(0)', 'tid');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('(0)', 'tid');
+ pg_input_error_message
+------------------------------------------
+ invalid input syntax for type tid: "(0)"
+(1 row)
+
+SELECT pg_input_is_valid('(0,-1)', 'tid');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('(0,-1)', 'tid');
+ pg_input_error_message
+---------------------------------------------
+ invalid input syntax for type tid: "(0,-1)"
+(1 row)
+
-- tests for functions related to TID handling
CREATE TABLE tid_tab (a int);
-- min() and max() for TIDs
ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::pg_snapshot;
^
+-- also try it with non-error-throwing API
+select pg_input_is_valid('12:13:', 'pg_snapshot');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+select pg_input_is_valid('31:12:', 'pg_snapshot');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('31:12:', 'pg_snapshot');
+ pg_input_error_message
+-----------------------------------------------------
+ invalid input syntax for type pg_snapshot: "31:12:"
+(1 row)
+
+select pg_input_is_valid('12:16:14,13', 'pg_snapshot');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+select pg_input_error_message('12:16:14,13', 'pg_snapshot');
+ pg_input_error_message
+----------------------------------------------------------
+ invalid input syntax for type pg_snapshot: "12:16:14,13"
+(1 row)
+
create temp table snapshot_test (
nr integer,
snap pg_snapshot
SELECT pg_input_is_valid('50000', 'int2');
SELECT pg_input_error_message('50000', 'int2');
+-- While we're here, check int2vector as well
+SELECT pg_input_is_valid(' 1 3 5 ', 'int2vector');
+SELECT pg_input_error_message('1 asdf', 'int2vector');
+SELECT pg_input_error_message('50000', 'int2vector');
+
SELECT * FROM INT2_TBL AS f(a, b);
SELECT * FROM (TABLE int2_tbl) AS s (a, b);
SELECT * FROM OID_TBL;
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('1234', 'oid');
+SELECT pg_input_is_valid('01XYZ', 'oid');
+SELECT pg_input_error_message('01XYZ', 'oid');
+SELECT pg_input_is_valid('9999999999', 'oid');
+SELECT pg_input_error_message('9999999999', 'oid');
+
+-- While we're here, check oidvector as well
+SELECT pg_input_is_valid(' 1 2 4 ', 'oidvector');
+SELECT pg_input_is_valid('01 01XYZ', 'oidvector');
+SELECT pg_input_error_message('01 01XYZ', 'oidvector');
+SELECT pg_input_is_valid('01 9999999999', 'oidvector');
+SELECT pg_input_error_message('01 9999999999', 'oidvector');
+
SELECT o.* FROM OID_TBL o WHERE o.f1 = 1234;
SELECT o.* FROM OID_TBL o WHERE o.f1 <> '1234';
INSERT INTO PG_LSN_TBL VALUES ('ABCD/');
INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('16AE7F7', 'pg_lsn');
+SELECT pg_input_error_message('16AE7F7', 'pg_lsn');
+
-- Min/Max aggregation
SELECT MIN(f1), MAX(f1) FROM PG_LSN_TBL;
SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole,
'SELECT, fake_privilege', FALSE); -- error
+-- Test non-throwing aclitem I/O
+SELECT pg_input_is_valid('regress_priv_user1=r/regress_priv_user2', 'aclitem');
+SELECT pg_input_is_valid('regress_priv_user1=r/', 'aclitem');
+SELECT pg_input_error_message('regress_priv_user1=r/', 'aclitem');
+SELECT pg_input_is_valid('regress_priv_user1=r/regress_no_such_user', 'aclitem');
+SELECT pg_input_error_message('regress_priv_user1=r/regress_no_such_user', 'aclitem');
+SELECT pg_input_is_valid('regress_priv_user1=rY', 'aclitem');
+SELECT pg_input_error_message('regress_priv_user1=rY', 'aclitem');
+
--
-- Testing blanket default grants is very hazardous since it might change
-- the privileges attached to objects created by concurrent regression tests.
SELECT '(4294967296,1)'::tid; -- error
SELECT '(1,65536)'::tid; -- error
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('(0)', 'tid');
+SELECT pg_input_error_message('(0)', 'tid');
+SELECT pg_input_is_valid('(0,-1)', 'tid');
+SELECT pg_input_error_message('(0,-1)', 'tid');
+
-- tests for functions related to TID handling
select '12:13:0'::pg_snapshot;
select '12:16:14,13'::pg_snapshot;
+-- also try it with non-error-throwing API
+select pg_input_is_valid('12:13:', 'pg_snapshot');
+select pg_input_is_valid('31:12:', 'pg_snapshot');
+select pg_input_error_message('31:12:', 'pg_snapshot');
+select pg_input_is_valid('12:16:14,13', 'pg_snapshot');
+select pg_input_error_message('12:16:14,13', 'pg_snapshot');
+
create temp table snapshot_test (
nr integer,
snap pg_snapshot