|
|
|
@ -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; |
|
|
|
|