From 29610d4e417a1a0cf099e10736b5c14aec90f641 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Sun, 21 Jul 2024 09:49:17 -0500
Subject: [PATCH 1/4] change function SET to use a separate GUC source

This allows distinguishing it from ALTER ROLE SET and ALTER DATABASE
SET, as needed for the following patch.

See also:
2abae34a2e8fde42be731b4e18d44cd08901464d - added function SET
0c66a223774dec62edb5281a47e72fe480a8f7aa - (partially) updated comments about PGC_S_TEST
---
 src/backend/access/table/tableamapi.c    |  2 +-
 src/backend/catalog/pg_db_role_setting.c |  8 ++++----
 src/backend/commands/functioncmds.c      |  4 ++--
 src/backend/commands/tablespace.c        |  4 ++--
 src/backend/commands/variable.c          |  8 ++++----
 src/backend/storage/buffer/localbuf.c    |  2 +-
 src/backend/utils/cache/ts_cache.c       |  2 +-
 src/backend/utils/misc/guc.c             | 18 +++++++++---------
 src/include/utils/guc.h                  |  7 ++++---
 9 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c
index e9b598256fb..7c0f08ed46d 100644
--- a/src/backend/access/table/tableamapi.c
+++ b/src/backend/access/table/tableamapi.c
@@ -132,7 +132,7 @@ check_default_table_access_method(char **newval, void **extra, GucSource source)
 			 * nonexistent table access method, only a NOTICE. See comments in
 			 * guc.h.
 			 */
