Zdenek,
On Fri, Jul 14, 2006 at 12:17:55AM +0200, Zdenek Kotala wrote:
> There is last version of patch with following changes/improvements:
> 1) I divide set_config_option to more smaller functions without backside
> effect.
I did not check the changes you have done to set_config_option and the like
but tested the commenting / uncommenting / changing of guc variables and the
behavior and log output. The general idea (at least my idea) is that
whenever a SIGHUP is received and there is some difference between the
config file and the active value that the server is using, a notice message
is written to the log. That way, at every moment you can see if the active
values coincide with the configuration file by sending a SIGHUP and if there
are no such messages the admin can stop and restart the server and be sure
that the settings will be the same after a restart.
While testing, I specified a bunch of test cases that I attach below.
I also renamed the GUC_JUST_RELOAD to GUC_IN_CONFFILE because I did not
really understand what GUC_JUST_RELOAD should mean. GUC_IN_CONFFILE means
that the variable does show up in the configuration file and is active
there, i.e. is not commented.
Please check my changes, I'm pretty sure it can be cleaned up further.
Joachim
Test cases for "guc falls back to default":
guc_context <= PGC_POSTMASTER (shared_buffers is an example, Default: 1000)
Commented value gets un-commented (value != default)
=> message every time a sighup is received
Example:
#shared_buffers = 3301
START
shared_buffers = 3301
HUP
Output:
LOG: parameter "shared_buffers" cannot be changed after server start;
configuration file change ignored
Value gets changed (to != initial).
=> message every time a sighup is received
Example:
shared_buffers = 3301
START
shared_buffers = 3302
HUP
Output:
LOG: parameter "max_prepared_transactions" cannot be changed after
server start; configuration file change ignored
Value gets commented (initial != default).
=> message every time a sighup is received
Example:
shared_buffers = 3301
START
#shared_buffers = 3301
HUP
Output:
LOG: parameter "max_prepared_transactions" cannot be changed
(commented) after server start; configuration file change ignored
Commented value (not applied) gets changed back to initial setting:
=> no more messages after SIGHUP
Example:
shared_buffers = 3301
START
#shared_buffers = 3301
HUP (value does not get applied)
shared_buffers = 3301
HUP
Output:
None
Commented value (not applied) gets changed to != initial:
=> message every time a SIGHUP is received
Example:
shared_buffers = 3301
START
#shared_buffers = 3301
HUP
shared_buffers = 3302
HUP
Output:
LOG: parameter "shared_buffers" cannot be changed after server start;
configuration file change ignored
guc_context <= PGC_SIGHUP set (fsync is an example, Default: true)
Value (== default) gets commented
=> nothing happens
Example:
fsync = true
START
#fsync = true
HUP
Output:
None
Value (!= default) gets commented
=> falls back to default on first HUP that is received)
Example:
fsync = false
START
fsync = true
HUP
(subsequent HUPs do not show output anymore)
Output:
LOG: configuration option fsync falls back to default value
Commented value gets un-commented (value != default)
Example:
#fsync = false
START
fsync = false
HUP
Output:
None
Commented value gets un-commented (value == default)
Example:
#fsync = true
START
fsync = true
HUP
Output:
None
diff -cr cvs/pgsql/src/backend/utils/misc/guc.c
cvs.build/pgsql/src/backend/utils/misc/guc.c
*** cvs/pgsql/src/backend/utils/misc/guc.c 2006-07-14 22:19:46.000000000
+0200
--- cvs.build/pgsql/src/backend/utils/misc/guc.c 2006-07-24
12:22:55.000000000 +0200
***************
*** 2667,2705 ****
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:
--- 2667,2705 ----
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:
***************
*** 3152,3158 ****
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
! int my_status = gconf->status;
GucStack *stack = gconf->stack;
bool useTentative;
bool changed;
--- 3152,3158 ----
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
! int my_status = gconf->status &
(~GUC_IN_CONFFILE);
GucStack *stack = gconf->stack;
bool useTentative;
bool changed;
***************
*** 3590,3649 ****
return result;
}
-
/*
! * Sets option `name' to given value. The value should be a string
! * which is going to be parsed and converted to the appropriate data
! * type. The context and source parameters indicate in which context this
! * function is being called so it can apply the access restrictions
! * properly.
! *
! * If value is NULL, set the option to its default value. If the
! * parameter changeVal is false then don't really set the option but do all
! * the checks to see if it would work.
! *
! * If there is an error (non-existing option, invalid value) then an
! * ereport(ERROR) is thrown *unless* this is called in a context where we
! * don't want to ereport (currently, startup or SIGHUP config file reread).
! * In that case we write a suitable error message via ereport(DEBUG) and
! * return false. This is working around the deficiencies in the ereport
! * mechanism, so don't blame me. In all other cases, the function
! * returns true, including cases where the input is valid but we chose
! * not to apply it because of context or source-priority considerations.
! *
! * See also SetConfigOption for an external interface.
*/
! bool
! set_config_option(const char *name, const char *value,
! GucContext context, GucSource source,
! bool isLocal, bool changeVal)
{
- struct config_generic *record;
- int elevel;
- bool makeDefault;
! if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
{
! /*
! * To avoid cluttering the log, only the postmaster bleats
loudly
! * about problems with the config file.
! */
! elevel = IsUnderPostmaster ? DEBUG2 : LOG;
! }
! else if (source == PGC_S_DATABASE || source == PGC_S_USER)
! elevel = INFO;
! else
! elevel = ERROR;
! record = find_option(name, elevel);
! if (record == NULL)
! {
! ereport(elevel,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("unrecognized configuration parameter
\"%s\"", name)));
! return false;
}
/*
* 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
--- 3590,3871 ----
return result;
}
/*
! * Try to parse value. Determine what is type and call related
! * parsing function or if newval is equal to NULL, reset value
! * to default or bootval. If the value parsed okay return true,
! * else false.
*/
! static bool
! parse_value(int elevel, const struct config_generic *record,
! const char *value, GucSource *source, bool changeVal,
! union config_var_value *retval)
{
! Assert( !(changeVal && retval==NULL) );
! /*
! * Evaluate value and set variable.
! */
! switch (record->vartype)
{
! case PGC_BOOL:
! {
! struct config_bool *conf = (struct config_bool
*) record;
! bool newval;
! if (value)
! {
! if (!parse_bool(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires a Boolean value",
!
record->name)));
! return false;
! }
! }
! else
! {
! /* Revert value to default if source is
configuration file. It is used when
! * configuration parameter is
removed/commented out in the config file. Else
! * RESET or SET TO DEFAULT command is
called and reset_val is used.
! */
! if( *source == PGC_S_FILE )
! {
! newval = conf->boot_val;
! }
! else
! {
! newval = conf->reset_val;
! *source =
conf->gen.reset_source;
! }
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, *source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %d",
!
record->name, (int) newval)));
! return false;
! }
! if( retval != NULL )
! retval->boolval = newval;
! break;
! }
!
! case PGC_INT:
! {
! struct config_int *conf = (struct config_int *)
record;
! int newval;
!
! if (value)
! {
! if (!parse_int(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires an integer value",
! record->name)));
! return false;
! }
! if (newval < conf->min || newval >
conf->max)
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("%d is
outside the valid range for parameter \"%s\" (%d .. %d)",
!
newval, record->name, conf->min, conf->max)));
! return false;
! }
! }
! else
! {
! /* Revert value to default if source is
configuration file. It is used when
! * configuration parameter is
removed/commented out in the config file. Else
! * RESET or SET TO DEFAULT command is
called and reset_val is used.
! */
! if( *source == PGC_S_FILE )
! {
! newval = conf->boot_val;
! }
! else
! {
! newval = conf->reset_val;
! *source =
conf->gen.reset_source;
! }
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, *source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %d",
!
record->name, newval)));
! return false;
! }
! if( retval != NULL )
! retval->intval = newval;
! break;
! }
!
! case PGC_REAL:
! {
! struct config_real *conf = (struct config_real
*) record;
! double newval;
!
! if (value)
! {
! if (!parse_real(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires a numeric value",
!
record->name)));
! return false;
! }
! if (newval < conf->min || newval >
conf->max)
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("%g is
outside the valid range for parameter \"%s\" (%g .. %g)",
!
newval, record->name, conf->min, conf->max)));
! return false;
! }
! }
! else
! {
! /* Revert value to default if source is
configuration file. It is used when
! * configuration parameter is
removed/commented out in the config file. Else
! * RESET or SET TO DEFAULT command is
called and reset_val is used.
! */
! if( *source == PGC_S_FILE )
! {
! newval = conf->boot_val;
! }
! else
! {
! newval = conf->reset_val;
! *source =
conf->gen.reset_source;
! }
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, *source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %g",
!
record->name, newval)));
! return false;
! }
! if( retval != NULL )
! retval->realval = newval;
! break;
! }
!
! case PGC_STRING:
! {
! struct config_string *conf = (struct
config_string *) record;
! char *newval;
!
! if (value)
! {
! newval = guc_strdup(elevel, value);
! if (newval == NULL)
! return false;
! /*
! * The only sort of "parsing" check we
need to do is
! * apply truncation if GUC_IS_NAME.
! */
! if (conf->gen.flags & GUC_IS_NAME)
! truncate_identifier(newval,
strlen(newval), true);
! }
! else if (*source == PGC_S_FILE)
! {
! /* Revert value to default when item is
removed from config file. */
! if ( conf->boot_val != NULL )
! {
! newval = guc_strdup(elevel,
conf->boot_val);
! if (newval == NULL)
! return false;
! }
! else
! {
! return false;
! }
! }
! else if (conf->reset_val)
! {
! /*
! * We could possibly avoid strdup here,
but easier to make
! * this case work the same as the
normal assignment case.
! */
! newval = guc_strdup(elevel,
conf->reset_val);
! if (newval == NULL)
! return false;
! *source = conf->gen.reset_source;
! }
! else
! {
! /* Nothing to reset to, as yet; so do
nothing */
! break;
! }
!
! if (conf->assign_hook)
! {
! const char *hookresult;
!
! /*
! * If the hook ereports, we have to
make sure we free
! * newval, else it will be a permanent
memory leak.
! */
! hookresult =
call_string_assign_hook(conf->assign_hook,
!
newval,
!
changeVal,
!
*source);
! if (hookresult == NULL)
! {
! free(newval);
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value for
parameter \"%s\": \"%s\"",
! record->name,
value ? value : "")));
! return false;
! }
! else if (hookresult != newval)
! {
! free(newval);
!
! /*
! * Having to cast away const
here is annoying, but the
! * alternative is to declare
assign_hooks as returning
! * char*, which would mean
they'd have to cast away
! * const, or as both taking and
returning char*, which
! * doesn't seem attractive
either --- we don't want
! * them to scribble on the
passed str.
! */
! newval = (char *) hookresult;
! }
! }
!
! if ( !changeVal )
! free(newval);
! if( retval != NULL )
! retval->stringval= newval;
! break;
! }
}
+ return true;
+ }
+ /*
+ *
+ */
+ bool
+ checkContext(int elevel, struct config_generic *record, GucContext context)
+ {
/*
* 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
***************
*** 3652,3685 ****
switch (record->context)
{
case PGC_INTERNAL:
- if (context == PGC_SIGHUP)
- return true;
if (context != PGC_INTERNAL)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed",
! name)));
return false;
}
break;
case PGC_POSTMASTER:
if (context == PGC_SIGHUP)
! {
! if (changeVal && !is_newvalue_equal(record,
value))
! ereport(elevel,
!
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
! errmsg("parameter
\"%s\" cannot be changed after server start; configuration file change ignored",
! name)));
- return true;
- }
if (context != PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed after server start",
! name)));
return false;
}
break;
--- 3874,3898 ----
switch (record->context)
{
case PGC_INTERNAL:
if (context != PGC_INTERNAL)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed",
! record->name)));
return false;
}
break;
case PGC_POSTMASTER:
if (context == PGC_SIGHUP)
! return false;
if (context != PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed after server start",
! record->name)));
return false;
}
break;
***************
*** 3689,3695 ****
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed now",
! name)));
return false;
}
--- 3902,3908 ----
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be changed now",
! record->name)));
return false;
}
***************
*** 3712,3725 ****
* backend start.
*/
if (IsUnderPostmaster)
! return true;
}
else if (context != PGC_BACKEND && context !=
PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be set after connection start",
! name)));
return false;
}
break;
--- 3925,3940 ----
* backend start.
*/
if (IsUnderPostmaster)
! {
! return false;
! }
}
else if (context != PGC_BACKEND && context !=
PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\"
cannot be set after connection start",
! record->name)));
return false;
}
break;
***************
*** 3729,3735 ****
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to
set parameter \"%s\"",
! name)));
return false;
}
break;
--- 3944,3950 ----
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to
set parameter \"%s\"",
! record->name)));
return false;
}
break;
***************
*** 3737,3748 ****
/* always okay */
break;
}
/*
* 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.
--- 3952,4074 ----
/* always okay */
break;
}
+ return true;
+ }
+ /*
+ * Get error level for different sources and context.
+ */
+ int
+ get_elevel(GucContext context, GucSource source)
+ {
+ int elevel;
+ if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
+ {
+ /*
+ * To avoid cluttering the log, only the postmaster bleats
loudly
+ * about problems with the config file.
+ */
+ elevel = IsUnderPostmaster ? DEBUG2 : LOG;
+ }
+ else if (source == PGC_S_DATABASE || source == PGC_S_USER)
+ elevel = INFO;
+ else
+ elevel = ERROR;
+
+ return elevel;
+ }
+
+ /*
+ * Verify if option exists and value is valid.
+ * It is primary used for validation of items in configuration file.
+ */
+ bool
+ verify_config_option(const char *name, const char *value,
+ GucContext context, GucSource source,
+ bool *isNewEqual, bool *isContextOK)
+ {
+ union config_var_value newval;
+ int elevel;
+ struct config_generic *record;
+
+ elevel = get_elevel(context, source);
+
+ record = find_option(name, elevel);
+ if (record == NULL)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter
\"%s\"", name)));
+ return false;
+ }
+
+ if(value != NULL)
+ record->status |= GUC_IN_CONFFILE;
+
+ if( parse_value(elevel, record, value, &source, false, &newval) )
+ {
+ if( isNewEqual != NULL)
+ *isNewEqual = is_newvalue_equal(record, value);
+ if( isContextOK != NULL)
+ *isContextOK = checkContext(elevel, record, context);
+ }
+ else
+ return false;
+
+ return true;
+ }
+
+
+ /*
+ * Sets option `name' to given value. The value should be a string
+ * which is going to be parsed and converted to the appropriate data
+ * type. The context and source parameters indicate in which context this
+ * function is being called so it can apply the access restrictions
+ * properly.
+ *
+ * If value is NULL, set the option to its default value. If the
+ * parameter changeVal is false then don't really set the option but do all
+ * the checks to see if it would work.
+ *
+ * If there is an error (non-existing option, invalid value) then an
+ * ereport(ERROR) is thrown *unless* this is called in a context where we
+ * don't want to ereport (currently, startup or SIGHUP config file reread).
+ * In that case we write a suitable error message via ereport(DEBUG) and
+ * return false. This is working around the deficiencies in the ereport
+ * mechanism, so don't blame me. In all other cases, the function
+ * returns true, including cases where the input is valid but we chose
+ * not to apply it because of context or source-priority considerations.
+ *
+ * See also SetConfigOption for an external interface.
+ */
+ bool
+ set_config_option(const char *name, const char *value,
+ GucContext context, GucSource source,
+ bool isLocal, bool changeVal)
+ {
+ struct config_generic *record;
+ int elevel;
+ bool makeDefault;
+
+ elevel = get_elevel(context, source);
+
+ record = find_option(name, elevel);
+ if (record == NULL)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter
\"%s\"", name)));
+ return false;
+ }
+
+ /* Check if change is allowed in the running context. */
+ if( !checkContext )
+ return false;
/*
* Should we set reset/stacked values? (If so, the behavior is not
* transactional.)
*/
! makeDefault = changeVal && (source <= PGC_S_OVERRIDE) && (value != NULL
|| source == PGC_S_FILE);
/*
* Ignore attempted set if overridden by previously processed setting.
***************
*** 3771,3803 ****
{
struct config_bool *conf = (struct config_bool
*) record;
bool newval;
!
! if (value)
! {
! if (!parse_bool(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires a Boolean value",
! name)));
! return false;
! }
! }
! else
! {
! newval = conf->reset_val;
! source = conf->gen.reset_source;
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %d",
! name,
(int) newval)));
! return false;
! }
if (changeVal || makeDefault)
{
--- 4097,4105 ----
{
struct config_bool *conf = (struct config_bool
*) record;
bool newval;
!
! if( !parse_value(elevel, record, value,
&source, changeVal, (union config_var_value*) &newval) )
! return false;
if (changeVal || makeDefault)
{
***************
*** 3848,3887 ****
struct config_int *conf = (struct config_int *)
record;
int newval;
! if (value)
! {
! if (!parse_int(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires an integer value",
! name)));
! return false;
! }
! if (newval < conf->min || newval >
conf->max)
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("%d is
outside the valid range for parameter \"%s\" (%d .. %d)",
!
newval, name, conf->min, conf->max)));
! return false;
! }
! }
! else
! {
! newval = conf->reset_val;
! source = conf->gen.reset_source;
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %d",
! name,
newval)));
! return false;
! }
if (changeVal || makeDefault)
{
--- 4150,4157 ----
struct config_int *conf = (struct config_int *)
record;
int newval;
! if( !parse_value(elevel, record, value,
&source, changeVal, (union config_var_value*) &newval) )
! return false;
if (changeVal || makeDefault)
{
***************
*** 3932,3971 ****
struct config_real *conf = (struct config_real
*) record;
double newval;
! if (value)
! {
! if (!parse_real(value, &newval))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\"
requires a numeric value",
! name)));
! return false;
! }
! if (newval < conf->min || newval >
conf->max)
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("%g is
outside the valid range for parameter \"%s\" (%g .. %g)",
!
newval, name, conf->min, conf->max)));
! return false;
! }
! }
! else
! {
! newval = conf->reset_val;
! source = conf->gen.reset_source;
! }
!
! if (conf->assign_hook)
! if (!(*conf->assign_hook) (newval,
changeVal, source))
! {
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value
for parameter \"%s\": %g",
! name,
newval)));
! return false;
! }
if (changeVal || makeDefault)
{
--- 4202,4209 ----
struct config_real *conf = (struct config_real
*) record;
double newval;
! if( !parse_value(elevel, record, value,
&source, changeVal, (union config_var_value*) &newval) )
! return false;
if (changeVal || makeDefault)
{
***************
*** 4016,4086 ****
struct config_string *conf = (struct
config_string *) record;
char *newval;
! if (value)
! {
! newval = guc_strdup(elevel, value);
! if (newval == NULL)
! return false;
! /*
! * The only sort of "parsing" check we
need to do is
! * apply truncation if GUC_IS_NAME.
! */
! if (conf->gen.flags & GUC_IS_NAME)
! truncate_identifier(newval,
strlen(newval), true);
! }
! else if (conf->reset_val)
! {
! /*
! * We could possibly avoid strdup here,
but easier to make
! * this case work the same as the
normal assignment case.
! */
! newval = guc_strdup(elevel,
conf->reset_val);
! if (newval == NULL)
! return false;
! source = conf->gen.reset_source;
! }
! else
! {
! /* Nothing to reset to, as yet; so do
nothing */
! break;
! }
!
! if (conf->assign_hook)
! {
! const char *hookresult;
!
! /*
! * If the hook ereports, we have to
make sure we free
! * newval, else it will be a permanent
memory leak.
! */
! hookresult =
call_string_assign_hook(conf->assign_hook,
!
newval,
!
changeVal,
!
source);
! if (hookresult == NULL)
! {
! free(newval);
! ereport(elevel,
!
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid value for
parameter \"%s\": \"%s\"",
! name, value ?
value : "")));
! return false;
! }
! else if (hookresult != newval)
! {
! free(newval);
!
! /*
! * Having to cast away const
here is annoying, but the
! * alternative is to declare
assign_hooks as returning
! * char*, which would mean
they'd have to cast away
! * const, or as both taking and
returning char*, which
! * doesn't seem attractive
either --- we don't want
! * them to scribble on the
passed str.
! */
! newval = (char *) hookresult;
! }
! }
if (changeVal || makeDefault)
{
--- 4254,4261 ----
struct config_string *conf = (struct
config_string *) record;
char *newval;
! if( !parse_value(elevel, record, value,
&source, changeVal, (union config_var_value*) &newval) )
! return false;
if (changeVal || makeDefault)
{
***************
*** 4128,4134 ****
}
}
else
! free(newval);
break;
}
}
--- 4303,4310 ----
}
}
else
! if( newval != NULL )
! free(newval);
break;
}
}
***************
*** 5130,5135 ****
--- 5306,5316 ----
static bool
is_newvalue_equal(struct config_generic *record, const char *newvalue)
{
+ if( !newvalue )
+ {
+ return false;
+ }
+
switch (record->vartype)
{
case PGC_BOOL:
diff -cr cvs/pgsql/src/backend/utils/misc/guc-file.l
cvs.build/pgsql/src/backend/utils/misc/guc-file.l
*** cvs/pgsql/src/backend/utils/misc/guc-file.l 2006-03-24 08:10:37.000000000
+0100
--- cvs.build/pgsql/src/backend/utils/misc/guc-file.l 2006-07-24
12:26:30.000000000 +0200
***************
*** 50,56 ****
static bool ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext
context, int elevel,
struct name_value_pair
**head_p,
! struct name_value_pair
**tail_p);
static void free_name_value_list(struct name_value_pair * list);
static char *GUC_scanstr(const char *s);
--- 50,57 ----
static bool ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext
context, int elevel,
struct name_value_pair
**head_p,
! struct name_value_pair
**tail_p,
! int *varcount);
static void free_name_value_list(struct name_value_pair * list);
static char *GUC_scanstr(const char *s);
***************
*** 112,119 ****
void
ProcessConfigFile(GucContext context)
{
! int elevel;
struct name_value_pair *item, *head, *tail;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
--- 113,123 ----
void
ProcessConfigFile(GucContext context)
{
! int elevel, i;
struct name_value_pair *item, *head, *tail;
+ char *env;
+ bool *apply_list = NULL;
+ int varcount = 0;
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
***************
*** 132,156 ****
if (!ParseConfigFile(ConfigFileName, NULL,
0, context, elevel,
! &head, &tail))
goto cleanup_list;
/* 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);
}
--- 136,245 ----
if (!ParseConfigFile(ConfigFileName, NULL,
0, context, elevel,
! &head, &tail, &varcount))
goto cleanup_list;
+ /* Can we allocate memory here, what about leaving here prematurely? */
+ apply_list = (bool *) palloc(sizeof(bool) * varcount);
+
/* Check if all options are valid */
! for (item = head, i = 0; item; item = item->next, i++)
{
! bool isEqual, isContextOk;
!
! if (!verify_config_option(item->name, item->value, context,
! PGC_S_FILE, &isEqual,
&isContextOk))
! {
! ereport(elevel,
! (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
! errmsg("configuration file is invalid")));
goto cleanup_list;
+ }
+
+ apply_list[i] = true;
+ if( isContextOk == false )
+ {
+ if( context == PGC_SIGHUP )
+ {
+ if ( isEqual == false )
+ {
+ ereport(elevel,
+
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot
be changed after server start; configuration file change ignored",
+ item->name)));
+ /* do not apply the changed value here
*/
+ apply_list[i] = false;
+ }
+ }
+ else
+ /* if it is boot phase loading context must be
valid for all
+ * configuration item. */
+ goto cleanup_list;
+ }
}
/* If we got here all the options checked out okay, so apply them. */
! for (item = head, i = 0; item; item = item->next, i++)
! if (apply_list[i])
! set_config_option(item->name, item->value, context,
! PGC_S_FILE, false,
true);
!
! if( context == PGC_SIGHUP)
{
! /*
! * Revert all "untouched" options with reset source PGC_S_FILE to
! * default/boot value.
! */
! for (i = 0; i < num_guc_variables; i++)
! {
! struct config_generic *gconf = guc_variables[i];
! if ( gconf->context != PGC_INTERNAL &&
! !(gconf->context == PGC_BACKEND &&
IsUnderPostmaster) &&
! gconf->reset_source == PGC_S_FILE &&
! !(gconf->status & GUC_IN_CONFFILE))
! {
! if (gconf->context == PGC_POSTMASTER)
! ereport(elevel,
!
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
! errmsg("parameter \"%s\" cannot
be changed (commented) after server start; configuration file change ignored",
! gconf->name)));
! else if(set_config_option(gconf->name,
!
NULL, context,
!
PGC_S_FILE,
!
false, true))
! {
! GucStack *stack;
! /* set correctly source */
! 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;
!
! ereport(elevel,
!
(errcode(ERRCODE_SUCCESSFUL_COMPLETION),
! errmsg("configuration
option %s falls back to default value", gconf->name)));
! }
! }
! gconf->status &= ~GUC_IN_CONFFILE;
! }
!
! /* Revert to environment variable. PGPORT is ignored, because
it cannot be
! * set in running state.
! */
! env = getenv("PGDATESTYLE");
! if (env != NULL)
! set_config_option("datestyle", env, context,
! PGC_S_ENV_VAR, false,
true);
!
! env = getenv("PGCLIENTENCODING");
! if (env != NULL)
! set_config_option("client_encoding", env, context,
! PGC_S_ENV_VAR, false,
true);
}
! cleanup_list:
! if (apply_list)
! pfree(apply_list);
free_name_value_list(head);
}
***************
*** 187,199 ****
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext context, int elevel,
struct name_value_pair **head_p,
! struct name_value_pair **tail_p)
{
! bool OK = true;
! char abs_path[MAXPGPATH];
! FILE *fp;
YY_BUFFER_STATE lex_buffer;
! int token;
/*
* Reject too-deep include nesting depth. This is just a safety check
--- 276,289 ----
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, GucContext context, int elevel,
struct name_value_pair **head_p,
! struct name_value_pair **tail_p,
! int *varcount)
{
! bool OK = true;
! char abs_path[MAXPGPATH];
! FILE *fp;
YY_BUFFER_STATE lex_buffer;
! int token;
/*
* Reject too-deep include nesting depth. This is just a safety check
***************
*** 261,269 ****
/* now we must have the option value */
if (token != GUC_ID &&
! token != GUC_STRING &&
! token != GUC_INTEGER &&
! token != GUC_REAL &&
token != GUC_UNQUOTED_STRING)
goto parse_error;
if (token == GUC_STRING) /* strip quotes and escapes */
--- 351,359 ----
/* now we must have the option value */
if (token != GUC_ID &&
! token != GUC_STRING &&
! token != GUC_INTEGER &&
! token != GUC_REAL &&
token != GUC_UNQUOTED_STRING)
goto parse_error;
if (token == GUC_STRING) /* strip quotes and escapes */
***************
*** 287,293 ****
if (!ParseConfigFile(opt_value, config_file,
depth + 1,
context, elevel,
! head_p,
tail_p))
{
pfree(opt_name);
pfree(opt_value);
--- 377,383 ----
if (!ParseConfigFile(opt_value, config_file,
depth + 1,
context, elevel,
! head_p,
tail_p, varcount))
{
pfree(opt_name);
pfree(opt_value);
***************
*** 331,336 ****
--- 421,427 ----
else
(*tail_p)->next = item;
*tail_p = item;
+ (*varcount)++;
}
/* break out of loop if read EOF, else loop for next line */
***************
*** 350,356 ****
else
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("syntax error in file \"%s\" line %u,
near token \"%s\"",
config_file, ConfigFileLineno,
yytext)));
OK = false;
--- 441,447 ----
else
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("syntax error in file \"%s\" line %u,
near token \"%s\"",
config_file, ConfigFileLineno,
yytext)));
OK = false;
diff -cr cvs/pgsql/src/include/utils/guc.h
cvs.build/pgsql/src/include/utils/guc.h
*** cvs/pgsql/src/include/utils/guc.h 2006-07-14 22:19:50.000000000 +0200
--- cvs.build/pgsql/src/include/utils/guc.h 2006-07-16 16:39:06.000000000
+0200
***************
*** 16,22 ****
#include "tcop/dest.h"
#include "utils/array.h"
-
/*
* Certain options can only be set at certain times. The rules are
* like this:
--- 16,21 ----
***************
*** 194,199 ****
--- 193,201 ----
extern bool set_config_option(const char *name, const char *value,
GucContext context, GucSource source,
bool isLocal, bool changeVal);
+ extern bool verify_config_option(const char *name, const char *value,
+ GucContext context, GucSource source,
+ bool *isNewEqual, bool *isContextOK);
extern char *GetConfigOptionByName(const char *name, const char **varname);
extern void GetConfigOptionByNum(int varnum, const char **values, bool
*noshow);
extern int GetNumConfigOptions(void);
diff -cr cvs/pgsql/src/include/utils/guc_tables.h
cvs.build/pgsql/src/include/utils/guc_tables.h
*** cvs/pgsql/src/include/utils/guc_tables.h 2006-07-14 22:19:50.000000000
+0200
--- cvs.build/pgsql/src/include/utils/guc_tables.h 2006-07-24
11:49:14.000000000 +0200
***************
*** 134,140 ****
#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */
#define GUC_HAVE_LOCAL 0x0002 /* a SET LOCAL has been
executed */
#define GUC_HAVE_STACK 0x0004 /* we have stacked
prior value(s) */
!
/* GUC records for specific variable types */
--- 134,141 ----
#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */
#define GUC_HAVE_LOCAL 0x0002 /* a SET LOCAL has been
executed */
#define GUC_HAVE_STACK 0x0004 /* we have stacked
prior value(s) */
! #define GUC_IN_CONFFILE 0x0008 /* value shows up in
the configuration
!
file (is not commented) */
/* GUC records for specific variable types */
***************
*** 144,154 ****
/* 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
--- 145,156 ----
/* 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
***************
*** 157,169 ****
/* 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
--- 159,172 ----
/* 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
***************
*** 172,184 ****
/* 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
--- 175,189 ----
/* 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