Hi, 

I adjusted a bit the code and configured my mail client to
send patch attachments appropiately(hopefully). :)  

So here is my second version.

Cheers, 
Florin Irion
From 9a50261faad3a7578dab150f1c05b510285e283c Mon Sep 17 00:00:00 2001
From: Florin Irion <florin.ir...@enterprisedb.com>
Date: Thu, 7 Oct 2021 09:51:29 +0200
Subject: [PATCH] Reserve the prefix for each extension

Throw a warning when we `SET` a variable that doesn't exist with
a reserved prefix.  This will protect against setting incorrect
configurations for any extension, like we do with the `GUC`s.

Add a regression test that checks the patch using the "plpgsql"
registered prefix.
---
 src/backend/utils/misc/guc.c      | 49 +++++++++++++++++++++++++++++++
 src/test/regress/expected/guc.out | 21 +++++++++++++
 src/test/regress/sql/guc.sql      | 10 +++++++
 3 files changed, 80 insertions(+)

diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6652a60ec3..f19310df44 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -234,6 +234,8 @@ static bool check_recovery_target_lsn(char **newval, void 
**extra, GucSource sou
 static void assign_recovery_target_lsn(const char *newval, void *extra);
 static bool check_primary_slot_name(char **newval, void **extra, GucSource 
source);
 static bool check_default_with_oids(bool *newval, void **extra, GucSource 
source);
+static void EmitWarningsOnSetPlaceholders(const char *varName);
+static List *reserved_class_prefix = NIL;
 
 /* Private functions in guc-file.l that need to be called from guc.c */
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
@@ -8703,6 +8705,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool 
isTopLevel)
                                                                         
(superuser() ? PGC_SUSET : PGC_USERSET),
                                                                         
PGC_S_SESSION,
                                                                         
action, true, 0, false);
+
+                       EmitWarningsOnSetPlaceholders(stmt->name);
                        break;
                case VAR_SET_MULTI:
 
@@ -8789,6 +8793,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool 
isTopLevel)
                                                                         
(superuser() ? PGC_SUSET : PGC_USERSET),
                                                                         
PGC_S_SESSION,
                                                                         
action, true, 0, false);
+
+                       EmitWarningsOnSetPlaceholders(stmt->name);
                        break;
                case VAR_RESET_ALL:
                        ResetAllOptions();
@@ -9275,6 +9281,7 @@ EmitWarningsOnPlaceholders(const char *className)
 {
        int                     classLen = strlen(className);
        int                     i;
+       MemoryContext   oldcontext;
 
        for (i = 0; i < num_guc_variables; i++)
        {
@@ -9290,8 +9297,50 @@ EmitWarningsOnPlaceholders(const char *className)
                                                        var->name)));
                }
        }
+       oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+       reserved_class_prefix = lappend(reserved_class_prefix, 
pstrdup(className));
+       MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * Throw a warning if a user creates a placeholder with
+ * the "SET" command and we don't recognize it.
+ */
+static void
+EmitWarningsOnSetPlaceholders(const char *varName)
+{
+       char    *gucQualifierSeparator = strchr(varName, 
GUC_QUALIFIER_SEPARATOR);
+
+       if (gucQualifierSeparator)
+       {
+               unsigned int    classLen = gucQualifierSeparator - varName;
+               unsigned int    varLen = strlen(varName);
+               ListCell           *lc;
+
+               foreach(lc, reserved_class_prefix)
+               {
+                       char    *rcprefix = (char *) lfirst(lc);
+
+                       if (strncmp(varName, rcprefix, classLen) == 0)
+                       {
+                               for (int i = 0; i < num_guc_variables; i++)
+                               {
+                                       struct config_generic *var = 
guc_variables[i];
+
+                                       if ((var->flags & 
GUC_CUSTOM_PLACEHOLDER) != 0 &&
+                                               strncmp(varName, var->name, 
varLen)==0)
+                                       {
+                                               ereport(WARNING,
+                                                               
(errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                                
errmsg("unrecognized configuration parameter \"%s\"", var->name),
+                                                                
errdetail("\"%.*s\" is a reserved prefix.", classLen, var->name),
+                                                                errhint("If 
you need to create a custom placeholder use a different prefix.")));
+                                       }
+                               }
+                       }
+               }
+       }
+}
 
 /*
  * SHOW command
diff --git a/src/test/regress/expected/guc.out 
b/src/test/regress/expected/guc.out
index 59da91ff04..e5049bed19 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -813,3 +813,24 @@ set default_with_oids to f;
 -- Should not allow to set it to true.
 set default_with_oids to t;
 ERROR:  tables declared WITH OIDS are not supported
+-- test SET unrecongnized parameter
+SET foo = FALSE; -- no such setting
+ERROR:  unrecognized configuration parameter "foo"
+-- test setting a parameter with a registered prefix (plpgsql)
+SET plpgsql.extra_foo_warnings = FALSE; -- no such setting
+WARNING:  unrecognized configuration parameter "plpgsql.extra_foo_warnings"
+DETAIL:  "plpgsql" is a reserved prefix.
+HINT:  If you need to create a custom placeholder use a different prefix.
+SHOW plpgsql.extra_foo_warnings; -- the parameter is however set
+ plpgsql.extra_foo_warnings 
+----------------------------
+ false
+(1 row)
+
+-- cleanup
+RESET foo;
+ERROR:  unrecognized configuration parameter "foo"
+RESET plpgsql.extra_foo_warnings;
+WARNING:  unrecognized configuration parameter "plpgsql.extra_foo_warnings"
+DETAIL:  "plpgsql" is a reserved prefix.
+HINT:  If you need to create a custom placeholder use a different prefix.
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index c39c11388d..d9669d6e5a 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -311,3 +311,13 @@ reset check_function_bodies;
 set default_with_oids to f;
 -- Should not allow to set it to true.
 set default_with_oids to t;
+
+-- test SET unrecongnized parameter
+SET foo = FALSE; -- no such setting
+
+-- test setting a parameter with a registered prefix (plpgsql)
+SET plpgsql.extra_foo_warnings = FALSE; -- no such setting
+SHOW plpgsql.extra_foo_warnings; -- the parameter is however set
+-- cleanup
+RESET foo;
+RESET plpgsql.extra_foo_warnings;
\ No newline at end of file
-- 
2.31.1

Reply via email to