Custom GUCs and typos

Started by Srinath Reddy Sadipiralla8 months ago2 messages
#1Srinath Reddy Sadipiralla
srinath2133@gmail.com
1 attachment(s)

Hi,
while going through this thread [0]/messages/by-id/196f3f8e12f.87666a6c16169.9160742057750715009@zohocorp.com -- Thanks, Srinath Reddy Sadipiralla EDB: https://www.enterprisedb.com/ i have some different views and
questions on this problem so creating a separate thread,this is just an
attempt, i might be brutally wrong here.

1) On top of OP's patch I added support to warn if the prefix of custom GUC
is invalid,for valid questions such as "How do you know that's a bogus
prefix? It could perfectly well be a fully valid setting for an extension
that the installation doesn't choose to preload.",we can get the info of
such extensions using extension_file_exists() which tells that its
installed but not preloaded thanks to Greg for proposing this,which tells
this could be a potential valid extension ,so if its not it in
reserved_class_prefix and also not in prefix/share/extension dir then the
prefix is for sure invalid,so i warn and remove the GUC from hashtable.

2) for preloaded extensions if the suffix/parameter is a typo or if we want
to create a new custom GUC for ex:"pg_stat_statements.count_something = 1",
currently we throwing a warning as

WARNING: invalid configuration parameter name
"pg_stat_statements.count_something", removing it
"pg_stat_statements" is now a reserved prefix.

I guess this means you cant create a new custom GUC manually ,you have to
use "DefineCustomXXXVariable" routines,but this kind of warning is not
there for non-preloaded extensions ,so i added the same for non-preloaded
ones ,once again checking the extension_file_exists() if the extension was
built/installed this could make it a potential valid extension so it warns
as

if i try add this "plpgsql.ironman = 3000"

WARNING: invalid configuration parameter name "plpgsql.ironman", removing
it
DETAIL: "plpgsql.ironman" has a valid prefix.

3) I also added all the above support for the SET command also which
previously was not there.
4) TODO: add support for ALTER SYSTEM SET ,if this patch makes sense.

thoughts?

[0]: /messages/by-id/196f3f8e12f.87666a6c16169.9160742057750715009@zohocorp.com -- Thanks, Srinath Reddy Sadipiralla EDB: https://www.enterprisedb.com/
/messages/by-id/196f3f8e12f.87666a6c16169.9160742057750715009@zohocorp.com
--
Thanks,
Srinath Reddy Sadipiralla
EDB: https://www.enterprisedb.com/

Attachments:

v1-0001-Reject-and-warn-invalid-custom-GUCs.patchapplication/octet-stream; name=v1-0001-Reject-and-warn-invalid-custom-GUCs.patchDownload
From 11f89703d451e9e2a9f5bf63c13c100703f2097a Mon Sep 17 00:00:00 2001
From: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Date: Mon, 26 May 2025 06:39:56 +0530
Subject: [PATCH 1/1] Reject and warn invalid custom GUCs

---
 src/backend/postmaster/postmaster.c |   2 +
 src/backend/utils/misc/guc.c        | 133 +++++++++++++++++++++++++---
 src/backend/utils/misc/guc_funcs.c  |   2 +
 src/include/utils/guc.h             |   1 +
 4 files changed, 126 insertions(+), 12 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 490f7ce3664..c51d2a00ca2 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -931,6 +931,8 @@ PostmasterMain(int argc, char *argv[])
 	 */
 	process_shared_preload_libraries();
 
+	WarnAndRemoveInvalidGUCs();
+
 	/*
 	 * Initialize SSL library, if specified.
 	 */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 667df448732..75b5a14f9e5 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -50,6 +50,7 @@
 #include "utils/guc_tables.h"
 #include "utils/memutils.h"
 #include "utils/timestamp.h"
+#include "commands/extension.h"
 
 
 #define CONFIG_FILENAME "postgresql.conf"
@@ -254,6 +255,7 @@ static bool validate_option_array_item(const char *name, const char *value,
 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);
+static bool has_valid_class_prefix(const char *name,bool check_for_preloaded);
 static bool valid_custom_variable_name(const char *name);
 static bool assignable_custom_variable_name(const char *name, bool skip_errors,
 											int elevel);
@@ -1065,6 +1067,61 @@ add_guc_variable(struct config_generic *var, int elevel)
 	return true;
 }
 
