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

Reply via email to