diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index 089018d2c5..c53cf30999 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -4,7 +4,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.41 2006/08/12 04:11:50 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.42 2006/08/12 04:12:41 momjian Exp $ */ %{ @@ -50,8 +50,7 @@ int GUC_yylex(void); 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); + struct name_value_pair **tail_p); static void free_name_value_list(struct name_value_pair * list); static char *GUC_scanstr(const char *s); @@ -115,10 +114,8 @@ STRING \'([^'\\\n]|\\.|\'\')*\' void ProcessConfigFile(GucContext context) { - int elevel, i; + int elevel; struct name_value_pair *item, *head, *tail; - bool *apply_list = NULL; - int varcount = 0; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); @@ -137,56 +134,25 @@ ProcessConfigFile(GucContext context) if (!ParseConfigFile(ConfigFileName, NULL, 0, context, elevel, - &head, &tail, &varcount)) + &head, &tail)) 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++) + 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"))); + if (!set_config_option(item->name, item->value, context, + PGC_S_FILE, false, false)) goto cleanup_list; - } - - if( isContextOk == false ) - { - apply_list[i] = 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, context must be valid for all - * configuration item. */ - goto cleanup_list; - } - else - apply_list[i] = true; } /* 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); + for (item = head; item; item = item->next) + { + set_config_option(item->name, item->value, context, + PGC_S_FILE, false, true); + } - -cleanup_list: - if (apply_list) - pfree(apply_list); + cleanup_list: free_name_value_list(head); } @@ -223,14 +189,13 @@ 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) + struct name_value_pair **tail_p) { - bool OK = true; - char abs_path[MAXPGPATH]; - FILE *fp; + bool OK = true; + char abs_path[MAXPGPATH]; + FILE *fp; YY_BUFFER_STATE lex_buffer; - int token; + int token; /* * Reject too-deep include nesting depth. This is just a safety check @@ -324,7 +289,7 @@ ParseConfigFile(const char *config_file, const char *calling_file, if (!ParseConfigFile(opt_value, config_file, depth + 1, context, elevel, - head_p, tail_p, varcount)) + head_p, tail_p)) { pfree(opt_name); pfree(opt_value); @@ -368,7 +333,6 @@ ParseConfigFile(const char *config_file, const char *calling_file, else (*tail_p)->next = item; *tail_p = item; - (*varcount)++; } /* break out of loop if read EOF, else loop for next line */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4ecebddd76..abf993d2fd 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.336 2006/08/12 04:11:50 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.337 2006/08/12 04:12:41 momjian Exp $ * *-------------------------------------------------------------------- */ @@ -3690,380 +3690,6 @@ call_string_assign_hook(GucStringAssignHook assign_hook, 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 - { - 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, conf->gen.flags)) - { - 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 - { - 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 - { - 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 (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; -} - -/* - * Check if the option can be set at this time. See guc.h for the precise - * rules. - */ -static bool -checkContext(int elevel, struct config_generic *record, GucContext context) -{ - 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; - case PGC_SIGHUP: - if (context != PGC_SIGHUP && context != PGC_POSTMASTER) - { - ereport(elevel, - (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), - errmsg("parameter \"%s\" cannot be changed now", - record->name))); - return false; - } - - /* - * Hmm, the idea of the SIGHUP context is "ought to be global, but - * can be changed after postmaster start". But there's nothing - * that prevents a crafty administrator from sending SIGHUP - * signals to individual backends only. - */ - break; - case PGC_BACKEND: - if (context == PGC_SIGHUP) - { - /* - * If a PGC_BACKEND parameter is changed in the config file, - * we want to accept the new value in the postmaster (whence - * it will propagate to subsequently-started backends), but - * ignore it in existing backends. This is a tad klugy, but - * necessary because we don't re-read the config file during - * 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; - case PGC_SUSET: - if (context == PGC_USERSET || context == PGC_BACKEND) - { - ereport(elevel, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set parameter \"%s\"", - record->name))); - return false; - } - break; - case PGC_USERSET: - /* always okay */ - break; - } - return true; -} - -/* - * Get error level for different sources and context. - */ -static 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 @@ -4096,7 +3722,18 @@ set_config_option(const char *name, const char *value, int elevel; bool makeDefault; - elevel = get_elevel(context, source); + 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) @@ -4107,9 +3744,99 @@ set_config_option(const char *name, const char *value, return false; } - /* Check if change is allowed in the running context. */ - if( !checkContext(elevel, record, context) ) - 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 + * context. In that case we just ignore the attempt and return true. + */ + 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; + case PGC_SIGHUP: + if (context != PGC_SIGHUP && context != PGC_POSTMASTER) + { + ereport(elevel, + (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), + errmsg("parameter \"%s\" cannot be changed now", + name))); + return false; + } + + /* + * Hmm, the idea of the SIGHUP context is "ought to be global, but + * can be changed after postmaster start". But there's nothing + * that prevents a crafty administrator from sending SIGHUP + * signals to individual backends only. + */ + break; + case PGC_BACKEND: + if (context == PGC_SIGHUP) + { + /* + * If a PGC_BACKEND parameter is changed in the config file, + * we want to accept the new value in the postmaster (whence + * it will propagate to subsequently-started backends), but + * ignore it in existing backends. This is a tad klugy, but + * necessary because we don't re-read the config file during + * 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; + case PGC_SUSET: + if (context == PGC_USERSET || context == PGC_BACKEND) + { + ereport(elevel, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to set parameter \"%s\"", + name))); + return false; + } + break; + case PGC_USERSET: + /* always okay */ + break; + } /* * Should we set reset/stacked values? (If so, the behavior is not @@ -4144,9 +3871,33 @@ set_config_option(const char *name, const char *value, { 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 (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) { @@ -4197,8 +3948,40 @@ set_config_option(const char *name, const char *value, 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 (value) + { + if (!parse_int(value, &newval, conf->gen.flags)) + { + 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) { @@ -4249,8 +4032,40 @@ set_config_option(const char *name, const char *value, 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 (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) { @@ -4301,8 +4116,71 @@ set_config_option(const char *name, const char *value, 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 (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) { @@ -4350,8 +4228,7 @@ set_config_option(const char *name, const char *value, } } else - if( newval != NULL ) - free(newval); + free(newval); break; } } @@ -5437,9 +5314,6 @@ _ShowOption(struct config_generic * record, bool use_units) static bool is_newvalue_equal(struct config_generic *record, const char *newvalue) { - if( !newvalue ) - return false; - switch (record->vartype) { case PGC_BOOL: diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index f5bbf81312..9e770b4998 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2006, PostgreSQL Global Development Group * Written by Peter Eisentraut . * - * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.72 2006/08/11 20:08:28 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.73 2006/08/12 04:12:41 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H @@ -193,9 +193,6 @@ extern void ParseLongOption(const char *string, char **name, char **value); 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);