From fb55af227e9e3c9e348aedef6455bd38e0fc04a0 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 12 Aug 2006 04:12:41 +0000 Subject: [PATCH] Back out patch to reorganize guc processing. Was causing regression failures. --- src/backend/utils/misc/guc-file.l | 74 +--- src/backend/utils/misc/guc.c | 584 ++++++++++++------------------ src/include/utils/guc.h | 5 +- 3 files changed, 249 insertions(+), 414 deletions(-) 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,258 +3690,96 @@ 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. + * 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. */ -static bool -parse_value(int elevel, const struct config_generic *record, - const char *value, GucSource *source, bool changeVal, - union config_var_value *retval) +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; - Assert( !(changeVal && retval==NULL) ); - /* - * Evaluate value and set variable. - */ - switch (record->vartype) + if (context == PGC_SIGHUP || source == PGC_S_DEFAULT) { - 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; - } - } + /* + * 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; - if ( !changeVal ) - free(newval); - if( retval != NULL ) - retval->stringval= newval; - break; - } + record = find_option(name, elevel); + if (record == NULL) + { + ereport(elevel, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("unrecognized configuration parameter \"%s\"", name))); + return false; } - 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) -{ + /* + * 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", - record->name))); + name))); return false; } break; case PGC_POSTMASTER: if (context == PGC_SIGHUP) - return false; + { + 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", - record->name))); + name))); return false; } break; @@ -3951,7 +3789,7 @@ checkContext(int elevel, struct config_generic *record, GucContext context) ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed now", - record->name))); + name))); return false; } @@ -3974,16 +3812,14 @@ checkContext(int elevel, struct config_generic *record, GucContext context) * backend start. */ if (IsUnderPostmaster) - { - return false; - } + 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", - record->name))); + name))); return false; } break; @@ -3993,7 +3829,7 @@ checkContext(int elevel, struct config_generic *record, GucContext context) ereport(elevel, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set parameter \"%s\"", - record->name))); + name))); return false; } break; @@ -4001,115 +3837,6 @@ checkContext(int elevel, struct config_generic *record, GucContext context) /* 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 - * 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(elevel, record, context) ) - return false; /* * 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); -- 2.40.0