+/*
+ * Check if the given GUC name has a valid class prefix.
+ *
+ * A valid class prefix is defined as a known reserved prefix
+ * (e.g., "pg_stat_statements") and also ones which are built
+ * and not preloaded (e.g., "plpgsql").
+ *
+ * This function extracts the portion of the name before the
+ * first '.' and compares it against the list of reserved prefixes.
+ *
+ * Returns true if a valid prefix is found; false otherwise.
+ */
+static bool
+has_valid_class_prefix(const char *name,bool check_for_preloaded){
+	/* If there's no separator, it can't be a custom variable */
+	const char *sep = strchr(name, GUC_QUALIFIER_SEPARATOR);
+	size_t class_len;
+
+	if (sep == NULL)
+		return false;
+
+	class_len = sep - name;
+
+	if(check_for_preloaded)
+	{
+		ListCell *lc;
+
+		foreach(lc, reserved_class_prefix)
+		{
+			const char *rcprefix = lfirst(lc);
+
+			if (strlen(rcprefix) == class_len &&
+				strncmp(name, rcprefix, class_len) == 0)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	else
+	{
+
+		char *prefix = palloc(class_len + 1);
+		bool exists;
+		memcpy(prefix, name, class_len);
+		prefix[class_len] = '\0';
+
+		exists = extension_file_exists(prefix);
+		pfree(prefix);
+
+		return exists;
+	}
+
+}
+
 /*
  * Decide whether a proposed custom variable name is allowed.
  *
@@ -5269,13 +5326,22 @@ DefineCustomEnumVariable(const char *name,
 }
 
 /*
- * Mark the given GUC prefix as "reserved".
+ * remove_gucvar
  *
- * This deletes any existing placeholders matching the prefix,
- * and then prevents new ones from being created.
- * Extensions should call this after they've defined all of their custom
- * GUCs, to help catch misspelled config-file entries.
+ * Removes a GUC variable from the GUC hash table,
+ * and also from any associated internal lists.
  */
+static inline void remove_gucvar(struct config_generic *gucvar)
+{
+	/* Remove it from the hash table */
+	hash_search(guc_hashtab,
+				&gucvar->name,
+				HASH_REMOVE,
+				NULL);
+	/* Remove it from any lists it's in, too */
+	RemoveGUCFromLists(gucvar);
+}
+
 void
 MarkGUCPrefixReserved(const char *className)
 {
@@ -5305,13 +5371,7 @@ MarkGUCPrefixReserved(const char *className)
 							var->name),
 					 errdetail("\"%s\" is now a reserved prefix.",
 							   className)));
-			/* Remove it from the hash table */
-			hash_search(guc_hashtab,
-						&var->name,
-						HASH_REMOVE,
-						NULL);
-			/* Remove it from any lists it's in, too */
-			RemoveGUCFromLists(var);
+			remove_gucvar(var);
 		}
 	}
 
@@ -5321,6 +5381,55 @@ MarkGUCPrefixReserved(const char *className)
 	MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * WarnAndRemoveInvalidGUCs
+ *
+ * Iterates over the GUC hash table and identifies any custom placeholders
+ * (i.e., parameters not known at compile time) that do not follow the expected
+ * naming convention (i.e., do not have a valid reserved prefix) for both preloaded
+ * and the just built,not preloaded extension GUCs.
+ *
+ * For each such invalid GUC, a warning is emitted and the GUC is removed from
+ * the system. This helps catch configuration mistakes such as typos in
+ * extension-specific GUC names, which would otherwise be silently ignored.
+ */
+void WarnAndRemoveInvalidGUCs(){
+	HASH_SEQ_STATUS status;
+	GUCHashEntry *hentry;
+
+	/* Warn and remove invalid placeholders. */
+	hash_seq_init(&status, guc_hashtab);
+	while ((hentry = (GUCHashEntry *) hash_seq_search(&status)) != NULL)
+	{
+		struct config_generic *var = hentry->gucvar;
+
+		if((var->flags & GUC_CUSTOM_PLACEHOLDER) != 0){
+			bool is_valid_prefix =
+				has_valid_class_prefix(var->name, true) ||
+				has_valid_class_prefix(var->name, false);
+
+			if (!is_valid_prefix)
+			{
+				ereport(WARNING,
+						(errcode(ERRCODE_INVALID_NAME),
+						errmsg("invalid configuration parameter name \"%s\", removing it",
+								var->name),
+						errdetail("\"%s\" has a invalid prefix.", var->name)));
+			}
+			else
+			{
+				ereport(WARNING,
+						(errcode(ERRCODE_INVALID_NAME),
+						errmsg("invalid configuration parameter name \"%s\", removing it",
+								var->name),
+						errdetail("\"%s\" has a valid prefix.", var->name)));
+			}
+
+			remove_gucvar(var);
+		}
+	}
+}
+
 
 /*
  * Return an array of modified GUC options to show in EXPLAIN.
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c
index b9e26982abd..03e18bc5e99 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -152,6 +152,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
 			break;
 	}
 
+	WarnAndRemoveInvalidGUCs();
+
 	/* Invoke the post-alter hook for setting this GUC variable, by name. */
 	InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
 									ACL_SET, stmt->kind, false);
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f619100467d..7de00f63def 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -390,6 +390,7 @@ extern void DefineCustomEnumVariable(const char *name,
 									 GucShowHook show_hook) pg_attribute_nonnull(1, 4);
 
 extern void MarkGUCPrefixReserved(const char *className);
+extern void WarnAndRemoveInvalidGUCs(void);
 
 /* old name for MarkGUCPrefixReserved, for backwards compatibility: */
 #define EmitWarningsOnPlaceholders(className) MarkGUCPrefixReserved(className)
-- 
2.43.0

#2David G. Johnston
david.g.johnston@gmail.com
In reply to: Srinath Reddy Sadipiralla (#1)
Re: Custom GUCs and typos

On Sun, May 25, 2025 at 7:52 PM Srinath Reddy Sadipiralla <
srinath2133@gmail.com> wrote:

1) On top of OP's patch I added support to warn if the prefix of custom
GUC is invalid,for valid questions such as "How do you know that's a bogus
prefix? It could perfectly well be a fully valid setting for an extension
that the installation doesn't choose to preload.",we can get the info of
such extensions using extension_file_exists() which tells that its
installed but not preloaded thanks to Greg for proposing this,which tells
this could be a potential valid extension ,so if its not it in
reserved_class_prefix and also not in prefix/share/extension dir then the
prefix is for sure invalid,so i warn and remove the GUC from hashtable.

People can and do create GUCs without any intention or need to write C code
or place files onto the server to make use of them.

https://www.postgresql.org/docs/current/runtime-config-custom.html

"PostgreSQL will accept a setting for any two-part parameter name."

We cannot change this. So, please include discussion of their existence in
any description of changes you wish to make in this area.

David J.