Convert a few more datatype input functions to report errors softly.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Dec 2022 22:50:24 +0000 (17:50 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 14 Dec 2022 22:50:24 +0000 (17:50 -0500)
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.

(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all.  That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)

Amul Sul, minor mods by me

Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com

18 files changed:
src/backend/utils/adt/acl.c
src/backend/utils/adt/int.c
src/backend/utils/adt/oid.c
src/backend/utils/adt/pg_lsn.c
src/backend/utils/adt/tid.c
src/backend/utils/adt/xid8funcs.c
src/test/regress/expected/int2.out
src/test/regress/expected/oid.out
src/test/regress/expected/pg_lsn.out
src/test/regress/expected/privileges.out
src/test/regress/expected/tid.out
src/test/regress/expected/xid.out
src/test/regress/sql/int2.sql
src/test/regress/sql/oid.sql
src/test/regress/sql/pg_lsn.sql
src/test/regress/sql/privileges.sql
src/test/regress/sql/tid.sql
src/test/regress/sql/xid.sql

index bba953cd6e0b42899228ec535fac8122d017910b..d70b64a8cfe839257e39089744765bf20d9e60cf 100644 (file)
@@ -81,11 +81,11 @@ static List *cached_roles[] = {NIL, NIL, NIL};
 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,
@@ -135,9 +135,12 @@ static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue
  *     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;
@@ -169,7 +172,7 @@ getid(const char *s, char *n)
 
        /* 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.",
@@ -236,9 +239,12 @@ putid(char *p, const char *s)
  *     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,
@@ -248,25 +254,30 @@ aclparse(const char *s, AclItem *aip)
 
    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")));
 
@@ -328,7 +339,7 @@ aclparse(const char *s, AclItem *aip)
                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)));
@@ -340,7 +351,13 @@ aclparse(const char *s, AclItem *aip)
    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
@@ -348,12 +365,18 @@ aclparse(const char *s, AclItem *aip)
     */
    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
    {
@@ -569,14 +592,19 @@ Datum
 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")));
 
index 8de38abd11dc0244007c80eb6ee139bd0b6021e7..2c90e526a60a96ad31d9c78def19549056b4e512 100644 (file)
@@ -141,6 +141,7 @@ Datum
 int2vectorin(PG_FUNCTION_ARGS)
 {
    char       *intString = PG_GETARG_CSTRING(0);
+   Node       *escontext = fcinfo->context;
    int2vector *result;
    int         n;
 
@@ -160,19 +161,19 @@ int2vectorin(PG_FUNCTION_ARGS)
        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)));
@@ -183,7 +184,7 @@ int2vectorin(PG_FUNCTION_ARGS)
    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")));
 
index b5af42234120680b9f153eb1b10c6fea83e9d2d3..9d382b5cb7c151208a1a8bedf7dc61da3010c717 100644 (file)
@@ -19,6 +19,7 @@
 
 #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)));
@@ -53,19 +65,19 @@ oidin_subr(const char *s, char **endloc)
     * 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")));
@@ -81,7 +93,7 @@ oidin_subr(const char *s, char **endloc)
        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)));
@@ -104,7 +116,7 @@ oidin_subr(const char *s, char **endloc)
 #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")));
@@ -119,7 +131,7 @@ oidin(PG_FUNCTION_ARGS)
    char       *s = PG_GETARG_CSTRING(0);
    Oid         result;
 
-   result = oidin_subr(s, NULL);
+   result = oidin_subr(s, NULL, fcinfo->context);
    PG_RETURN_OID(result);
 }
 
@@ -194,6 +206,7 @@ Datum
 oidvectorin(PG_FUNCTION_ARGS)
 {
    char       *oidString = PG_GETARG_CSTRING(0);
+   Node       *escontext = fcinfo->context;
    oidvector  *result;
    int         n;
 
@@ -205,12 +218,14 @@ oidvectorin(PG_FUNCTION_ARGS)
            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")));
 
@@ -324,7 +339,7 @@ oidparse(Node *node)
             * 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));
    }
index 15266f36f598372fa3704b9ae059eefbb2c38cd2..316a102ef30963c499617f18ba6c97a99e7981d1 100644 (file)
@@ -69,7 +69,7 @@ pg_lsn_in(PG_FUNCTION_ARGS)
 
    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)));
index 83ac589f9579939fd46fb126097f35bdc6c80bd3..4dc1b327bf3d7008d196d3ba8ca679eb1a199a7c 100644 (file)
@@ -57,6 +57,7 @@ Datum
 tidin(PG_FUNCTION_ARGS)
 {
    char       *str = PG_GETARG_CSTRING(0);
+   Node       *escontext = fcinfo->context;
    char       *p,
               *coord[NTIDARGS];
    int         i;
@@ -71,7 +72,7 @@ tidin(PG_FUNCTION_ARGS)
            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)));
