From b7edb568bdc246e3a07de4914b58648fe38c08b1 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sat, 21 Apr 2007 20:02:41 +0000 Subject: [PATCH] Make configuration parameters fall back to their default values when they are removed from the configuration file. Joachim Wieland --- src/backend/utils/misc/guc-file.l | 158 ++++++++++++++++++++++++++++-- src/backend/utils/misc/guc.c | 128 +++++++++++++++++++----- src/include/utils/guc_tables.h | 11 ++- 3 files changed, 259 insertions(+), 38 deletions(-) diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index d0fc263614..4327601320 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.49 2007/03/13 14:32:25 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.50 2007/04/21 20:02:40 petere Exp $ */ %{ @@ -116,6 +116,9 @@ ProcessConfigFile(GucContext context) { 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,19 +143,158 @@ ProcessConfigFile(GucContext context) /* 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) { - set_config_option(item->name, item->value, context, - PGC_S_FILE, false, true); + /* + * 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,14 +454,14 @@ ParseConfigFile(const char *config_file, const char *calling_file, { 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; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index b9643a734d..8b1122d86e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.386 2007/04/18 16:44:18 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.387 2007/04/21 20:02:40 petere Exp $ * *-------------------------------------------------------------------- */ @@ -2481,7 +2481,8 @@ set_string_field(struct config_string * conf, char **field, char *newval) 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) { @@ -2504,7 +2505,8 @@ string_field_used(struct config_string * conf, char *strval) 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) { @@ -2673,6 +2675,18 @@ add_guc_variable(struct config_generic * var, int elevel) 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. @@ -2851,39 +2865,39 @@ InitializeGUCOptions(void) 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: @@ -3945,6 +3959,13 @@ set_config_option(const char *name, const char *value, return false; } + /* + * 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 @@ -4040,19 +4061,26 @@ set_config_option(const char *name, const char *value, } /* - * 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) { @@ -4084,6 +4112,14 @@ 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; @@ -4168,6 +4204,14 @@ 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; @@ -4252,6 +4296,14 @@ 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; @@ -4330,6 +4382,23 @@ 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) + 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) { /* @@ -6404,6 +6473,13 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) 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) { @@ -6448,7 +6524,7 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) if (buf.len == 0) newval = NULL; else if (doit) - newval = strdup(buf.data); + newval = guc_strdup(ERROR, buf.data); pfree(buf.data); return newval; diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index 611afb8083..f68a814f79 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.32 2007/03/13 14:32:25 petere Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.33 2007/04/21 20:02:41 petere Exp $ * *------------------------------------------------------------------------- */ @@ -154,10 +154,11 @@ struct config_bool /* these fields must be set correctly in initial value: */ /* (all but reset_val are constants) */ bool *variable; - bool reset_val; + bool boot_val; GucBoolAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ + bool reset_val; bool tentative_val; }; @@ -167,12 +168,13 @@ struct config_int /* these fields must be set correctly in initial value: */ /* (all but reset_val are constants) */ int *variable; - int reset_val; + int boot_val; int min; int max; GucIntAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ + int reset_val; int tentative_val; }; @@ -182,12 +184,13 @@ struct config_real /* these fields must be set correctly in initial value: */ /* (all but reset_val are constants) */ double *variable; - double reset_val; + double boot_val; double min; double max; GucRealAssignHook assign_hook; GucShowHook show_hook; /* variable fields, initialized at runtime: */ + double reset_val; double tentative_val; };