diff options
Diffstat (limited to 'src/backend/utils/misc/guc.c')
-rw-r--r-- | src/backend/utils/misc/guc.c | 254 |
1 files changed, 227 insertions, 27 deletions
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 0fa3489fb6..48ac8705b1 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.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 $ * *-------------------------------------------------------------------- */ @@ -2439,7 +2439,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) { @@ -2462,7 +2463,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) { @@ -2631,6 +2633,105 @@ 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; +} + +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. @@ -2809,39 +2910,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: @@ -3296,8 +3397,25 @@ push_old_value(struct config_generic * gconf) } } -/* +/*------ * 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) @@ -3335,6 +3453,33 @@ 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 @@ -3998,19 +4143,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) { @@ -4042,6 +4194,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; @@ -4126,6 +4286,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; @@ -4210,6 +4378,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; @@ -4288,6 +4464,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) { /* @@ -6304,6 +6497,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) { @@ -6348,7 +6548,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; |