-			if (source == PGC_S_TEST)
+			if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 			{
 				ereport(NOTICE,
 						(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 8c20f519fc0..abfcc5655b3 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -70,7 +70,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 								 RelationGetDescr(rel), &isnull);
 
 			if (!isnull)
-				new = GUCArrayReset(DatumGetArrayTypeP(datum));
+				new = GUCArrayReset(DatumGetArrayTypeP(datum), PGC_S_TEST);
 
 			if (new)
 			{
@@ -115,9 +115,9 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 
 		/* Update (valuestr is NULL in RESET cases) */
 		if (valuestr)
-			a = GUCArrayAdd(a, setstmt->name, valuestr);
+			a = GUCArrayAdd(a, setstmt->name, valuestr, PGC_S_TEST);
 		else
-			a = GUCArrayDelete(a, setstmt->name);
+			a = GUCArrayDelete(a, setstmt->name, PGC_S_TEST);
 
 		if (a)
 		{
@@ -141,7 +141,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
 
 		memset(nulls, false, sizeof(nulls));
 
-		a = GUCArrayAdd(NULL, setstmt->name, valuestr);
+		a = GUCArrayAdd(NULL, setstmt->name, valuestr, PGC_S_TEST);
 
 		values[Anum_pg_db_role_setting_setdatabase - 1] =
 			ObjectIdGetDatum(databaseid);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 6593fd7d811..30a58cf027c 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -657,9 +657,9 @@ update_proconfig_value(ArrayType *a, List *set_items)
 			char	   *valuestr = ExtractSetVariableArgs(sstmt);
 
 			if (valuestr)
-				a = GUCArrayAdd(a, sstmt->name, valuestr);
+				a = GUCArrayAdd(a, sstmt->name, valuestr, PGC_S_TEST_FUNCTION);
 			else				/* RESET */
-				a = GUCArrayDelete(a, sstmt->name);
+				a = GUCArrayDelete(a, sstmt->name, PGC_S_TEST_FUNCTION);
 		}
 	}
 
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 113b4807315..b0a460c1622 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1104,7 +1104,7 @@ check_default_tablespace(char **newval, void **extra, GucSource source)
 			 * When source == PGC_S_TEST, don't throw a hard error for a
 			 * nonexistent tablespace, only a NOTICE.  See comments in guc.h.
 			 */
-			if (source == PGC_S_TEST)
+			if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 			{
 				ereport(NOTICE,
 						(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -1251,7 +1251,7 @@ check_temp_tablespaces(char **newval, void **extra, GucSource source)
 			curoid = get_tablespace_oid(curname, source <= PGC_S_TEST);
 			if (curoid == InvalidOid)
 			{
-				if (source == PGC_S_TEST)
+				if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 					ereport(NOTICE,
 							(errcode(ERRCODE_UNDEFINED_OBJECT),
 							 errmsg("tablespace \"%s\" does not exist",
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 9345131711e..c9421b17952 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -831,7 +831,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
 	roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
 	if (!HeapTupleIsValid(roleTup))
 	{
-		if (source == PGC_S_TEST)
+		if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 		{
 			ereport(NOTICE,
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -856,7 +856,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
 	if (roleid != GetAuthenticatedUserId() &&
 		!superuser_arg(GetAuthenticatedUserId()))
 	{
-		if (source == PGC_S_TEST)
+		if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 		{
 			ereport(NOTICE,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -940,7 +940,7 @@ check_role(char **newval, void **extra, GucSource source)
 		roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
 		if (!HeapTupleIsValid(roleTup))
 		{
-			if (source == PGC_S_TEST)
+			if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 			{
 				ereport(NOTICE,
 						(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -965,7 +965,7 @@ check_role(char **newval, void **extra, GucSource source)
 		if (!InitializingParallelWorker &&
 			!member_can_set_role(GetSessionUserId(), roleid))
 		{
-			if (source == PGC_S_TEST)
+			if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 			{
 				ereport(NOTICE,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 8da7dd6c98a..0e92f604061 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -707,7 +707,7 @@ check_temp_buffers(int *newval, void **extra, GucSource source)
 	 * Once local buffers have been initialized, it's too late to change this.
 	 * However, if this is only a test call, allow it.
 	 */
-	if (source != PGC_S_TEST && NLocBuffer && NLocBuffer != *newval)
+	if ((source != PGC_S_TEST && source != PGC_S_TEST_FUNCTION) && NLocBuffer && NLocBuffer != *newval)
 	{
 		GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temporary tables have been accessed in the session.");
 		return false;
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 54de33eadd2..9d7e8b66763 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -628,7 +628,7 @@ check_default_text_search_config(char **newval, void **extra, GucSource source)
 		 */
 		if (!OidIsValid(cfgId))
 		{
-			if (source == PGC_S_TEST)
+			if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION)
 			{
 				ereport(NOTICE,
 						(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a043d529efa..e2734475189 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -252,7 +252,7 @@ static void reapply_stacked_values(struct config_generic *variable,
 								   GucContext curscontext, GucSource cursource,
 								   Oid cursrole);
 static bool validate_option_array_item(const char *name, const char *value,
-									   bool skipIfNoPermissions);
+									   bool skipIfNoPermissions, int source);
 static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head);
 static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 									  const char *name, const char *value);
@@ -6444,7 +6444,7 @@ ProcessGUCArray(ArrayType *array,
  * to indicate the current table entry is NULL.
  */
 ArrayType *
-GUCArrayAdd(ArrayType *array, const char *name, const char *value)
+GUCArrayAdd(ArrayType *array, const char *name, const char *value, int source)
 {
 	struct config_generic *record;
 	Datum		datum;
@@ -6455,7 +6455,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
 	Assert(value);
 
 	/* test if the option is valid and we're allowed to set it */
-	(void) validate_option_array_item(name, value, false);
+	(void) validate_option_array_item(name, value, false, source);
 
 	/* normalize name (converts obsolete GUC names to modern spellings) */
 	record = find_option(name, false, true, WARNING);
@@ -6522,7 +6522,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
  * is NULL then a null should be stored.
  */
 ArrayType *
-GUCArrayDelete(ArrayType *array, const char *name)
+GUCArrayDelete(ArrayType *array, const char *name, int source)
 {
 	struct config_generic *record;
 	ArrayType  *newarray;
@@ -6532,7 +6532,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
 	Assert(name);
 
 	/* test if the option is valid and we're allowed to set it */
-	(void) validate_option_array_item(name, NULL, false);
+	(void) validate_option_array_item(name, NULL, false, source);
 
 	/* normalize name (converts obsolete GUC names to modern spellings) */
 	record = find_option(name, false, true, WARNING);
@@ -6592,7 +6592,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
  * those that are PGC_USERSET or we have permission to set
  */
 ArrayType *
-GUCArrayReset(ArrayType *array)
+GUCArrayReset(ArrayType *array, int source)
 {
 	ArrayType  *newarray;
 	int			i;
@@ -6630,7 +6630,7 @@ GUCArrayReset(ArrayType *array)
 		*eqsgn = '\0';
 
 		/* skip if we have permission to delete it */
-		if (validate_option_array_item(val, NULL, true))
+		if (validate_option_array_item(val, NULL, true, source))
 			continue;
 
 		/* else add it to the output array */
@@ -6665,7 +6665,7 @@ GUCArrayReset(ArrayType *array)
  */
 static bool
 validate_option_array_item(const char *name, const char *value,
-						   bool skipIfNoPermissions)
+						   bool skipIfNoPermissions, int source)
 
 {
 	struct config_generic *gconf;
@@ -6726,7 +6726,7 @@ validate_option_array_item(const char *name, const char *value,
 	/* test for permissions and valid option value */
 	(void) set_config_option(name, value,
 							 superuser() ? PGC_SUSET : PGC_USERSET,
-							 PGC_S_TEST, GUC_ACTION_SET, false, 0, false);
+							 source, GUC_ACTION_SET, false, 0, false);
 
 	return true;
 }
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ff506bf48d9..84203109af7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -119,6 +119,7 @@ typedef enum
 	PGC_S_OVERRIDE,				/* special case to forcibly set default */
 	PGC_S_INTERACTIVE,			/* dividing line for error reporting */
 	PGC_S_TEST,					/* test per-database or per-user setting */
+	PGC_S_TEST_FUNCTION,			/* function SET */
 	PGC_S_SESSION,				/* SET command */
 } GucSource;
 
@@ -406,9 +407,9 @@ extern void TransformGUCArray(ArrayType *array, List **names,
 							  List **values);
 extern void ProcessGUCArray(ArrayType *array,
 							GucContext context, GucSource source, GucAction action);
-extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
-extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
-extern ArrayType *GUCArrayReset(ArrayType *array);
+extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value, int source);
+extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name, int source);
+extern ArrayType *GUCArrayReset(ArrayType *array, int source);
 
 extern void *guc_malloc(int elevel, size_t size);
 extern pg_nodiscard void *guc_realloc(int elevel, void *old, size_t size);
-- 
2.42.0

