macaddr_in(PG_FUNCTION_ARGS)
 {
    char       *str = PG_GETARG_CSTRING(0);
+   Node       *escontext = fcinfo->context;
    macaddr    *result;
    int         a,
                b,
        count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
                       &a, &b, &c, &d, &e, &f, junk);
    if (count != 6)
-       ereport(ERROR,
+       ereturn(escontext, (Datum) 0,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
                        str)));
    if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
        (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
        (e < 0) || (e > 255) || (f < 0) || (f > 255))
-       ereport(ERROR,
+       ereturn(escontext, (Datum) 0,
                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
 
 
 #define lobits(addr) \
   ((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))
 
-static unsigned char hex2_to_uchar(const unsigned char *ptr, const unsigned char *str);
+static unsigned char hex2_to_uchar(const unsigned char *ptr, bool *badhex);
 
 static const signed char hexlookup[128] = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 /*
  * hex2_to_uchar - convert 2 hex digits to a byte (unsigned char)
  *
- * This will ereport() if the end of the string is reached ('\0' found), or if
+ * Sets *badhex to true if the end of the string is reached ('\0' found), or if
  * either character is not a valid hex digit.
- *
- * ptr is the pointer to where the digits to convert are in the string, str is
- * the entire string, which is used only for error reporting.
  */
 static inline unsigned char
-hex2_to_uchar(const unsigned char *ptr, const unsigned char *str)
+hex2_to_uchar(const unsigned char *ptr, bool *badhex)
 {
-   unsigned char ret = 0;
+   unsigned char ret;
    signed char lookup;
 
    /* Handle the first character */
    return ret;
 
 invalid_input:
-   ereport(ERROR,
-           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-            errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
-                   str)));
-
-   /* We do not actually reach here */
+   *badhex = true;
    return 0;
 }
 
 macaddr8_in(PG_FUNCTION_ARGS)
 {
    const unsigned char *str = (unsigned char *) PG_GETARG_CSTRING(0);
+   Node       *escontext = fcinfo->context;
    const unsigned char *ptr = str;
+   bool        badhex = false;
    macaddr8   *result;
    unsigned char a = 0,
                b = 0,
        switch (count)
        {
            case 1:
-               a = hex2_to_uchar(ptr, str);
+               a = hex2_to_uchar(ptr, &badhex);
                break;
            case 2:
-               b = hex2_to_uchar(ptr, str);
+               b = hex2_to_uchar(ptr, &badhex);
                break;
            case 3:
-               c = hex2_to_uchar(ptr, str);
+               c = hex2_to_uchar(ptr, &badhex);
                break;
            case 4:
-               d = hex2_to_uchar(ptr, str);
+               d = hex2_to_uchar(ptr, &badhex);
                break;
            case 5:
-               e = hex2_to_uchar(ptr, str);
+               e = hex2_to_uchar(ptr, &badhex);
                break;
            case 6:
-               f = hex2_to_uchar(ptr, str);
+               f = hex2_to_uchar(ptr, &badhex);
                break;
            case 7:
-               g = hex2_to_uchar(ptr, str);
+               g = hex2_to_uchar(ptr, &badhex);
                break;
            case 8:
-               h = hex2_to_uchar(ptr, str);
+               h = hex2_to_uchar(ptr, &badhex);
                break;
            default:
                /* must be trailing garbage... */
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                        errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
-                               str)));
+               goto fail;
        }
 
+       if (badhex)
+           goto fail;
+
        /* Move forward to where the next byte should be */
        ptr += 2;
 
 
            /* Have to use the same spacer throughout */
            else if (spacer != *ptr)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                        errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
-                               str)));
+               goto fail;
 
            /* move past the spacer */
            ptr++;
 
                /* If we found a space and then non-space, it's invalid */
                if (*ptr)
-                   ereport(ERROR,
-                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                            errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
-                                   str)));
+                   goto fail;
            }
        }
    }
        e = 0xFE;
    }
    else if (count != 8)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
-                       str)));
+       goto fail;
 
    result = (macaddr8 *) palloc0(sizeof(macaddr8));
 
    result->h = h;
 
    PG_RETURN_MACADDR8_P(result);
+
+fail:
+   ereturn(escontext, (Datum) 0,
+           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+            errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
+                   str)));
 }
 
 /*
 
  * Common INET/CIDR input routine
  */
 static inet *
