Re: Allow commenting of variables in postgresql.conf to - - Mailing list pgsql-patches
From | Zdenek Kotala |
---|---|
Subject | Re: Allow commenting of variables in postgresql.conf to - |
Date | |
Msg-id | 44B6C693.2010106@sun.com Whole thread Raw |
In response to | Re: Allow commenting of variables in postgresql.conf to - (Zdenek Kotala <Zdenek.Kotala@Sun.COM>) |
Responses |
Re: Allow commenting of variables in postgresql.conf to - try 4
|
List | pgsql-patches |
There is last version of patch with following changes/improvements: 1) I divide set_config_option to more smaller functions without backside effect. 2) Behavior of reconfiguration is "same" in SIG_HUP context (exclude elevel). All errors are reported and full validity of file is checked too. Zdenek Index: src/backend/utils/misc/guc-file.l =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v retrieving revision 1.37 diff -c -r1.37 guc-file.l *** src/backend/utils/misc/guc-file.l 7 Mar 2006 01:03:12 -0000 1.37 --- src/backend/utils/misc/guc-file.l 13 Jul 2006 21:55:28 -0000 *************** *** 112,119 **** void ProcessConfigFile(GucContext context) { ! int elevel; struct name_value_pair *item, *head, *tail; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); --- 112,120 ---- void ProcessConfigFile(GucContext context) { ! int elevel, i; struct name_value_pair *item, *head, *tail; + char *env; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); *************** *** 137,146 **** /* 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. */ --- 138,173 ---- /* Check if all options are valid */ for (item = head; item; item = item->next) ! { ! 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; + } + + 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))); + } + } + 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. */ *************** *** 150,155 **** --- 177,233 ---- 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_POSTMASTER && + !( gconf->context == PGC_BACKEND && IsUnderPostmaster) && + (gconf->reset_source == PGC_S_FILE) && ( (gconf->status & GUC_JUST_RELOAD) == 0) ) + { + 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 has been revert to the boot value", gconf->name))); + } + } + gconf->status &= ~GUC_JUST_RELOAD; + } + + /* 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: free_name_value_list(head); } Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.319 diff -c -r1.319 guc.c *** src/backend/utils/misc/guc.c 11 May 2006 19:15:35 -0000 1.319 --- src/backend/utils/misc/guc.c 13 Jul 2006 21:55:28 -0000 *************** *** 2648,2686 **** 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: --- 2648,2686 ---- 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: *************** *** 3133,3139 **** 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; --- 3133,3139 ---- for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; ! int my_status = gconf->status & (~GUC_JUST_RELOAD); GucStack *stack = gconf->stack; bool useTentative; bool changed; *************** *** 3571,3630 **** 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 --- 3571,3852 ---- 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 *************** *** 3633,3666 **** 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; --- 3855,3879 ---- 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; *************** *** 3670,3676 **** ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed now", ! name))); return false; } --- 3883,3889 ---- ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed now", ! record->name))); return false; } *************** *** 3693,3706 **** * 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; --- 3906,3921 ---- * 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; *************** *** 3710,3716 **** ereport(elevel, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set parameter \"%s\"", ! name))); return false; } break; --- 3925,3931 ---- ereport(elevel, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set parameter \"%s\"", ! record->name))); return false; } break; *************** *** 3718,3729 **** /* 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. --- 3933,4052 ---- /* 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( 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. *************** *** 3752,3784 **** { 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) { --- 4075,4083 ---- { 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) { *************** *** 3829,3868 **** 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) { --- 4128,4135 ---- 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) { *************** *** 3913,3952 **** 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) { --- 4180,4187 ---- 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) { *************** *** 3997,4067 **** 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) { --- 4232,4239 ---- 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) { *************** *** 4109,4115 **** } } else ! free(newval); break; } } --- 4281,4288 ---- } } else ! if( newval != NULL ) ! free(newval); break; } } *************** *** 4117,4122 **** --- 4290,4298 ---- if (changeVal && (record->flags & GUC_REPORT)) ReportGUCOption(record); + if(changeVal && record->source == PGC_S_FILE && value != NULL) + record->status |= GUC_JUST_RELOAD; + return true; } *************** *** 5111,5116 **** --- 5287,5297 ---- static bool is_newvalue_equal(struct config_generic *record, const char *newvalue) { + if( !newvalue ) + { + return false; + } + switch (record->vartype) { case PGC_BOOL: Index: src/include/utils/guc.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/utils/guc.h,v retrieving revision 1.68 diff -c -r1.68 guc.h *** src/include/utils/guc.h 11 May 2006 19:15:35 -0000 1.68 --- src/include/utils/guc.h 13 Jul 2006 21:55:29 -0000 *************** *** 17,23 **** #include "tcop/dest.h" #include "utils/array.h" - /* * Certain options can only be set at certain times. The rules are * like this: --- 17,22 ---- *************** *** 195,200 **** --- 194,202 ---- 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); Index: src/include/utils/guc_tables.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/utils/guc_tables.h,v retrieving revision 1.22 diff -c -r1.22 guc_tables.h *** src/include/utils/guc_tables.h 5 Mar 2006 15:59:07 -0000 1.22 --- src/include/utils/guc_tables.h 13 Jul 2006 21:55:29 -0000 *************** *** 132,138 **** #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 */ --- 132,138 ---- #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_JUST_RELOAD 0x0008 /* value is just reload from configuration file */ /* GUC records for specific variable types */ *************** *** 142,152 **** /* 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 --- 142,153 ---- /* 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 *************** *** 155,167 **** /* 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 --- 156,169 ---- /* 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 *************** *** 170,182 **** /* 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 --- 172,186 ---- /* 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
pgsql-patches by date: