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