-network_in(char *src, bool is_cidr)
+network_in(char *src, bool is_cidr, Node *escontext)
 {
    int         bits;
    inet       *dst;
    bits = pg_inet_net_pton(ip_family(dst), src, ip_addr(dst),
                            is_cidr ? ip_addrsize(dst) : -1);
    if ((bits < 0) || (bits > ip_maxbits(dst)))
-       ereport(ERROR,
+       ereturn(escontext, NULL,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
        /* translator: first %s is inet or cidr */
                 errmsg("invalid input syntax for type %s: \"%s\"",
    if (is_cidr)
    {
        if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
-           ereport(ERROR,
+           ereturn(escontext, NULL,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid cidr value: \"%s\"", src),
                     errdetail("Value has bits set to right of mask.")));
 {
    char       *src = PG_GETARG_CSTRING(0);
 
-   PG_RETURN_INET_P(network_in(src, false));
+   PG_RETURN_INET_P(network_in(src, false, fcinfo->context));
 }
 
 Datum
 {
    char       *src = PG_GETARG_CSTRING(0);
 
-   PG_RETURN_INET_P(network_in(src, true));
+   PG_RETURN_INET_P(network_in(src, true, fcinfo->context));
 }
 
 
 
    clean_ipv6_addr(port->raddr.addr.ss_family, remote_host);
 
-   PG_RETURN_INET_P(network_in(remote_host, false));
+   PG_RETURN_INET_P(network_in(remote_host, false, NULL));
 }
 
 
 
    clean_ipv6_addr(port->laddr.addr.ss_family, local_host);
 
-   PG_RETURN_INET_P(network_in(local_host, false));
+   PG_RETURN_INET_P(network_in(local_host, false, NULL));
 }
 
 
 
 bit_in(PG_FUNCTION_ARGS)
 {
    char       *input_string = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
    Oid         typelem = PG_GETARG_OID(1);
 #endif
    int32       atttypmod = PG_GETARG_INT32(2);
+   Node       *escontext = fcinfo->context;
    VarBit     *result;         /* The resulting bit string           */
    char       *sp;             /* pointer into the character string  */
    bits8      *r;              /* pointer into the result */
    else
    {
        if (slen > VARBITMAXLEN / 4)
-           ereport(ERROR,
+           ereturn(escontext, (Datum) 0,
                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                     errmsg("bit string length exceeds the maximum allowed (%d)",
                            VARBITMAXLEN)));
    if (atttypmod <= 0)
        atttypmod = bitlen;
    else if (bitlen != atttypmod)
-       ereport(ERROR,
+       ereturn(escontext, (Datum) 0,
                (errcode(ERRCODE_STRING_DATA_LENGTH_MISMATCH),
                 errmsg("bit string length %d does not match type bit(%d)",
                        bitlen, atttypmod)));
            if (*sp == '1')
                *r |= x;
            else if (*sp != '0')
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("\"%.*s\" is not a valid binary digit",
                                pg_mblen(sp), sp)));
            else if (*sp >= 'a' && *sp <= 'f')
                x = (bits8) (*sp - 'a') + 10;
            else
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("\"%.*s\" is not a valid hexadecimal digit",
                                pg_mblen(sp), sp)));
 varbit_in(PG_FUNCTION_ARGS)
 {
    char       *input_string = PG_GETARG_CSTRING(0);
-
 #ifdef NOT_USED
    Oid         typelem = PG_GETARG_OID(1);
 #endif
    int32       atttypmod = PG_GETARG_INT32(2);
+   Node       *escontext = fcinfo->context;
    VarBit     *result;         /* The resulting bit string           */
    char       *sp;             /* pointer into the character string  */
    bits8      *r;              /* pointer into the result */
    else
    {
        if (slen > VARBITMAXLEN / 4)
-           ereport(ERROR,
+           ereturn(escontext, (Datum) 0,
                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                     errmsg("bit string length exceeds the maximum allowed (%d)",
                            VARBITMAXLEN)));
    if (atttypmod <= 0)
        atttypmod = bitlen;
    else if (bitlen > atttypmod)
-       ereport(ERROR,
+       ereturn(escontext, (Datum) 0,
                (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
                 errmsg("bit string too long for type bit varying(%d)",
                        atttypmod)));
            if (*sp == '1')
                *r |= x;
            else if (*sp != '0')
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("\"%.*s\" is not a valid binary digit",
                                pg_mblen(sp), sp)));
            else if (*sp >= 'a' && *sp <= 'f')
                x = (bits8) (*sp - 'a') + 10;
            else
-               ereport(ERROR,
+               ereturn(escontext, (Datum) 0,
                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                         errmsg("\"%.*s\" is not a valid hexadecimal digit",
                                pg_mblen(sp), sp)));
 
  1001 | 0101 | 1001 | 0101
 (1 row)
 
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('01010001', 'bit(10)');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01010001', 'bit(10)');
+             pg_input_error_message              
+-------------------------------------------------
+ bit string length 8 does not match type bit(10)
+(1 row)
+
+SELECT pg_input_is_valid('01010Z01', 'bit(8)');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01010Z01', 'bit(8)');
+     pg_input_error_message      
+---------------------------------
+ "Z" is not a valid binary digit
+(1 row)
+
+SELECT pg_input_is_valid('x01010Z01', 'bit(32)');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('x01010Z01', 'bit(32)');
+        pg_input_error_message        
+--------------------------------------
+ "Z" is not a valid hexadecimal digit
+(1 row)
+
+SELECT pg_input_is_valid('01010Z01', 'varbit');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('01010Z01', 'varbit');
+     pg_input_error_message      
+---------------------------------
+ "Z" is not a valid binary digit
+(1 row)
+
+SELECT pg_input_is_valid('x01010Z01', 'varbit');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('x01010Z01', 'varbit');
+        pg_input_error_message        
+--------------------------------------
+ "Z" is not a valid hexadecimal digit
+(1 row)
+
 
  ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
 (91 rows)
 
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('1234', 'cidr');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1234', 'cidr');
+           pg_input_error_message           
+--------------------------------------------
+ invalid input syntax for type cidr: "1234"
+(1 row)
+
+SELECT pg_input_is_valid('192.168.198.200/24', 'cidr');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('192.168.198.200/24', 'cidr');
+          pg_input_error_message          
+------------------------------------------
+ invalid cidr value: "192.168.198.200/24"
+(1 row)
+
+SELECT pg_input_is_valid('1234', 'inet');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('1234', 'inet');
+           pg_input_error_message           
+--------------------------------------------
+ invalid input syntax for type inet: "1234"
+(1 row)
+
 
 (12 rows)
 
 DROP TABLE macaddr_data;
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('08:00:2b:01:02:ZZ', 'macaddr');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('08:00:2b:01:02:ZZ', 'macaddr');
+                   pg_input_error_message                   
+------------------------------------------------------------
+ invalid input syntax for type macaddr: "08:00:2b:01:02:ZZ"
+(1 row)
+
+SELECT pg_input_is_valid('08:00:2b:01:02:', 'macaddr');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('08:00:2b:01:02:', 'macaddr');
+                  pg_input_error_message                  
+----------------------------------------------------------
+ invalid input syntax for type macaddr: "08:00:2b:01:02:"
+(1 row)
+
 
 (20 rows)
 
 DROP TABLE macaddr8_data;
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('08:00:2b:01:02:03:04:ZZ', 'macaddr8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('08:00:2b:01:02:03:04:ZZ', 'macaddr8');
+                      pg_input_error_message                       
+-------------------------------------------------------------------
+ invalid input syntax for type macaddr8: "08:00:2b:01:02:03:04:ZZ"
+(1 row)
+
+SELECT pg_input_is_valid('08:00:2b:01:02:03:04:', 'macaddr8');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('08:00:2b:01:02:03:04:', 'macaddr8');
+                     pg_input_error_message                      
+-----------------------------------------------------------------
+ invalid input syntax for type macaddr8: "08:00:2b:01:02:03:04:"
+(1 row)
+
 
 \d bit_defaults
 INSERT INTO bit_defaults DEFAULT VALUES;
 TABLE bit_defaults;
+
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('01010001', 'bit(10)');
+SELECT pg_input_error_message('01010001', 'bit(10)');
+SELECT pg_input_is_valid('01010Z01', 'bit(8)');
+SELECT pg_input_error_message('01010Z01', 'bit(8)');
+SELECT pg_input_is_valid('x01010Z01', 'bit(32)');
+SELECT pg_input_error_message('x01010Z01', 'bit(32)');
+
+SELECT pg_input_is_valid('01010Z01', 'varbit');
+SELECT pg_input_error_message('01010Z01', 'varbit');
+SELECT pg_input_is_valid('x01010Z01', 'varbit');
+SELECT pg_input_error_message('x01010Z01', 'varbit');
 
   ('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0'::inet),
   ('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128'::inet)
 ) AS i(a) ORDER BY a;
+
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('1234', 'cidr');
+SELECT pg_input_error_message('1234', 'cidr');
+SELECT pg_input_is_valid('192.168.198.200/24', 'cidr');
+SELECT pg_input_error_message('192.168.198.200/24', 'cidr');
+
+SELECT pg_input_is_valid('1234', 'inet');
+SELECT pg_input_error_message('1234', 'inet');
 
 SELECT  b | '01:02:03:04:05:06' FROM macaddr_data;
 
 DROP TABLE macaddr_data;
+
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('08:00:2b:01:02:ZZ', 'macaddr');
+SELECT pg_input_error_message('08:00:2b:01:02:ZZ', 'macaddr');
+SELECT pg_input_is_valid('08:00:2b:01:02:', 'macaddr');
+SELECT pg_input_error_message('08:00:2b:01:02:', 'macaddr');
 
 SELECT  b | '01:02:03:04:05:06' FROM macaddr8_data;
 
 DROP TABLE macaddr8_data;
+
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('08:00:2b:01:02:03:04:ZZ', 'macaddr8');
+SELECT pg_input_error_message('08:00:2b:01:02:03:04:ZZ', 'macaddr8');
+SELECT pg_input_is_valid('08:00:2b:01:02:03:04:', 'macaddr8');
+SELECT pg_input_error_message('08:00:2b:01:02:03:04:', 'macaddr8');