Warning on SET of nonexisting setting with a prefix reserved by an extension
authorPeter Eisentraut <[email protected]>
Wed, 1 Dec 2021 14:07:29 +0000 (15:07 +0100)
committerPeter Eisentraut <[email protected]>
Wed, 1 Dec 2021 14:08:32 +0000 (15:08 +0100)
An extension can already de facto reserve a GUC prefix using
EmitWarningsOnPlaceholders().  But this was only checked against
settings that exist at the time the extension is loaded (or the
extension chooses to call this).  No diagnostic is given when a SET
command later uses a nonexisting setting with a custom prefix.

With this change, EmitWarningsOnPlaceholders() saves the prefixes it
reserves in a list, and SET checks when it finds a "placeholder"
setting whether it belongs to a reserved prefix and issues a warning
in that case.

Add a regression test that checks the patch using the "plpgsql"
registered prefix.

Author: Florin Irion <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/CA+HEvJDhWuuTpGTJT9Tgbdzm4QS4EzPAwDBScWK18H2Q=FVJFw@mail.gmail.com

src/backend/utils/misc/guc.c
src/test/regress/expected/guc.out
src/test/regress/sql/guc.sql

index 25ac4ca85bafbd4c1220419f118e5c7bb91de175..a292498e2f763c3b54d4d2f536c11327d947db67 100644 (file)
@@ -235,6 +235,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 check_reserved_prefixes(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,
@@ -8755,6 +8757,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
                                                                         (superuser() ? PGC_SUSET : PGC_USERSET),
                                                                         PGC_S_SESSION,
                                                                         action, true, 0, false);
+                       check_reserved_prefixes(stmt->name);
                        break;
                case VAR_SET_MULTI:
 
@@ -8840,6 +8843,8 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
                                                                         (superuser() ? PGC_SUSET : PGC_USERSET),
                                                                         PGC_S_SESSION,
                                                                         action, true, 0, false);
+
+                       check_reserved_prefixes(stmt->name);
                        break;
                case VAR_RESET_ALL:
                        ResetAllOptions();
@@ -9326,6 +9331,7 @@ EmitWarningsOnPlaceholders(const char *className)
 {
        int                     classLen = strlen(className);
        int                     i;
+       MemoryContext   oldcontext;
 
        for (i = 0; i < num_guc_variables; i++)
        {
@@ -9341,8 +9347,49 @@ EmitWarningsOnPlaceholders(const char *className)
                                                        var->name)));
                }
        }
+
+       oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+       reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
+       MemoryContextSwitchTo(oldcontext);
 }
 
+/*
+ * Check a setting name against prefixes previously reserved by
+ * EmitWarningsOnPlaceholders() and throw a warning if matching.
+ */
+static void
+check_reserved_prefixes(const char *varName)
+{
+       char       *sep = strchr(varName, GUC_QUALIFIER_SEPARATOR);
+
+       if (sep)
+       {
+               size_t          classLen = sep - varName;
+               ListCell   *lc;
+
+               foreach(lc, reserved_class_prefix)
+               {
+                       char       *rcprefix = 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 &&
+                                               strcmp(varName, var->name) == 0)
+                                       {
+                                               ereport(WARNING,
+                                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                                errmsg("unrecognized configuration parameter \"%s\"", var->name),
+                                                                errdetail("\"%.*s\" is a reserved prefix.", (int) classLen, var->name)));
+                                       }
+                               }
+                       }
+               }
+       }
+}
 
 /*
  * SHOW command
index 59da91ff04de6854dbc549e2d6b76ab0ac79f4c7..2433038c3ec492a885138f4262a883c063745fc5 100644 (file)
@@ -813,3 +813,22 @@ 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 unrecognized 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.
+SHOW plpgsql.extra_foo_warnings;  -- but the parameter is 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.
index c39c11388d51f9d54952107bfe1b44aa9a7bc378..b57758ed273c7a987b756641f0421a01abe63c47 100644 (file)
@@ -311,3 +311,14 @@ 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 unrecognized 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;  -- but the parameter is set
+
+-- cleanup
+RESET foo;
+RESET plpgsql.extra_foo_warnings;