From 0e56a01c00737bab39f3b0a192767e9f3110ecda Mon Sep 17 00:00:00 2001
From: Amul Sul <amul.sul@enterprisedb.com>
Date: Mon, 12 Dec 2022 11:18:23 +0530
Subject: [PATCH v1 01/14] Change aclitemin to allow non-throw error reporting

---
 src/backend/utils/adt/acl.c               | 23 +++++++++++++--------
 src/test/regress/expected/type_sanity.out | 25 +++++++++++++++++++++++
 src/test/regress/sql/type_sanity.sql      |  6 ++++++
 3 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index ed1b6a41cfb..8d0aa603aea 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -85,7 +85,7 @@ static const char *getid(const char *s, char *n);
 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,
@@ -238,7 +238,7 @@ putid(char *p, const char *s)
  *		  UID/GID, id type identifier and mode type values.
  */
 static const char *
-aclparse(const char *s, AclItem *aip)
+aclparse(const char *s, AclItem *aip, Node *escontext)
 {
 	AclMode		privs,
 				goption,
@@ -253,20 +253,20 @@ aclparse(const char *s, AclItem *aip)
 	{
 		/* 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 */
 		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")));
 
@@ -331,7 +331,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)));
@@ -353,7 +353,7 @@ aclparse(const char *s, AclItem *aip)
 	{
 		s = getid(s + 1, name2);
 		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);
@@ -572,14 +572,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);
+
+	/* aclparse() returns NULL in case of soft-errors */
+	if ((s = aclparse(s, aip, escontext)) == 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")));
 
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index d3ac08c9ee3..b2bc0205a76 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -771,3 +771,28 @@ SELECT oid, typname, typtype, typelem, typarray
 -----+---------+---------+---------+----------
 (0 rows)
 
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('pg_monitor=r/', 'aclitem');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('pg_monitor=r/', 'aclitem');
+     pg_input_error_message      
+---------------------------------
+ a name must follow the "/" sign
+(1 row)
+
+SELECT pg_input_is_valid('pg_monitor=rY', 'aclitem');
+ pg_input_is_valid 
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('pg_monitor=rY', 'aclitem');
+                  pg_input_error_message                   
+-----------------------------------------------------------
+ invalid mode character: must be one of "arwdDxtXUCTcsAvz"
+(1 row)
+
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 5edc1f1f6ed..080ac15b6c8 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -590,3 +590,9 @@ SELECT oid, typname, typtype, typelem, typarray
                     WHERE a.atttypid=t.oid AND
                           a.attnum > 0 AND
                           a.attrelid='tab_core_types'::regclass);
+
+-- test non-error-throwing API for some core types
+SELECT pg_input_is_valid('pg_monitor=r/', 'aclitem');
+SELECT pg_input_error_message('pg_monitor=r/', 'aclitem');
+SELECT pg_input_is_valid('pg_monitor=rY', 'aclitem');
+SELECT pg_input_error_message('pg_monitor=rY', 'aclitem');
-- 
2.18.0