@@ -79,7 +80,7 @@ tidin(PG_FUNCTION_ARGS)
    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)));
@@ -93,7 +94,7 @@ tidin(PG_FUNCTION_ARGS)
 #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)));
@@ -102,7 +103,7 @@ tidin(PG_FUNCTION_ARGS)
    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)));
index d8e40b3b969e4b940589c23a1149929fc482c69c..2093776809f1a3c225573fea0ec84e95146018da 100644 (file)
@@ -285,7 +285,7 @@ buf_finalize(StringInfo buf)
  * parse snapshot from cstring
  */
 static pg_snapshot *
-parse_snapshot(const char *str)
+parse_snapshot(const char *str, Node *escontext)
 {
    FullTransactionId xmin;
    FullTransactionId xmax;
@@ -341,11 +341,10 @@ parse_snapshot(const char *str)
    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 */
 }
 
 /*
@@ -447,7 +446,7 @@ pg_snapshot_in(PG_FUNCTION_ARGS)
    char       *str = PG_GETARG_CSTRING(0);
    pg_snapshot *snap;
 
-   snap = parse_snapshot(str);
+   snap = parse_snapshot(str, fcinfo->context);
 
    PG_RETURN_POINTER(snap);
 }
index 08e2f9f9dca372b685d9437a0a62b58865eefd59..08c333b75a2196fc89bfb6dc4a8bde977332ec7d 100644 (file)
@@ -70,6 +70,25 @@ SELECT pg_input_error_message('50000', 'int2');
  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);
index 890937345367eef97190e7babb44fdc3812f76c0..b664bab5f93480d2270ed0c883f54215bcb917ed 100644 (file)
@@ -65,6 +65,68 @@ SELECT * FROM OID_TBL;
          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  
 ------
index 99a748a6a765168b33d2c32556783deca0836323..01501f8c9bf1a72d86e8f45bd9b8c0f607bc432f 100644 (file)
@@ -26,6 +26,19 @@ INSERT INTO PG_LSN_TBL VALUES ('/ABCD');
 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        
index 169b364b22fc461b73f8a48305f3e701e0c44e12..34c26a804a7f4567038572fb2f5e6bbe4ea2c735 100644 (file)
@@ -2233,6 +2233,49 @@ SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole,
 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.
index 8cd6d60595273eae85f1a3d6391a6f63b88ce0b8..ff67ed43f0a1845fa78ba5a74e7bdcea5808e921 100644 (file)
@@ -17,6 +17,31 @@ SELECT '(1,65536)'::tid;  -- error
 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
index d8e76f3321f809db86035eaaaa03c924b1491dd2..c7b8d299c8479fba25595bb6fb2a7dd48258400a 100644 (file)
@@ -181,6 +181,37 @@ select '12:16:14,13'::pg_snapshot;
 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
index ad30c2feaa110b6651b70ba2733841797e60fe1b..a812235ee50fd54a11d6089f8583ea2f7811f966 100644 (file)
@@ -23,6 +23,11 @@ SELECT pg_input_is_valid('asdf', 'int2');
 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);
index 25b4b68a6a0e613de1cff5710c0e5114572bdd64..39937c2f1d33ff957df6dab1a1f9f8eb5b208a2f 100644 (file)
@@ -28,6 +28,20 @@ INSERT INTO OID_TBL(f1) VALUES ('-23582358720398502385');
 
 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';
index 615368ba960be3b23d9554e06578085ac9acdfd9..3d57d66e0c0dc8e0c7f5c7b4695bdac6273b501f 100644 (file)
@@ -15,6 +15,10 @@ INSERT INTO PG_LSN_TBL VALUES (' 0/12345678');
 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;
 
index b2db1c6dd56b84302dccf9377a7763a9fed603e9..39aa0b4ecf78cdea10d64ff01f0fae85ecc240e0 100644 (file)
@@ -1430,6 +1430,15 @@ SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole,
 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.
index 990d314a5f8d6baa664a3b81de72c351a9023581..8196194c04ff52746a3d8d91fde4c989f6ca6231 100644 (file)
@@ -9,6 +9,12 @@ SELECT
 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
 
index bee17e63643df6a8ef9e24a43f8bf469859ccbf7..2289803681b5c9266190e0e00a52c0c4eb3abb68 100644 (file)
@@ -68,6 +68,13 @@ select '0:1:'::pg_snapshot;
 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