aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/misc/guc-file.l4
-rw-r--r--src/backend/utils/misc/guc.c167
-rw-r--r--src/test/regress/expected/guc.out21
-rw-r--r--src/test/regress/sql/guc.sql10
4 files changed, 132 insertions, 70 deletions
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 7885a169bb9..9498bbea2f6 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -282,7 +282,7 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
* Try to find the variable; but do not create a custom placeholder if
* it's not there already.
*/
- record = find_option(item->name, false, elevel);
+ record = find_option(item->name, false, true, elevel);
if (record)
{
@@ -306,7 +306,7 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
/* Now mark it as present in file */
record->status |= GUC_IS_IN_FILE;
}
- else if (strchr(item->name, GUC_QUALIFIER_SEPARATOR) == NULL)
+ else if (!valid_custom_variable_name(item->name))
{
/* Invalid non-custom variable, so complain */
ereport(elevel,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c9c9da85f39..1b007ca85ca 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5335,6 +5335,45 @@ add_guc_variable(struct config_generic *var, int elevel)
}
/*
+ * Decide whether a proposed custom variable name is allowed.
+ *
+ * It must be "identifier.identifier", where the rules for what is an
+ * identifier agree with scan.l.
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int num_sep = 0;
+ bool name_start = true;
+
+ for (const char *p = name; *p; p++)
+ {
+ if (*p == GUC_QUALIFIER_SEPARATOR)
+ {
+ if (name_start)
+ return false; /* empty name component */
+ num_sep++;
+ name_start = true;
+ }
+ else if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz", *p) != NULL ||
+ IS_HIGHBIT_SET(*p))
+ {
+ /* okay as first or non-first character */
+ name_start = false;
+ }
+ else if (!name_start && strchr("0123456789_$", *p) != NULL)
+ /* okay as non-first character */ ;
+ else
+ return false;
+ }
+ if (name_start)
+ return false; /* empty name component */
+ /* OK if we had exactly one separator */
+ return (num_sep == 1);
+}
+
+/*
* Create and add a placeholder variable for a custom variable name.
*/
static struct config_generic *
@@ -5381,12 +5420,23 @@ add_placeholder_variable(const char *name, int elevel)
}
/*
- * Look up option NAME. If it exists, return a pointer to its record,
- * else return NULL. If create_placeholders is true, we'll create a
- * placeholder record for a valid-looking custom variable name.
+ * Look up option "name". If it exists, return a pointer to its record.
+ * Otherwise, if create_placeholders is true and name is a valid-looking
+ * custom variable name, we'll create and return a placeholder record.
+ * Otherwise, if skip_errors is true, then we silently return NULL for
+ * an unrecognized or invalid name. Otherwise, the error is reported at
+ * error level elevel (and we return NULL if that's less than ERROR).
+ *
+ * Note: internal errors, primarily out-of-memory, draw an elevel-level
+ * report and NULL return regardless of skip_errors. Hence, callers must
+ * handle a NULL return whenever elevel < ERROR, but they should not need
+ * to emit any additional error message. (In practice, internal errors
+ * can only happen when create_placeholders is true, so callers passing
+ * false need not think terribly hard about this.)
*/
static struct config_generic *
-find_option(const char *name, bool create_placeholders, int elevel)
+find_option(const char *name, bool create_placeholders, bool skip_errors,
+ int elevel)
{
const char **key = &name;
struct config_generic **res;
@@ -5414,19 +5464,38 @@ find_option(const char *name, bool create_placeholders, int elevel)
for (i = 0; map_old_guc_names[i] != NULL; i += 2)
{
if (guc_name_compare(name, map_old_guc_names[i]) == 0)
- return find_option(map_old_guc_names[i + 1], false, elevel);
+ return find_option(map_old_guc_names[i + 1], false,
+ skip_errors, elevel);
}
if (create_placeholders)
{
/*
- * Check if the name is qualified, and if so, add a placeholder.
+ * Check if the name is valid, and if so, add a placeholder. If it
+ * doesn't contain a separator, don't assume that it was meant to be a
+ * placeholder.
*/
if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL)
- return add_placeholder_variable(name, elevel);
+ {
+ 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 of the form \"identifier.identifier\".")));
+ return NULL;
+ }
}
/* Unknown name */
+ if (!skip_errors)
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"",
+ name)));
return NULL;
}
@@ -6444,7 +6513,7 @@ ReportChangedGUCOptions(void)
{
struct config_generic *record;
- record = find_option("in_hot_standby", false, ERROR);
+ record = find_option("in_hot_standby", false, false, ERROR);
Assert(record != NULL);
record->status |= GUC_NEEDS_REPORT;
report_needed = true;
@@ -7218,14 +7287,9 @@ set_config_option(const char *name, const char *value,
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
- record = find_option(name, true, elevel);
+ record = find_option(name, true, false, elevel);
if (record == NULL)
- {
- ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
return 0;
- }
/*
* Check if the option can be set at this time. See guc.h for the precise
@@ -7947,10 +8011,10 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
*/
elevel = IsUnderPostmaster ? DEBUG3 : LOG;
- record = find_option(name, true, elevel);
+ record = find_option(name, true, false, elevel);
/* should not happen */
if (record == NULL)
- elog(ERROR, "unrecognized configuration parameter \"%s\"", name);
+ return;
sourcefile = guc_strdup(elevel, sourcefile);
if (record->sourcefile)
@@ -7999,16 +8063,9 @@ GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, false, ERROR);
+ record = find_option(name, false, missing_ok, ERROR);
if (record == NULL)
- {
- if (missing_ok)
- return NULL;
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"",
- name)));
- }
+ return NULL;
if (restrict_privileged &&
(record->flags & GUC_SUPERUSER_ONLY) &&
!is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
@@ -8055,11 +8112,8 @@ GetConfigOptionResetString(const char *name)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, false, ERROR);
- if (record == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
+ record = find_option(name, false, false, ERROR);
+ Assert(record != NULL);
if ((record->flags & GUC_SUPERUSER_ONLY) &&
!is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
ereport(ERROR,
@@ -8103,16 +8157,9 @@ GetConfigOptionFlags(const char *name, bool missing_ok)
{
struct config_generic *record;
- record = find_option(name, false, WARNING);
+ record = find_option(name, false, missing_ok, ERROR);
if (record == NULL)
- {
- if (missing_ok)
- return 0;
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"",
- name)));
- }
+ return 0;
return record->flags;
}
@@ -8144,7 +8191,7 @@ flatten_set_variable_args(const char *name, List *args)
* Get flags for the variable; if it's not known, use default flags.
* (Caller might throw error later, but not our business to do so here.)
*/
- record = find_option(name, false, WARNING);
+ record = find_option(name, false, true, WARNING);
if (record)
flags = record->flags;
else
@@ -8439,12 +8486,8 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
struct config_generic *record;
- record = find_option(name, false, ERROR);
- if (record == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"",
- name)));
+ record = find_option(name, false, false, ERROR);
+ Assert(record != NULL);
/*
* Don't allow parameters that can't be set in configuration files to
@@ -9460,19 +9503,12 @@ GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
{
struct config_generic *record;
- record = find_option(name, false, ERROR);
+ record = find_option(name, false, missing_ok, ERROR);
if (record == NULL)
{
- if (missing_ok)
- {
- if (varname)
- *varname = NULL;
- return NULL;
- }
-
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
+ if (varname)
+ *varname = NULL;
+ return NULL;
}
if ((record->flags & GUC_SUPERUSER_ONLY) &&
@@ -10318,7 +10354,7 @@ read_nondefault_variables(void)
if ((varname = read_string_with_null(fp)) == NULL)
break;
- if ((record = find_option(varname, true, FATAL)) == NULL)
+ if ((record = find_option(varname, true, false, FATAL)) == NULL)
elog(FATAL, "failed to locate variable \"%s\" in exec config params file", varname);
if ((varvalue = read_string_with_null(fp)) == NULL)
@@ -11008,7 +11044,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
(void) validate_option_array_item(name, value, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
- record = find_option(name, false, WARNING);
+ record = find_option(name, false, true, WARNING);
if (record)
name = record->name;
@@ -11087,7 +11123,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
(void) validate_option_array_item(name, NULL, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
- record = find_option(name, false, WARNING);
+ record = find_option(name, false, true, WARNING);
if (record)
name = record->name;
@@ -11234,7 +11270,7 @@ validate_option_array_item(const char *name, const char *value,
* SUSET and user is superuser).
*
* name is not known, but exists or can be created as a placeholder (i.e.,
- * it has a prefixed name). We allow this case if you're a superuser,
+ * it has a valid custom name). We allow this case if you're a superuser,
* otherwise not. Superusers are assumed to know what they're doing. We
* can't allow it for other users, because when the placeholder is
* resolved it might turn out to be a SUSET variable;
@@ -11243,16 +11279,11 @@ validate_option_array_item(const char *name, const char *value,
* name is not known and can't be created as a placeholder. Throw error,
* unless skipIfNoPermissions is true, in which case return false.
*/
- gconf = find_option(name, true, WARNING);
+ gconf = find_option(name, true, skipIfNoPermissions, ERROR);
if (!gconf)
{
/* not known, failed to make a placeholder */
- if (skipIfNoPermissions)
- return false;
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"",
- name)));
+ return false;
}
if (gconf->flags & GUC_CUSTOM_PLACEHOLDER)
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 811f80a0976..c55871a972e 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -511,6 +511,27 @@ SET seq_page_cost TO 'NaN';
ERROR: invalid value for parameter "seq_page_cost": "NaN"
SET vacuum_cost_delay TO '10s';
ERROR: 10000 ms is outside the valid range for parameter "vacuum_cost_delay" (0 .. 100)
+SET no_such_variable TO 42;
+ERROR: unrecognized configuration parameter "no_such_variable"
+-- Test "custom" GUCs created on the fly (which aren't really an
+-- intended feature, but many people use them).
+SET custom.my_guc = 42;
+SHOW custom.my_guc;
+ custom.my_guc
+---------------
+ 42
+(1 row)
+
+SET custom."bad-guc" = 42; -- disallowed because -c cannot set this name
+ERROR: invalid configuration parameter name "custom.bad-guc"
+DETAIL: Custom parameter names must be of the form "identifier.identifier".
+SHOW custom."bad-guc";
+ERROR: unrecognized configuration parameter "custom.bad-guc"
+SET special."weird name" = 'foo'; -- could be allowed, but we choose not to
+ERROR: invalid configuration parameter name "special.weird name"
+DETAIL: Custom parameter names must be of the form "identifier.identifier".
+SHOW special."weird name";
+ERROR: unrecognized configuration parameter "special.weird name"
--
-- Test DISCARD TEMP
--
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index 43dbba3775e..3650188d9d7 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -147,6 +147,16 @@ SELECT '2006-08-13 12:34:56'::timestamptz;
-- Test some simple error cases
SET seq_page_cost TO 'NaN';
SET vacuum_cost_delay TO '10s';
+SET no_such_variable TO 42;
+
+-- Test "custom" GUCs created on the fly (which aren't really an
+-- intended feature, but many people use them).
+SET custom.my_guc = 42;
+SHOW custom.my_guc;
+SET custom."bad-guc" = 42; -- disallowed because -c cannot set this name
+SHOW custom."bad-guc";
+SET special."weird name" = 'foo'; -- could be allowed, but we choose not to
+SHOW special."weird name";
--
-- Test DISCARD TEMP