*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.47 2007/02/10 14:58:55 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.48 2007/03/12 22:09:27 petere Exp $
*/
%{
static void free_name_value_list(struct name_value_pair * list);
static char *GUC_scanstr(const char *s);
+static char *custom_variable_classes_backup;
+
%}
%option 8bit
{
int elevel;
struct name_value_pair *item, *head, *tail;
+ int i;
+ bool *in_conffile = NULL;
+ const char *var;
+ bool success = false;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
head = tail = NULL;
+ /*
+ * We do not know whether we will read the configuration file
+ * without error. If we encounter an error we have to restore the
+ * previous value of the custom_variable_classes variable for
+ * consistency. Therefore we have to save its value.
+ */
+ var = GetConfigOption("custom_variable_classes");
+ if (var)
+ custom_variable_classes_backup = pstrdup(var);
+ set_config_option("custom_variable_classes", NULL, context,
+ PGC_S_DEFAULT, false, true);
+
if (!ParseConfigFile(ConfigFileName, NULL,
0, context, elevel,
&head, &tail))
/* Check if all options are valid */
for (item = head; item; item = item->next)
{
+ char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR);
+ if (sep && !is_custom_class(item->name, sep - item->name))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"",
+ item->name)));
+ goto cleanup_list;
+ }
+
if (!set_config_option(item->name, item->value, context,
PGC_S_FILE, false, false))
goto cleanup_list;
}
- /* If we got here all the options checked out okay, so apply them. */
+
+ /*
+ * 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.
+ */
+ 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;
+
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;
+ }
+
+ for (i = 0; i < num_guc_variables; i++)
+ {
+ struct config_generic *gconf = guc_variables[i];
+ if (!in_conffile[i] && strchr(gconf->name, GUC_QUALIFIER_SEPARATOR))
+ {
+ /* handle custom variables here */
+ int j;
+
+ /*
+ * guc_delete_variable will re-sort the array of
+ * configuration options and decrease num_guc_variables by
+ * one.
+ */
+ guc_delete_custom_variable(gconf->name);
+
+ /*
+ * Since the array is always sorted we just have to shift
+ * all boolean values after position i by one position to
+ * the left.
+ */
+ for (j = i; j < num_guc_variables; j++)
+ in_conffile[j] = in_conffile[j+1];
+
+ /*
+ * Decrease the loop variable to re-test the entry at the
+ * current position that is now a different one.
+ */
+ i--;
+
+ continue;
+ }
+ else if (!in_conffile[i] && gconf->source == PGC_S_FILE)
+ {
+ if (gconf->context < PGC_SIGHUP)
+ 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);
+ }
+ }
+ 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;
+ }
+ }
+ }
+ }
+
+ /* 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);
- }
+
+ /*
+ * 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.
+ */
+ var = getenv("PGDATESTYLE");
+ if (var != NULL)
+ set_config_option("datestyle", var, context,
+ PGC_S_ENV_VAR, false, true);
+
+ var = getenv("PGCLIENTENCODING");
+ if (var != NULL)
+ set_config_option("client_encoding", var, context,
+ PGC_S_ENV_VAR, false, true);
+
+ success = true;
cleanup_list:
+ free(in_conffile);
free_name_value_list(head);
+ if (!success)
+ set_config_option("custom_variable_classes",
+ custom_variable_classes_backup,
+ context, PGC_S_FILE, false, true);
+ if (custom_variable_classes_backup)
+ {
+ pfree(custom_variable_classes_backup);
+ custom_variable_classes_backup = NULL;
+ }
}
{
pfree(opt_name);
pfree(opt_value);
- /* we assume error message was logged already */
+
+ /* We assume the error message was logged already. */
OK = false;
goto cleanup_exit;
}
- pfree(opt_name);
- pfree(opt_value);
}
- else
+
+ if (pg_strcasecmp(opt_name, "include") != 0)
{
/* append to list */
struct name_value_pair *item;
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.379 2007/03/06 02:06:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.380 2007/03/12 22:09:28 petere Exp $
*
*--------------------------------------------------------------------
*/
if (oldval == NULL ||
oldval == *(conf->variable) ||
oldval == conf->reset_val ||
- oldval == conf->tentative_val)
+ oldval == conf->tentative_val ||
+ oldval == conf->boot_val)
return;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
if (strval == *(conf->variable) ||
strval == conf->reset_val ||
- strval == conf->tentative_val)
+ strval == conf->tentative_val ||
+ strval == conf->boot_val)
return true;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
return true;
}
+static int
+guc_get_index(const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_guc_variables; i++)
+ if (strcasecmp(name, guc_variables[i]->name) == 0)
+ return i;
+
+ return -1;
+}
+
+static void
+guc_delete_variable(const char *name)
+{
+ struct config_generic *gen;
+ int idx;
+ GucStack *stack, *prev;
+ struct config_string *conf;
+
+ idx = guc_get_index(name);
+ Assert(idx >= 0);
+
+ gen = guc_variables[idx];
+
+ /*
+ * Even though this function could delete other types of variables as well,
+ * at the moment we only call it for custom variables that always have type
+ * string.
+ */
+ Assert(gen->group == CUSTOM_OPTIONS);
+ Assert(gen->vartype == PGC_STRING);
+
+ conf = (struct config_string *) gen;
+ set_string_field(conf, &conf->reset_val, NULL);
+ set_string_field(conf, &conf->tentative_val, NULL);
+ for (stack = conf->gen.stack; stack; stack = prev)
+ {
+ set_string_field(conf, &stack->tentative_val.stringval, NULL);
+ set_string_field(conf, &stack->value.stringval, NULL);
+ prev = stack->prev;
+ pfree(stack);
+ }
+
+ /* no pfree() here, gen has been allocated via guc_malloc */
+ free(gen);
+ guc_variables[idx] = guc_variables[num_guc_variables - 1];
+ num_guc_variables--;
+ qsort((void *) guc_variables, num_guc_variables,
+ sizeof(struct config_generic *), guc_var_compare);
+}
+
+static void
+guc_delete_custom_variable(const char *name)
+{
+ struct config_generic *gen;
+ struct config_string *conf;
+ int idx;
+
+ idx = guc_get_index(name);
+ Assert(idx >= 0);
+
+ gen = guc_variables[idx];
+
+ Assert(gen->group == CUSTOM_OPTIONS);
+ Assert(gen->vartype == PGC_STRING);
+
+ conf = (struct config_string *) gen;
+
+ /*
+ * Here we check whether it is safe to really delete the variable
+ * or if we have to wait for the end of the transaction. We do
+ * not remove the physical entry of a custom variable if it has
+ * been SET to another value in the transaction but has been
+ * removed from the configuration file.
+ */
+ if (gen->stack)
+ {
+ /*
+ * Make sure this custom variable (which has a definition in
+ * the configuration file) behaves as any other custom
+ * variable from now on. We have deleted its former
+ * definition; that's why "RESET foo.bar" should not fall back
+ * to this deleted definition from the configuration file.
+ * The analogy is that any other custom variable that gets
+ * introduced in a session has no reset value either. And a
+ * variable that has been SET within a transaction and has
+ * then been deleted from the configuration file should behave
+ * as if it had been introduced in the session.
+ */
+ Assert(gen->vartype == PGC_STRING);
+ gen->reset_source = PGC_S_DEFAULT;
+ set_string_field(conf, &conf->reset_val, NULL);
+ }
+ else
+ guc_delete_variable(name);
+}
+
+
/*
* Create and add a placeholder variable. It's presumed to belong
* to a valid custom variable class at this point.
struct config_bool *conf = (struct config_bool *) gconf;
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->reset_val, true,
+ if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
- conf->gen.name, (int) conf->reset_val);
- *conf->variable = conf->reset_val;
+ conf->gen.name, (int) conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_INT:
{
struct config_int *conf = (struct config_int *) gconf;
- Assert(conf->reset_val >= conf->min);
- Assert(conf->reset_val <= conf->max);
+ Assert(conf->boot_val >= conf->min);
+ Assert(conf->boot_val <= conf->max);
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->reset_val, true,
+ if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
- conf->gen.name, conf->reset_val);
- *conf->variable = conf->reset_val;
+ conf->gen.name, conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_REAL:
{
struct config_real *conf = (struct config_real *) gconf;
- Assert(conf->reset_val >= conf->min);
- Assert(conf->reset_val <= conf->max);
+ Assert(conf->boot_val >= conf->min);
+ Assert(conf->boot_val <= conf->max);
if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->reset_val, true,
+ if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %g",
- conf->gen.name, conf->reset_val);
- *conf->variable = conf->reset_val;
+ conf->gen.name, conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_STRING:
}
}
-/*
+/*------
* Do GUC processing at transaction or subtransaction commit or abort.
+ *
+ * GUC can abort the transaction in exactly one case, namely when you
+ * delete a custom variable class while a still-open transaction has
+ * SET a custom variable within this class.
+ *
+ * Consider the following example. In the configuration file we could
+ * have:
+ *
+ * custom_variable_classes = "foo"
+ *
+ * begin;
+ * set foo.bar to 1;
+ * <delete foo.bar from configuration file and send SIGHUP>
+ * commit;
+ *
+ * This will result in an error because foo.bar is no longer available
+ * but commit would have to guarantee that the value is preserved.
*/
void
AtEOXact_GUC(bool isCommit, bool isSubXact)
continue;
Assert(stack->nest_level == my_level);
+ if (!isSubXact && gconf->group == CUSTOM_OPTIONS)
+ {
+ char *dot = strchr(gconf->name, GUC_QUALIFIER_SEPARATOR);
+ Assert(dot != NULL);
+ if (!is_custom_class(gconf->name, dot - gconf->name))
+ {
+ if (!isCommit)
+ {
+ /* We do not commit the transaction. Delete the variable. */
+ guc_delete_variable(gconf->name);
+ /* Call the loop with the same i again, which is
+ * the next variable. */
+ i--;
+ continue;
+ }
+ else
+ {
+ /* We commit the transaction. Throw an error that
+ * the respective custom class does not exist. */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("no valid custom variable class available for "
+ "parameter \"%s\"", gconf->name)));
+ }
+ }
+ }
+
/*
* We will pop the stack entry. Start by restoring outer xact status
* (since we may want to modify it below). Be careful to use
}
/*
- * Should we set reset/stacked values? (If so, the behavior is not
- * transactional.)
+ * Should we set reset/stacked values? (If so, the behavior is not
+ * transactional.) This is done either when we get a default
+ * 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);
+ 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.
+ * 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.
*/
- if (record->source > source)
+ if (record->source > source && source != PGC_S_DEFAULT)
{
if (changeVal && !makeDefault)
{
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;
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;
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;
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)
+ newval = NULL;
+ else
+ {
+ newval = guc_strdup(elevel, conf->boot_val);
+ if (newval == NULL)
+ return false;
+ }
+ }
+ /* else we handle a "RESET varname" command */
else if (conf->reset_val)
{
/*
int 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)
{
if (buf.len == 0)
newval = NULL;
else if (doit)
- newval = strdup(buf.data);
+ newval = guc_strdup(ERROR, buf.data);
pfree(buf.data);
return newval;