Disallow setting bogus GUCs within an extension's reserved namespace.
Commit75d22069e
tried to throw a warning for setting a custom GUC whose prefix belongs to a previously-loaded extension, if there is no such GUC defined by the extension. But that caused unstable behavior with parallel workers, because workers don't necessarily load extensions and GUCs in the same order their leader did. To make that work safely, we have to completely disallow the case. We now actually remove any such GUCs at the time of initial extension load, and then throw an error not just a warning if you try to add one later. While this might create a compatibility issue for a few people, the improvement in error-detection capability seems worth it; it's hard to believe that there's any good use-case for choosing such GUC names. This also un-reverts5609cc01c
(Rename EmitWarningsOnPlaceholders() to MarkGUCPrefixReserved()), since that function's old name is now even more of a misnomer. Florin Irion and Tom Lane Discussion: https://postgr.es/m/1902182.1640711215@sss.pgh.pa.us
This commit is contained in:
parent
2776922201
commit
88103567cb
|
@ -68,7 +68,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("auth_delay");
|
||||
MarkGUCPrefixReserved("auth_delay");
|
||||
|
||||
/* Install Hooks */
|
||||
original_client_auth_hook = ClientAuthentication_hook;
|
||||
|
|
|
@ -231,7 +231,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("auto_explain");
|
||||
MarkGUCPrefixReserved("auto_explain");
|
||||
|
||||
/* Install hooks. */
|
||||
prev_ExecutorStart = ExecutorStart_hook;
|
||||
|
|
|
@ -69,7 +69,7 @@ _PG_init(void)
|
|||
0,
|
||||
check_archive_directory, NULL, NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("basic_archive");
|
||||
MarkGUCPrefixReserved("basic_archive");
|
||||
|
||||
basic_archive_context = AllocSetContextCreate(TopMemoryContext,
|
||||
"basic_archive",
|
||||
|
|
|
@ -137,7 +137,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("pg_prewarm");
|
||||
MarkGUCPrefixReserved("pg_prewarm");
|
||||
|
||||
RequestAddinShmemSpace(MAXALIGN(sizeof(AutoPrewarmSharedState)));
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("pg_stat_statements");
|
||||
MarkGUCPrefixReserved("pg_stat_statements");
|
||||
|
||||
/*
|
||||
* Request additional shared resources. (These are no-ops if we're not in
|
||||
|
|
|
@ -101,7 +101,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("pg_trgm");
|
||||
MarkGUCPrefixReserved("pg_trgm");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -538,5 +538,5 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("postgres_fdw");
|
||||
MarkGUCPrefixReserved("postgres_fdw");
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("sepgsql");
|
||||
MarkGUCPrefixReserved("sepgsql");
|
||||
|
||||
/* Initialize userspace access vector cache */
|
||||
sepgsql_avc_init();
|
||||
|
|
|
@ -150,6 +150,8 @@ extern bool optimize_bounded_sort;
|
|||
|
||||
static int GUC_check_errcode_value;
|
||||
|
||||
static List *reserved_class_prefix = NIL;
|
||||
|
||||
/* global variables for check hook support */
|
||||
char *GUC_check_errmsg_string;
|
||||
char *GUC_check_errdetail_string;
|
||||
|
@ -5590,18 +5592,44 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
|
|||
* doesn't contain a separator, don't assume that it was meant to be a
|
||||
* placeholder.
|
||||
*/
|
||||
if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL)
|
||||
const char *sep = strchr(name, GUC_QUALIFIER_SEPARATOR);
|
||||
|
||||
if (sep != NULL)
|
||||
{
|
||||
if (valid_custom_variable_name(name))
|
||||
return add_placeholder_variable(name, elevel);
|
||||
/* A special error message seems desirable here */
|
||||
if (!skip_errors)
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("invalid configuration parameter name \"%s\"",
|
||||
name),
|
||||
errdetail("Custom parameter names must be two or more simple identifiers separated by dots.")));
|
||||
return NULL;
|
||||
size_t classLen = sep - name;
|
||||
ListCell *lc;
|
||||
|
||||
/* The name must be syntactically acceptable ... */
|
||||
if (!valid_custom_variable_name(name))
|
||||
{
|
||||
if (!skip_errors)
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("invalid configuration parameter name \"%s\"",
|
||||
name),
|
||||
errdetail("Custom parameter names must be two or more simple identifiers separated by dots.")));
|
||||
return NULL;
|
||||
}
|
||||
/* ... and it must not match any previously-reserved prefix */
|
||||
foreach(lc, reserved_class_prefix)
|
||||
{
|
||||
const char *rcprefix = lfirst(lc);
|
||||
|
||||
if (strlen(rcprefix) == classLen &&
|
||||
strncmp(name, rcprefix, classLen) == 0)
|
||||
{
|
||||
if (!skip_errors)
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("invalid configuration parameter name \"%s\"",
|
||||
name),
|
||||
errdetail("\"%s\" is a reserved prefix.",
|
||||
rcprefix)));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* OK, create it */
|
||||
return add_placeholder_variable(name, elevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9355,15 +9383,26 @@ DefineCustomEnumVariable(const char *name,
|
|||
}
|
||||
|
||||
/*
|
||||
* Mark the given GUC prefix as "reserved".
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void
|
||||
EmitWarningsOnPlaceholders(const char *className)
|
||||
MarkGUCPrefixReserved(const char *className)
|
||||
{
|
||||
int classLen = strlen(className);
|
||||
int i;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/*
|
||||
* Check for existing placeholders. We must actually remove invalid
|
||||
* placeholders, else future parallel worker startups will fail. (We
|
||||
* don't bother trying to free associated memory, since this shouldn't
|
||||
* happen often.)
|
||||
*/
|
||||
for (i = 0; i < num_guc_variables; i++)
|
||||
{
|
||||
struct config_generic *var = guc_variables[i];
|
||||
|
@ -9373,11 +9412,21 @@ EmitWarningsOnPlaceholders(const char *className)
|
|||
var->name[classLen] == GUC_QUALIFIER_SEPARATOR)
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("unrecognized configuration parameter \"%s\"",
|
||||
var->name)));
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("invalid configuration parameter name \"%s\", removing it",
|
||||
var->name),
|
||||
errdetail("\"%s\" is now a reserved prefix.",
|
||||
className)));
|
||||
num_guc_variables--;
|
||||
memmove(&guc_variables[i], &guc_variables[i + 1],
|
||||
(num_guc_variables - i) * sizeof(struct config_generic *));
|
||||
}
|
||||
}
|
||||
|
||||
/* And remember the name so we can prevent future mistakes. */
|
||||
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
||||
reserved_class_prefix = lappend(reserved_class_prefix, pstrdup(className));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -354,7 +354,10 @@ extern void DefineCustomEnumVariable(const char *name,
|
|||
GucEnumAssignHook assign_hook,
|
||||
GucShowHook show_hook);
|
||||
|
||||
extern void EmitWarningsOnPlaceholders(const char *className);
|
||||
extern void MarkGUCPrefixReserved(const char *className);
|
||||
|
||||
/* old name for MarkGUCPrefixReserved, for backwards compatibility: */
|
||||
#define EmitWarningsOnPlaceholders(className) MarkGUCPrefixReserved(className)
|
||||
|
||||
extern const char *GetConfigOption(const char *name, bool missing_ok,
|
||||
bool restrict_privileged);
|
||||
|
|
|
@ -455,7 +455,7 @@ _PG_init(void)
|
|||
PGC_SUSET, 0,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("plperl");
|
||||
MarkGUCPrefixReserved("plperl");
|
||||
|
||||
/*
|
||||
* Create hash tables.
|
||||
|
|
|
@ -197,7 +197,7 @@ _PG_init(void)
|
|||
plpgsql_extra_errors_assign_hook,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("plpgsql");
|
||||
MarkGUCPrefixReserved("plpgsql");
|
||||
|
||||
plpgsql_HashTableInit();
|
||||
RegisterXactCallback(plpgsql_xact_cb, NULL);
|
||||
|
|
|
@ -474,8 +474,8 @@ _PG_init(void)
|
|||
PGC_SUSET, 0,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("pltcl");
|
||||
EmitWarningsOnPlaceholders("pltclu");
|
||||
MarkGUCPrefixReserved("pltcl");
|
||||
MarkGUCPrefixReserved("pltclu");
|
||||
|
||||
pltcl_pm_init_done = true;
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("delay_execution");
|
||||
MarkGUCPrefixReserved("delay_execution");
|
||||
|
||||
/* Install our hook */
|
||||
prev_planner_hook = planner_hook;
|
||||
|
|
|
@ -49,7 +49,7 @@ _PG_init(void)
|
|||
NULL,
|
||||
NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("ssl_passphrase");
|
||||
MarkGUCPrefixReserved("ssl_passphrase");
|
||||
|
||||
if (ssl_passphrase)
|
||||
openssl_tls_init_hook = set_rot13;
|
||||
|
|
|
@ -322,7 +322,7 @@ _PG_init(void)
|
|||
0,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
EmitWarningsOnPlaceholders("worker_spi");
|
||||
MarkGUCPrefixReserved("worker_spi");
|
||||
|
||||
/* set up common data for all our workers */
|
||||
memset(&worker, 0, sizeof(worker));
|
||||
|
|
|
@ -548,6 +548,17 @@ ERROR: invalid configuration parameter name "special.weird name"
|
|||
DETAIL: Custom parameter names must be two or more simple identifiers separated by dots.
|
||||
SHOW special."weird name";
|
||||
ERROR: unrecognized configuration parameter "special.weird name"
|
||||
-- Check what happens when you try to set a "custom" GUC within the
|
||||
-- namespace of an extension.
|
||||
SET plpgsql.extra_foo_warnings = true; -- allowed if plpgsql is not loaded yet
|
||||
LOAD 'plpgsql'; -- this will throw a warning and delete the variable
|
||||
WARNING: invalid configuration parameter name "plpgsql.extra_foo_warnings", removing it
|
||||
DETAIL: "plpgsql" is now a reserved prefix.
|
||||
SET plpgsql.extra_foo_warnings = true; -- now, it's an error
|
||||
ERROR: invalid configuration parameter name "plpgsql.extra_foo_warnings"
|
||||
DETAIL: "plpgsql" is a reserved prefix.
|
||||
SHOW plpgsql.extra_foo_warnings;
|
||||
ERROR: unrecognized configuration parameter "plpgsql.extra_foo_warnings"
|
||||
--
|
||||
-- Test DISCARD TEMP
|
||||
--
|
||||
|
|
|
@ -163,6 +163,13 @@ SHOW custom."bad-guc";
|
|||
SET special."weird name" = 'foo'; -- could be allowed, but we choose not to
|
||||
SHOW special."weird name";
|
||||
|
||||
-- Check what happens when you try to set a "custom" GUC within the
|
||||
-- namespace of an extension.
|
||||
SET plpgsql.extra_foo_warnings = true; -- allowed if plpgsql is not loaded yet
|
||||
LOAD 'plpgsql'; -- this will throw a warning and delete the variable
|
||||
SET plpgsql.extra_foo_warnings = true; -- now, it's an error
|
||||
SHOW plpgsql.extra_foo_warnings;
|
||||
|
||||
--
|
||||
-- Test DISCARD TEMP
|
||||
--
|
||||
|
|
Loading…
Reference in New Issue