aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/cache/ts_cache.c12
-rw-r--r--src/backend/utils/misc/README10
-rw-r--r--src/backend/utils/misc/guc-file.l313
-rw-r--r--src/backend/utils/misc/guc.c443
-rw-r--r--src/include/utils/guc_tables.h21
5 files changed, 407 insertions, 392 deletions
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 94051b8c32f..63c57905565 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -20,7 +20,7 @@
* Copyright (c) 2006-2007, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.2 2007/08/22 01:39:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.3 2007/09/10 00:57:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -592,14 +592,6 @@ getTSCurrentConfig(bool emitError)
const char *
assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
{
- /* do nothing during initial GUC setup */
- if (newval == NULL)
- {
- if (doit)
- TSCurrentConfigCache = InvalidOid;
- return newval;
- }
-
/*
* If we aren't inside a transaction, we cannot do database access so
* cannot verify the config name. Must accept it on faith.
@@ -637,7 +629,7 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
newval = strdup(buf);
pfree(buf);
- if (doit)
+ if (doit && newval)
TSCurrentConfigCache = cfgId;
}
else
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 3ea838b1f53..bf1e3b65455 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/utils/misc/README,v 1.5 2004/07/01 00:51:24 tgl Exp $
+$PostgreSQL: pgsql/src/backend/utils/misc/README,v 1.6 2007/09/10 00:57:21 tgl Exp $
GUC IMPLEMENTATION NOTES
@@ -53,6 +53,14 @@ The third choice is allowed in case the assign_hook wants to return a
datestyle always returns a string that includes both output and input
datestyle options, although the input might have specified only one.
+Note that a string variable's assign_hook will NEVER be called with a NULL
+value for newvalue, since there would be no way to distinguish success
+and failure returns. If the boot_val or reset_val for a string variable
+is NULL, it will just be assigned without calling the assign_hook.
+Therefore, a NULL boot_val should never be used in combination with an
+assign_hook that has side-effects, as the side-effects wouldn't happen
+during a RESET that re-institutes the boot-time setting.
+
If a show_hook is provided, it points to a function of the signature
const char *show_hook(void)
This hook allows variable-specific computation of the value displayed
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 4327601320e..124dfbf62e1 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -4,7 +4,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.50 2007/04/21 20:02:40 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.51 2007/09/10 00:57:21 tgl Exp $
*/
%{
@@ -116,9 +116,10 @@ ProcessConfigFile(GucContext context)
{
int elevel;
struct name_value_pair *item, *head, *tail;
+ char *cvc = NULL;
+ struct config_string *cvc_struct;
+ const char *envvar;
int i;
- bool *in_conffile = NULL;
- const char *var;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
@@ -133,6 +134,7 @@ ProcessConfigFile(GucContext context)
else
elevel = ERROR;
+ /* Parse the file into a list of option names and values */
head = tail = NULL;
if (!ParseConfigFile(ConfigFileName, NULL,
@@ -140,162 +142,178 @@ ProcessConfigFile(GucContext context)
&head, &tail))
goto cleanup_list;
- /* Check if all options are valid */
- for (item = head; item; item = item->next)
+ /*
+ * We need the proposed new value of custom_variable_classes to check
+ * custom variables with. ParseConfigFile ensured that if it's in
+ * the file, it's first in the list. But first check to see if we
+ * have an active value from the command line, which should override
+ * the file in any case. (Since there's no relevant env var, the
+ * only possible nondefault sources are the file and ARGV.)
+ */
+ cvc_struct = (struct config_string *)
+ find_option("custom_variable_classes", false, elevel);
+ if (cvc_struct && cvc_struct->gen.reset_source > PGC_S_FILE)
{
- char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR);
- if (sep && !is_custom_class(item->name, sep - item->name))
+ cvc = guc_strdup(elevel, cvc_struct->reset_val);
+ if (cvc == NULL)
+ goto cleanup_list;
+ }
+ else if (head != NULL &&
+ guc_name_compare(head->name, "custom_variable_classes") == 0)
+ {
+ /*
+ * Need to canonicalize the value via the assign hook. Casting away
+ * const is a bit ugly, but we know the result is malloc'd.
+ */
+ cvc = (char *) assign_custom_variable_classes(head->value,
+ false, PGC_S_FILE);
+ if (cvc == NULL)
{
ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"",
- item->name)));
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ head->name, head->value)));
goto cleanup_list;
}
-
- if (!set_config_option(item->name, item->value, context,
- PGC_S_FILE, false, false))
- goto cleanup_list;
}
-
/*
- * Mark all variables as not showing up in the config file. The
- * allocation has to take place after ParseConfigFile() since this
- * function can change num_guc_variables due to custom variables.
- * It would be easier to add a new field or status bit to struct
- * conf_generic, but that way we would expose internal information
- * that is just needed here in the following few lines. The price
- * to pay for this separation are a few more loops over the set of
- * configuration options, but those are expected to be rather few
- * and we only have to pay the cost at SIGHUP. We initialize
- * in_conffile only here because set_config_option() makes
- * guc_variables grow with custom variables.
+ * Mark all extant GUC variables as not present in the config file.
+ * We need this so that we can tell below which ones have been removed
+ * from the file since we last processed it.
*/
- in_conffile = guc_malloc(elevel, num_guc_variables * sizeof(bool));
- if (!in_conffile)
- goto cleanup_list;
for (i = 0; i < num_guc_variables; i++)
- in_conffile[i] = false;
+ {
+ struct config_generic *gconf = guc_variables[i];
+
+ gconf->status &= ~GUC_IS_IN_FILE;
+ }
+ /*
+ * Check if all options are valid. As a side-effect, the GUC_IS_IN_FILE
+ * flag is set on each GUC variable mentioned in the list.
+ */
for (item = head; item; item = item->next)
{
- /*
- * After set_config_option() the variable name item->name is
- * known to exist.
- */
- Assert(guc_get_index(item->name) >= 0);
- in_conffile[guc_get_index(item->name)] = true;
+ char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR);
+
+ if (sep)
+ {
+ /*
+ * We have to consider three cases for custom variables:
+ *
+ * 1. The class name is not valid according to the (new) setting
+ * of custom_variable_classes. If so, reject. We don't care
+ * which side is at fault.
+ */
+ if (!is_custom_class(item->name, sep - item->name, cvc))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"",
+ item->name)));
+ goto cleanup_list;
+ }
+ /*
+ * 2. There is no GUC entry. If we called set_config_option then
+ * it would make a placeholder, which we don't want to do yet,
+ * since we could still fail further down the list. Do nothing
+ * (assuming that making the placeholder will succeed later).
+ */
+ if (find_option(item->name, false, elevel) == NULL)
+ continue;
+ /*
+ * 3. There is already a GUC entry (either real or placeholder) for
+ * the variable. In this case we should let set_config_option
+ * check it, since the assignment could well fail if it's a real
+ * entry.
+ */
+ }
+
+ if (!set_config_option(item->name, item->value, context,
+ PGC_S_FILE, false, false))
+ goto cleanup_list;
}
+ /*
+ * Check for variables having been removed from the config file, and
+ * revert their reset values (and perhaps also effective values) to the
+ * boot-time defaults. If such a variable can't be changed after startup,
+ * just throw a warning and continue. (This is analogous to the fact that
+ * set_config_option only throws a warning for a new but different value.
+ * If we wanted to make it a hard error, we'd need an extra pass over the
+ * list so that we could throw the error before starting to apply
+ * changes.)
+ */
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
- if (!in_conffile[i] && gconf->source == PGC_S_FILE)
+ GucStack *stack;
+
+ if (gconf->reset_source != PGC_S_FILE ||
+ (gconf->status & GUC_IS_IN_FILE))
+ continue;
+ if (gconf->context < PGC_SIGHUP)
{
- if (gconf->context < PGC_SIGHUP)
- ereport(elevel,
+ ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
- errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored",
- gconf->name)));
- else
- {
- /* prepare */
- GucStack *stack;
- if (gconf->reset_source == PGC_S_FILE)
- gconf->reset_source = PGC_S_DEFAULT;
- for (stack = gconf->stack; stack; stack = stack->prev)
- if (stack->source == PGC_S_FILE)
- stack->source = PGC_S_DEFAULT;
- /* apply the default */
- set_config_option(gconf->name, NULL, context,
- PGC_S_DEFAULT, false, true);
- }
+ errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored",
+ gconf->name)));
+ continue;
}
- else if (!in_conffile[i] && gconf->reset_source == PGC_S_FILE)
- {
- /*------
- * Change the reset_val to default_val. Here's an
- * example: In the configuration file we have
- *
- * seq_page_cost = 3.00
- *
- * Now we execute in a session
- *
- * SET seq_page_cost TO 4.00;
- *
- * Then we remove this option from the configuration file
- * and send SIGHUP. Now when you execute
- *
- * RESET seq_page_cost;
- *
- * it should fall back to 1.00 (the default value for
- * seq_page_cost) and not to 3.00 (which is the current
- * reset_val).
- */
- switch (gconf->vartype)
- {
- case PGC_BOOL:
- {
- struct config_bool *conf;
- conf = (struct config_bool *) gconf;
- conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_INT:
- {
- struct config_int *conf;
- conf = (struct config_int *) gconf;
- conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_REAL:
- {
- struct config_real *conf;
- conf = (struct config_real *) gconf;
- conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_STRING:
- {
- struct config_string *conf;
- conf = (struct config_string *) gconf;
- /*
- * We can cast away the const here because we
- * won't free the address. It is protected by
- * set_string_field() and string_field_used().
- */
- conf->reset_val = (char *) conf->boot_val;
- break;
- }
- }
+ /*
+ * Reset any "file" sources to "default", else set_config_option
+ * will not override those settings. tentative_source should
+ * never be "file".
+ */
+ if (gconf->reset_source == PGC_S_FILE)
+ gconf->reset_source = PGC_S_DEFAULT;
+ Assert(gconf->tentative_source != PGC_S_FILE);
+ if (gconf->source == PGC_S_FILE)
+ gconf->source = PGC_S_DEFAULT;
+ for (stack = gconf->stack; stack; stack = stack->prev)
+ {
+ Assert(stack->tentative_source != PGC_S_FILE);
+ if (stack->source == PGC_S_FILE)
+ stack->source = PGC_S_DEFAULT;
}
- }
- /* If we got here all the options checked out okay, so apply them. */
- for (item = head; item; item = item->next)
- set_config_option(item->name, item->value, context,
- PGC_S_FILE, false, true);
+ /* Now we can re-apply the wired-in default */
+ set_config_option(gconf->name, NULL, context, PGC_S_DEFAULT,
+ false, true);
+ }
/*
- * Reset variables to the value of environment variables
- * (PGC_S_ENV_VAR overrules PGC_S_FILE). PGPORT is ignored,
- * because it cannot be changed without restart.
+ * Restore any variables determined by environment variables. This
+ * is a no-op except in the case where one of these had been in the
+ * config file and is now removed. PGC_S_ENV_VAR will override the
+ * wired-in default we just applied, but cannot override any other source.
+ * PGPORT can be ignored, because it cannot be changed without restart.
+ * Keep this list in sync with InitializeGUCOptions()!
*/
- var = getenv("PGDATESTYLE");
- if (var != NULL)
- set_config_option("datestyle", var, context,
+ envvar = getenv("PGDATESTYLE");
+ if (envvar != NULL)
+ set_config_option("datestyle", envvar, PGC_POSTMASTER,
PGC_S_ENV_VAR, false, true);
- var = getenv("PGCLIENTENCODING");
- if (var != NULL)
- set_config_option("client_encoding", var, context,
+ envvar = getenv("PGCLIENTENCODING");
+ if (envvar != NULL)
+ set_config_option("client_encoding", envvar, PGC_POSTMASTER,
PGC_S_ENV_VAR, false, true);
+
+ /* If we got here all the options checked out okay, so apply them. */
+ for (item = head; item; item = item->next)
+ {
+ set_config_option(item->name, item->value, context,
+ PGC_S_FILE, false, true);
+ }
+
cleanup_list:
- free(in_conffile);
free_name_value_list(head);
+ if (cvc)
+ free(cvc);
}
@@ -389,6 +407,7 @@ ParseConfigFile(const char *config_file, const char *calling_file,
while ((token = yylex()))
{
char *opt_name, *opt_value;
+ struct name_value_pair *item;
if (token == GUC_EOL) /* empty or comment line */
continue;
@@ -421,7 +440,7 @@ ParseConfigFile(const char *config_file, const char *calling_file,
goto parse_error;
/* OK, process the option name and value */
- if (pg_strcasecmp(opt_name, "include") == 0)
+ if (guc_name_compare(opt_name, "include") == 0)
{
/*
* An include directive isn't a variable and should be processed
@@ -443,29 +462,39 @@ ParseConfigFile(const char *config_file, const char *calling_file,
pfree(opt_name);
pfree(opt_value);
}
- else if (pg_strcasecmp(opt_name, "custom_variable_classes") == 0)
+ else if (guc_name_compare(opt_name, "custom_variable_classes") == 0)
{
/*
* This variable must be processed first as it controls
- * the validity of other variables; so apply immediately.
+ * the validity of other variables; so it goes at the head
+ * of the result list. If we already found a value for it,
+ * replace with this one.
*/
- if (!set_config_option(opt_name, opt_value, context,
- PGC_S_FILE, false, true))
+ item = *head_p;
+ if (item != NULL &&
+ guc_name_compare(item->name, "custom_variable_classes") == 0)
{
- pfree(opt_name);
- pfree(opt_value);
-
- /* We assume the error message was logged already. */
- OK = false;
- goto cleanup_exit;
+ /* replace existing head item */
+ pfree(item->name);
+ pfree(item->value);
+ item->name = opt_name;
+ item->value = opt_value;
+ }
+ else
+ {
+ /* prepend to list */
+ item = palloc(sizeof *item);
+ item->name = opt_name;
+ item->value = opt_value;
+ item->next = *head_p;
+ *head_p = item;
+ if (*tail_p == NULL)
+ *tail_p = item;
}
}
-
- if (pg_strcasecmp(opt_name, "include") != 0)
+ else
{
- /* append to list */
- struct name_value_pair *item;
-
+ /* ordinary variable, append to list */
item = palloc(sizeof *item);
item->name = opt_name;
item->value = opt_value;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 60f7ed5ca32..6cdc87df6db 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.416 2007/09/03 18:46:30 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.417 2007/09/10 00:57:21 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -2505,7 +2505,7 @@ static void ReportGUCOption(struct config_generic * record);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest);
static char *_ShowOption(struct config_generic * record, bool use_units);
-static bool is_newvalue_equal(struct config_generic * record, const char *newvalue);
+static bool is_newvalue_equal(struct config_generic *record, const char *newvalue);
/*
@@ -2690,40 +2690,6 @@ build_guc_variables(void)
sizeof(struct config_generic *), guc_var_compare);
}
-static bool
-is_custom_class(const char *name, int dotPos)
-{
- /*
- * assign_custom_variable_classes() has made sure no empty identifiers or
- * whitespace exists in the variable
- */
- bool result = false;
- const char *ccs = GetConfigOption("custom_variable_classes");
-
- if (ccs != NULL)
- {
- const char *start = ccs;
-
- for (;; ++ccs)
- {
- int c = *ccs;
-
- if (c == 0 || c == ',')
- {
- if (dotPos == ccs - start && strncmp(start, name, dotPos) == 0)
- {
- result = true;
- break;
- }
- if (c == 0)
- break;
- start = ccs + 1;
- }
- }
- }
- return result;
-}
-
/*
* Add a new GUC variable to the list of known variables. The
* list is expanded if needed.
@@ -2767,7 +2733,7 @@ add_guc_variable(struct config_generic * var, int elevel)
* Create and add a placeholder variable. It's presumed to belong
* to a valid custom variable class at this point.
*/
-static struct config_string *
+static struct config_generic *
add_placeholder_variable(const char *name, int elevel)
{
size_t sz = sizeof(struct config_string) + sizeof(char *);
@@ -2777,9 +2743,8 @@ add_placeholder_variable(const char *name, int elevel)
var = (struct config_string *) guc_malloc(elevel, sz);
if (var == NULL)
return NULL;
-
- gen = &var->gen;
memset(var, 0, sz);
+ gen = &var->gen;
gen->name = guc_strdup(elevel, name);
if (gen->name == NULL)
@@ -2796,7 +2761,8 @@ add_placeholder_variable(const char *name, int elevel)
/*
* The char* is allocated at the end of the struct since we have no
- * 'static' place to point to.
+ * 'static' place to point to. Note that the current value, as well
+ * as the boot and reset values, start out NULL.
*/
var->variable = (char **) (var + 1);
@@ -2807,17 +2773,53 @@ add_placeholder_variable(const char *name, int elevel)
return NULL;
}
- return var;
+ return gen;
+}
+
+/*
+ * Detect whether the portion of "name" before dotPos matches any custom
+ * variable class name listed in custom_var_classes. The latter must be
+ * formatted the way that assign_custom_variable_classes does it, ie,
+ * no whitespace. NULL is valid for custom_var_classes.
+ */
+static bool
+is_custom_class(const char *name, int dotPos, const char *custom_var_classes)
+{
+ bool result = false;
+ const char *ccs = custom_var_classes;
+
+ if (ccs != NULL)
+ {
+ const char *start = ccs;
+
+ for (;; ++ccs)
+ {
+ char c = *ccs;
+
+ if (c == '\0' || c == ',')
+ {
+ if (dotPos == ccs - start && strncmp(start, name, dotPos) == 0)
+ {
+ result = true;
+ break;
+ }
+ if (c == '\0')
+ break;
+ start = ccs + 1;
+ }
+ }
+ }
+ return result;
}
/*
- * Look up option NAME. If it exists, return a pointer to its record,
- * else return NULL.
+ * 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.
*/
static struct config_generic *
-find_option(const char *name, int elevel)
+find_option(const char *name, bool create_placeholders, int elevel)
{
- const char *dot;
const char **key = &name;
struct config_generic **res;
int i;
@@ -2844,17 +2846,21 @@ find_option(const char *name, 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], elevel);
+ return find_option(map_old_guc_names[i + 1], false, elevel);
}
- /*
- * Check if the name is qualified, and if so, check if the qualifier maps
- * to a custom variable class.
- */
- dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
- if (dot != NULL && is_custom_class(name, dot - name))
- /* Add a placeholder variable for this name */
- return (struct config_generic *) add_placeholder_variable(name, elevel);
+ if (create_placeholders)
+ {
+ /*
+ * Check if the name is qualified, and if so, check if the qualifier
+ * matches any custom variable class. If so, add a placeholder.
+ */
+ const char *dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
+
+ if (dot != NULL &&
+ is_custom_class(name, dot - name, custom_variable_classes))
+ return add_placeholder_variable(name, elevel);
+ }
/* Unknown name */
return NULL;
@@ -2902,30 +2908,6 @@ guc_name_compare(const char *namea, const char *nameb)
}
-static int
-guc_get_index(const char *name)
-{
- const char **key = &name;
- struct config_generic **res;
-
- Assert(name);
-
- /*
- * By equating const char ** with struct config_generic *, we are assuming
- * the name field is first in config_generic.
- */
- res = (struct config_generic **) bsearch((void *) &key,
- (void *) guc_variables,
- num_guc_variables,
- sizeof(struct config_generic *),
- guc_var_compare);
- if (!res)
- return -1;
-
- return res - guc_variables;
-}
-
-
/*
* Initialize GUC options during program startup.
*
@@ -3017,7 +2999,7 @@ InitializeGUCOptions(void)
if (conf->boot_val == NULL)
{
- /* Cannot set value yet */
+ /* leave the value NULL, do not call assign hook */
break;
}
@@ -3067,7 +3049,8 @@ InitializeGUCOptions(void)
/*
* For historical reasons, some GUC parameters can receive defaults from
- * environment variables. Process those settings.
+ * environment variables. Process those settings. NB: if you add or
+ * remove anything here, see also ProcessConfigFile().
*/
env = getenv("PGPORT");
@@ -3335,16 +3318,10 @@ ResetAllOptions(void)
struct config_string *conf = (struct config_string *) gconf;
char *str;
- if (conf->reset_val == NULL)
- {
- /* Nothing to reset to, as yet; so do nothing */
- break;
- }
-
/* We need not strdup here */
str = conf->reset_val;
- if (conf->assign_hook)
+ if (conf->assign_hook && str)
{
const char *newstr;
@@ -3517,7 +3494,9 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
/*
* Skip if nothing's happened to this var in this transaction
*/
- if (my_status == 0)
+ if ((my_status & (GUC_HAVE_TENTATIVE |
+ GUC_HAVE_LOCAL |
+ GUC_HAVE_STACK)) == 0)
{
Assert(stack == NULL);
continue;
@@ -3679,7 +3658,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
if (*conf->variable != newval)
{
- if (conf->assign_hook)
+ if (conf->assign_hook && newval)
{
const char *newstr;
@@ -4182,7 +4161,7 @@ set_config_option(const char *name, const char *value,
else
elevel = ERROR;
- record = find_option(name, elevel);
+ record = find_option(name, true, elevel);
if (record == NULL)
{
ereport(elevel,
@@ -4192,11 +4171,13 @@ set_config_option(const char *name, const char *value,
}
/*
- * Do not replace a value that has been set on the command line by a SIGHUP
- * reload
+ * If source is postgresql.conf, mark the found record with GUC_IS_IN_FILE.
+ * This is for the convenience of ProcessConfigFile. Note that we do it
+ * even if changeVal is false, since ProcessConfigFile wants the marking
+ * to occur during its testing pass.
*/
- if (context == PGC_SIGHUP && record->source == PGC_S_ARGV)
- return true;
+ if (source == PGC_S_FILE)
+ record->status |= GUC_IS_IN_FILE;
/*
* Check if the option can be set at this time. See guc.h for the precise
@@ -4220,12 +4201,17 @@ set_config_option(const char *name, const char *value,
case PGC_POSTMASTER:
if (context == PGC_SIGHUP)
{
+ /*
+ * We are reading a PGC_POSTMASTER var from postgresql.conf.
+ * We can't change the setting, so give a warning if the DBA
+ * tries to change it. (Throwing an error would be more
+ * consistent, but seems overly rigid.)
+ */
if (changeVal && !is_newvalue_equal(record, value))
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored",
name)));
-
return true;
}
if (context != PGC_POSTMASTER)
@@ -4298,22 +4284,17 @@ set_config_option(const char *name, const char *value,
* value from the database's/user's/client's default settings or
* when we reset a value to its default.
*/
- makeDefault = changeVal && (source <= PGC_S_OVERRIDE)
- && ((value != NULL) || source == PGC_S_DEFAULT);
+ makeDefault = changeVal && (source <= PGC_S_OVERRIDE) &&
+ ((value != NULL) || source == PGC_S_DEFAULT);
/*
- * Ignore attempted set if overridden by previously processed
- * setting. However, if changeVal is false then plow ahead anyway
- * since we are trying to find out if the value is potentially
- * good, not actually use it. Also keep going if makeDefault is
- * true, since we may want to set the reset/stacked values even if
- * we can't set the variable itself. There's one exception to
- * this rule: if we want to apply the default value to variables
- * that were removed from the configuration file. This is
- * indicated by source == PGC_S_DEFAULT and context == PGC_SIGHUP.
+ * Ignore attempted set if overridden by previously processed setting.
+ * However, if changeVal is false then plow ahead anyway since we are
+ * trying to find out if the value is potentially good, not actually use
+ * it. Also keep going if makeDefault is true, since we may want to set
+ * the reset/stacked values even if we can't set the variable itself.
*/
- if (record->source > source
- && !(source == PGC_S_DEFAULT && context == PGC_SIGHUP))
+ if (record->source > source)
{
if (changeVal && !makeDefault)
{
@@ -4326,6 +4307,9 @@ set_config_option(const char *name, const char *value,
/*
* Evaluate value and set variable.
+ *
+ * Note: if value == NULL then we are supposed to set to the reset_val,
+ * except when source == PGC_S_DEFAULT; then we set to the boot_val.
*/
switch (record->vartype)
{
@@ -4345,14 +4329,8 @@ set_config_option(const char *name, const char *value,
return false;
}
}
- /*
- * If value == NULL and source == PGC_S_DEFAULT then
- * we reset some value to its default (removed from
- * configuration file).
- */
else if (source == PGC_S_DEFAULT)
newval = conf->boot_val;
- /* else we handle a "RESET varname" command */
else
{
newval = conf->reset_val;
@@ -4440,14 +4418,8 @@ set_config_option(const char *name, const char *value,
return false;
}
}
- /*
- * If value == NULL and source == PGC_S_DEFAULT then
- * we reset some value to its default (removed from
- * configuration file).
- */
else if (source == PGC_S_DEFAULT)
newval = conf->boot_val;
- /* else we handle a "RESET varname" command */
else
{
newval = conf->reset_val;
@@ -4532,14 +4504,8 @@ set_config_option(const char *name, const char *value,
return false;
}
}
- /*
- * If value == NULL and source == PGC_S_DEFAULT then
- * we reset some value to its default (removed from
- * configuration file).
- */
else if (source == PGC_S_DEFAULT)
newval = conf->boot_val;
- /* else we handle a "RESET varname" command */
else
{
newval = conf->reset_val;
@@ -4618,11 +4584,6 @@ set_config_option(const char *name, const char *value,
if (conf->gen.flags & GUC_IS_NAME)
truncate_identifier(newval, strlen(newval), true);
}
- /*
- * If value == NULL and source == PGC_S_DEFAULT then
- * we reset some value to its default (removed from
- * configuration file).
- */
else if (source == PGC_S_DEFAULT)
{
if (conf->boot_val == NULL)
@@ -4634,25 +4595,25 @@ set_config_option(const char *name, const char *value,
return false;
}
}
- /* else we handle a "RESET varname" command */
- else if (conf->reset_val)
+ else
{
/*
* We could possibly avoid strdup here, but easier to make
- * this case work the same as the normal assignment case.
+ * this case work the same as the normal assignment case;
+ * note the possible free of newval below.
*/
- newval = guc_strdup(elevel, conf->reset_val);
- if (newval == NULL)
- return false;
+ if (conf->reset_val == NULL)
+ newval = NULL;
+ else
+ {
+ newval = guc_strdup(elevel, conf->reset_val);
+ if (newval == NULL)
+ return false;
+ }
source = conf->gen.reset_source;
}
- else
- {
- /* Nothing to reset to, as yet; so do nothing */
- break;
- }
- if (conf->assign_hook)
+ if (conf->assign_hook && newval)
{
const char *hookresult;
@@ -4718,7 +4679,7 @@ set_config_option(const char *name, const char *value,
}
}
/* Perhaps we didn't install newval anywhere */
- if (!string_field_used(conf, newval))
+ if (newval && !string_field_used(conf, newval))
free(newval);
}
else if (isLocal)
@@ -4734,7 +4695,7 @@ set_config_option(const char *name, const char *value,
guc_dirty = true;
}
}
- else
+ else if (newval)
free(newval);
break;
}
@@ -4774,7 +4735,7 @@ GetConfigOption(const char *name)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -4807,6 +4768,10 @@ GetConfigOption(const char *name)
/*
* Get the RESET value associated with the given option.
+ *
+ * Note: this is not re-entrant, due to use of static result buffer;
+ * not to mention that a string variable could have its reset_val changed.
+ * Beware of assuming the result value is good for very long.
*/
const char *
GetConfigOptionResetString(const char *name)
@@ -4814,7 +4779,7 @@ GetConfigOptionResetString(const char *name)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -4854,7 +4819,7 @@ IsSuperuserConfigOption(const char *name)
{
struct config_generic *record;
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
/* On an unrecognized name, don't error, just return false. */
if (record == NULL)
return false;
@@ -4886,7 +4851,7 @@ flatten_set_variable_args(const char *name, List *args)
return NULL;
/* Else get flags for the variable */
- record = find_option(name, ERROR);
+ record = find_option(name, true, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -5145,22 +5110,54 @@ set_config_by_name(PG_FUNCTION_ARGS)
}
+/*
+ * Common code for DefineCustomXXXVariable subroutines: allocate the
+ * new variable's config struct and fill in generic fields.
+ */
+static struct config_generic *
+init_custom_variable(const char *name,
+ const char *short_desc,
+ const char *long_desc,
+ GucContext context,
+ enum config_type type,
+ size_t sz)
+{
+ struct config_generic *gen;
+
+ gen = (struct config_generic *) guc_malloc(ERROR, sz);
+ memset(gen, 0, sz);
+
+ gen->name = guc_strdup(ERROR, name);
+ gen->context = context;
+ gen->group = CUSTOM_OPTIONS;
+ gen->short_desc = short_desc;
+ gen->long_desc = long_desc;
+ gen->vartype = type;
+
+ return gen;
+}
+
+/*
+ * Common code for DefineCustomXXXVariable subroutines: insert the new
+ * variable into the GUC variable array, replacing any placeholder.
+ */
static void
-define_custom_variable(struct config_generic * variable)
+define_custom_variable(struct config_generic *variable)
{
const char *name = variable->name;
const char **nameAddr = &name;
const char *value;
struct config_string *pHolder;
- struct config_generic **res = (struct config_generic **) bsearch(
- (void *) &nameAddr,
- (void *) guc_variables,
- num_guc_variables,
- sizeof(struct config_generic *),
- guc_var_compare);
+ struct config_generic **res;
+ res = (struct config_generic **) bsearch((void *) &nameAddr,
+ (void *) guc_variables,
+ num_guc_variables,
+ sizeof(struct config_generic *),
+ guc_var_compare);
if (res == NULL)
{
+ /* No placeholder to replace, so just add it */
add_guc_variable(variable, ERROR);
return;
}
@@ -5174,13 +5171,14 @@ define_custom_variable(struct config_generic * variable)
errmsg("attempt to redefine parameter \"%s\"", name)));
Assert((*res)->vartype == PGC_STRING);
- pHolder = (struct config_string *) * res;
+ pHolder = (struct config_string *) (*res);
- /* We have the same name, no sorting is necessary */
+ /*
+ * Replace the placeholder.
+ * We aren't changing the name, so no re-sorting is necessary
+ */
*res = variable;
- value = *pHolder->variable;
-
/*
* Assign the string value stored in the placeholder to the real variable.
*
@@ -5188,9 +5186,12 @@ define_custom_variable(struct config_generic * variable)
* assignment, since we don't want it to roll back if the current xact
* fails later.
*/
- set_config_option(name, value,
- pHolder->gen.context, pHolder->gen.source,
- false, true);
+ value = *pHolder->variable;
+
+ if (value)
+ set_config_option(name, value,
+ pHolder->gen.context, pHolder->gen.source,
+ false, true);
/*
* Free up as much as we conveniently can of the placeholder structure
@@ -5203,22 +5204,6 @@ define_custom_variable(struct config_generic * variable)
free(pHolder);
}
-static void
-init_custom_variable(struct config_generic * gen,
- const char *name,
- const char *short_desc,
- const char *long_desc,
- GucContext context,
- enum config_type type)
-{
- gen->name = guc_strdup(ERROR, name);
- gen->context = context;
- gen->group = CUSTOM_OPTIONS;
- gen->short_desc = short_desc;
- gen->long_desc = long_desc;
- gen->vartype = type;
-}
-
void
DefineCustomBoolVariable(const char *name,
const char *short_desc,
@@ -5228,13 +5213,13 @@ DefineCustomBoolVariable(const char *name,
GucBoolAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_bool);
- struct config_bool *var = (struct config_bool *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_BOOL);
+ struct config_bool *var;
+ var = (struct config_bool *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_BOOL, sizeof(struct config_bool));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->assign_hook = assign_hook;
var->show_hook = show_hook;
@@ -5252,13 +5237,13 @@ DefineCustomIntVariable(const char *name,
GucIntAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_int);
- struct config_int *var = (struct config_int *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_INT);
+ struct config_int *var;
+ var = (struct config_int *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_INT, sizeof(struct config_int));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->min = minValue;
var->max = maxValue;
@@ -5278,13 +5263,13 @@ DefineCustomRealVariable(const char *name,
GucRealAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_real);
- struct config_real *var = (struct config_real *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_REAL);
+ struct config_real *var;
+ var = (struct config_real *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_REAL, sizeof(struct config_real));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->min = minValue;
var->max = maxValue;
@@ -5302,14 +5287,16 @@ DefineCustomStringVariable(const char *name,
GucStringAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_string);
- struct config_string *var = (struct config_string *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_STRING);
+ struct config_string *var;
+ var = (struct config_string *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_STRING, sizeof(struct config_string));
var->variable = valueAddr;
- var->reset_val = *valueAddr;
+ var->boot_val = *valueAddr;
+ /* we could probably do without strdup, but keep it like normal case */
+ if (var->boot_val)
+ var->reset_val = guc_strdup(ERROR, var->boot_val);
var->assign_hook = assign_hook;
var->show_hook = show_hook;
define_custom_variable(&var->gen);
@@ -5345,7 +5332,7 @@ EmitWarningsOnPlaceholders(const char *className)
void
GetPGVariable(const char *name, DestReceiver *dest)
{
- if (pg_strcasecmp(name, "all") == 0)
+ if (guc_name_compare(name, "all") == 0)
ShowAllGUCConfig(dest);
else
ShowGUCConfigOption(name, dest);
@@ -5356,7 +5343,7 @@ GetPGVariableResultDesc(const char *name)
{
TupleDesc tupdesc;
- if (pg_strcasecmp(name, "all") == 0)
+ if (guc_name_compare(name, "all") == 0)
{
/* need a tuple descriptor representing three TEXT columns */
tupdesc = CreateTemplateTupleDesc(3, false);
@@ -5470,7 +5457,7 @@ GetConfigOptionByName(const char *name, const char **varname)
{
struct config_generic *record;
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -5927,9 +5914,19 @@ _ShowOption(struct config_generic * record, bool use_units)
}
+/*
+ * Attempt (badly) to detect if a proposed new GUC setting is the same
+ * as the current value.
+ *
+ * XXX this does not really work because it doesn't account for the
+ * effects of canonicalization of string values by assign_hooks.
+ */
static bool
-is_newvalue_equal(struct config_generic * record, const char *newvalue)
+is_newvalue_equal(struct config_generic *record, const char *newvalue)
{
+ /* newvalue == NULL isn't supported */
+ Assert(newvalue != NULL);
+
switch (record->vartype)
{
case PGC_BOOL:
@@ -5960,7 +5957,8 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue)
{
struct config_string *conf = (struct config_string *) record;
- return strcmp(*conf->variable, newvalue) == 0;
+ return *conf->variable != NULL &&
+ strcmp(*conf->variable, newvalue) == 0;
}
}
@@ -6140,7 +6138,7 @@ read_nondefault_variables(void)
if ((varname = read_string_with_null(fp)) == NULL)
break;
- if ((record = find_option(varname, FATAL)) == NULL)
+ if ((record = find_option(varname, true, FATAL)) == NULL)
elog(FATAL, "failed to locate variable %s in exec config params file", varname);
if ((varvalue = read_string_with_null(fp)) == NULL)
elog(FATAL, "invalid format of exec config params file");
@@ -6576,8 +6574,7 @@ assign_session_replication_role(const char *newval, bool doit, GucSource source)
}
static const char *
-assign_log_min_messages(const char *newval,
- bool doit, GucSource source)
+assign_log_min_messages(const char *newval, bool doit, GucSource source)
{
return (assign_msglvl(&log_min_messages, newval, doit, source));
}
@@ -6756,23 +6753,16 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
{
/*
* Check syntax. newval must be a comma separated list of identifiers.
- * Whitespace is allowed but skipped.
+ * Whitespace is allowed but removed from the result.
*/
bool hasSpaceAfterToken = false;
const char *cp = newval;
int symLen = 0;
- int c;
+ char c;
StringInfoData buf;
- /*
- * Resetting custom_variable_classes by removing it from the
- * configuration file will lead to newval = NULL
- */
- if (newval == NULL)
- return guc_strdup(ERROR, "");
-
initStringInfo(&buf);
- while ((c = *cp++) != 0)
+ while ((c = *cp++) != '\0')
{
if (isspace((unsigned char) c))
{
@@ -6783,12 +6773,12 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
if (c == ',')
{
- hasSpaceAfterToken = false;
- if (symLen > 0)
+ if (symLen > 0) /* terminate identifier */
{
- symLen = 0;
appendStringInfoChar(&buf, ',');
+ symLen = 0;
}
+ hasSpaceAfterToken = false;
continue;
}
@@ -6798,24 +6788,19 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
* Syntax error due to token following space after token or non
* alpha numeric character
*/
- ereport(LOG,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid syntax for \"custom_variable_classes\": \"%s\"", newval)));
pfree(buf.data);
return NULL;
}
+ appendStringInfoChar(&buf, c);
symLen++;
- appendStringInfoChar(&buf, (char) c);
}
/* Remove stray ',' at end */
if (symLen == 0 && buf.len > 0)
buf.data[--buf.len] = '\0';
- if (buf.len == 0)
- newval = NULL;
- else if (doit)
- newval = guc_strdup(ERROR, buf.data);
+ /* GUC wants the result malloc'd */
+ newval = guc_strdup(LOG, buf.data);
pfree(buf.data);
return newval;
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index f68a814f799..0bc74ccdd0b 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -7,7 +7,7 @@
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.33 2007/04/21 20:02:41 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.34 2007/09/10 00:57:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -130,7 +130,7 @@ struct config_generic
#define GUC_SUPERUSER_ONLY 0x0100 /* show only to superusers */
#define GUC_IS_NAME 0x0200 /* limit string to NAMEDATALEN-1 */
-#define GUC_UNIT_KB 0x0400 /* value is in 1 kB */
+#define GUC_UNIT_KB 0x0400 /* value is in kilobytes */
#define GUC_UNIT_BLOCKS 0x0800 /* value is in blocks */
#define GUC_UNIT_XBLOCKS 0x0C00 /* value is in xlog blocks */
#define GUC_UNIT_MEMORY 0x0C00 /* mask for KB, BLOCKS, XBLOCKS */
@@ -144,6 +144,11 @@ struct config_generic
#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */
#define GUC_HAVE_LOCAL 0x0002 /* a SET LOCAL has been executed */
#define GUC_HAVE_STACK 0x0004 /* we have stacked prior value(s) */
+#define GUC_IS_IN_FILE 0x0008 /* found it in config file */
+/*
+ * Caution: the GUC_IS_IN_FILE bit is transient state for ProcessConfigFile.
+ * Do not assume that its value represents useful information elsewhere.
+ */
/* GUC records for specific variable types */
@@ -151,8 +156,7 @@ struct config_generic
struct config_bool
{
struct config_generic gen;
- /* these fields must be set correctly in initial value: */
- /* (all but reset_val are constants) */
+ /* constant fields, must be set correctly in initial value: */
bool *variable;
bool boot_val;
GucBoolAssignHook assign_hook;
@@ -165,8 +169,7 @@ struct config_bool
struct config_int
{
struct config_generic gen;
- /* these fields must be set correctly in initial value: */
- /* (all but reset_val are constants) */
+ /* constant fields, must be set correctly in initial value: */
int *variable;
int boot_val;
int min;
@@ -181,8 +184,7 @@ struct config_int
struct config_real
{
struct config_generic gen;
- /* these fields must be set correctly in initial value: */
- /* (all but reset_val are constants) */
+ /* constant fields, must be set correctly in initial value: */
double *variable;
double boot_val;
double min;
@@ -197,8 +199,7 @@ struct config_real
struct config_string
{
struct config_generic gen;
- /* these fields must be set correctly in initial value: */
- /* (all are constants) */
+ /* constant fields, must be set correctly in initial value: */
char **variable;
const char *boot_val;
GucStringAssignHook assign_hook;