On Mon, Apr 02, 2007 at 07:25:46PM -0400, Bruce Momjian wrote:
> I assume this patch is not ready for 8.3, so I added a URL to the TODO
> list for it.
I have reworked it such that it ignores custom variable templates as Tom
suggested. Attached is the new version.
Joachim
Index: src/backend/utils/misc/guc-file.l
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v
retrieving revision 1.49
diff -c -r1.49 guc-file.l
*** src/backend/utils/misc/guc-file.l 13 Mar 2007 14:32:25 -0000 1.49
--- src/backend/utils/misc/guc-file.l 3 Apr 2007 18:56:15 -0000
***************
*** 116,121 ****
--- 116,124 ----
{
int elevel;
struct name_value_pair *item, *head, *tail;
+ int i;
+ bool *in_conffile = NULL;
+ const char *var;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
***************
*** 140,158 ****
/* Check if all options are valid */
for (item = head; item; item = item->next)
{
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. */
for (item = head; item; item = item->next)
{
set_config_option(item->name, item->value, context,
PGC_S_FILE, false, true);
! }
cleanup_list:
free_name_value_list(head);
}
--- 143,300 ----
/* 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;
}
!
! /*
! * 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] && 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);
cleanup_list:
+ free(in_conffile);
free_name_value_list(head);
}
***************
*** 312,325 ****
{
pfree(opt_name);
pfree(opt_value);
! /* we assume error message was logged already */
OK = false;
goto cleanup_exit;
}
- pfree(opt_name);
- pfree(opt_value);
}
! else
{
/* append to list */
struct name_value_pair *item;
--- 454,467 ----
{
pfree(opt_name);
pfree(opt_value);
!
! /* We assume the error message was logged already. */
OK = false;
goto cleanup_exit;
}
}
!
! if (pg_strcasecmp(opt_name, "include") != 0)
{
/* append to list */
struct name_value_pair *item;
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.383
diff -c -r1.383 guc.c
*** src/backend/utils/misc/guc.c 19 Mar 2007 23:38:30 -0000 1.383
--- src/backend/utils/misc/guc.c 3 Apr 2007 18:56:16 -0000
***************
*** 2454,2460 ****
if (oldval == NULL ||
oldval == *(conf->variable) ||
oldval == conf->reset_val ||
! oldval == conf->tentative_val)
return;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
--- 2454,2461 ----
if (oldval == NULL ||
oldval == *(conf->variable) ||
oldval == conf->reset_val ||
! oldval == conf->tentative_val ||
! oldval == conf->boot_val)
return;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
***************
*** 2477,2483 ****
if (strval == *(conf->variable) ||
strval == conf->reset_val ||
! strval == conf->tentative_val)
return true;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
--- 2478,2485 ----
if (strval == *(conf->variable) ||
strval == conf->reset_val ||
! strval == conf->tentative_val ||
! strval == conf->boot_val)
return true;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
***************
*** 2646,2651 ****
--- 2648,2665 ----
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;
+ }
+
/*
* Create and add a placeholder variable. It's presumed to belong
* to a valid custom variable class at this point.
***************
*** 2824,2862 ****
struct config_bool *conf = (struct config_bool *) gconf;
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->reset_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;
break;
}
case PGC_INT:
{
struct config_int *conf = (struct config_int *) gconf;
! Assert(conf->reset_val >= conf->min);
! Assert(conf->reset_val <= conf->max);
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->reset_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
! conf->gen.name, conf->reset_val);
! *conf->variable = conf->reset_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);
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->reset_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %g",
! conf->gen.name, conf->reset_val);
! *conf->variable = conf->reset_val;
break;
}
case PGC_STRING:
--- 2838,2876 ----
struct config_bool *conf = (struct config_bool *) gconf;
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
! 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->boot_val >= conf->min);
! Assert(conf->boot_val <= conf->max);
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
! 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->boot_val >= conf->min);
! Assert(conf->boot_val <= conf->max);
if (conf->assign_hook)
! if (!(*conf->assign_hook) (conf->boot_val, true,
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %g",
! conf->gen.name, conf->boot_val);
! *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_STRING:
***************
*** 3919,3924 ****
--- 3933,3945 ----
}
/*
+ * Do not replace a value that has been set on the command line by a SIGHUP
+ * reload
+ */
+ if (context == PGC_SIGHUP && record->source == PGC_S_ARGV)
+ return true;
+
+ /*
* Check if the option can be set at this time. See guc.h for the precise
* rules. Note that we don't want to throw errors if we're in the SIGHUP
* context. In that case we just ignore the attempt and return true.
***************
*** 4013,4031 ****
}
/*
! * Should we set reset/stacked values? (If so, the behavior is not
! * transactional.)
*/
! makeDefault = changeVal && (source <= PGC_S_OVERRIDE) && (value != NULL);
/*
! * 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)
{
if (changeVal && !makeDefault)
{
--- 4034,4059 ----
}
/*
! * 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) || 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.
*/
! if (record->source > source && source != PGC_S_DEFAULT)
{
if (changeVal && !makeDefault)
{
***************
*** 4057,4062 ****
--- 4085,4098 ----
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;
***************
*** 4141,4146 ****
--- 4177,4190 ----
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;
***************
*** 4225,4230 ****
--- 4269,4282 ----
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;
***************
*** 4303,4308 ****
--- 4355,4377 ----
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)
{
/*
***************
*** 6346,6351 ****
--- 6415,6427 ----
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)
{
***************
*** 6390,6396 ****
if (buf.len == 0)
newval = NULL;
else if (doit)
! newval = strdup(buf.data);
pfree(buf.data);
return newval;
--- 6466,6472 ----
if (buf.len == 0)
newval = NULL;
else if (doit)
! newval = guc_strdup(ERROR, buf.data);
pfree(buf.data);
return newval;
Index: src/include/utils/guc_tables.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/guc_tables.h,v
retrieving revision 1.32
diff -c -r1.32 guc_tables.h
*** src/include/utils/guc_tables.h 13 Mar 2007 14:32:25 -0000 1.32
--- src/include/utils/guc_tables.h 3 Apr 2007 18:56:21 -0000
***************
*** 154,164 ****
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
bool *variable;
! bool reset_val;
GucBoolAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
bool tentative_val;
};
struct config_int
--- 154,165 ----
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
bool *variable;
! bool boot_val;
GucBoolAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
bool tentative_val;
+ bool reset_val;
};
struct config_int
***************
*** 167,179 ****
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
int *variable;
! int reset_val;
int min;
int max;
GucIntAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
int tentative_val;
};
struct config_real
--- 168,181 ----
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
int *variable;
! int boot_val;
int min;
int max;
GucIntAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
int tentative_val;
+ int reset_val;
};
struct config_real
***************
*** 182,194 ****
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
double *variable;
! double reset_val;
double min;
double max;
GucRealAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
double tentative_val;
};
struct config_string
--- 184,197 ----
/* these fields must be set correctly in initial value: */
/* (all but reset_val are constants) */
double *variable;
! double boot_val;
double min;
double max;
GucRealAssignHook assign_hook;
GucShowHook show_hook;
/* variable fields, initialized at runtime: */
double tentative_val;
+ double reset_val;
};
struct config_string
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster