Tom Lane wrote: > Michael Fuhr <[EMAIL PROTECTED]> writes: > > The latest HEAD is segfaulting on startup if I have the following > > lines in postgresql.conf: > > > custom_variable_classes = 'plperl' > > plperl.use_strict = on > > Bruce, please re-revert that GUC patch and don't put it back in until > someone like Peter or me has actually reviewed it. My faith in it has > gone to zero, and I don't think you are able to fix it either.
Peter had already reviewed it and given comments. There were three things wrong with the original patch: o spacing, e.g. "if( x =- 1 )" o an incorrect API for memory freeing by parse_value() o verify_config_option() didn't consider custom variables These have all been corrected, so I don't see the value in removing the patch now that it is working. I have attached the three GUC patches that were applied to CVS, so if someone wants corrections or removal based on specific issues, please let me know. > BTW, do I need to mention that the plperl patch is breaking the > buildfarm again? That has been reverted, because of the regression failures and Andrew's analysis. -- Bruce Momjian [EMAIL PROTECTED] EnterpriseDB http://www.enterprisedb.com + If your life is a hard drive, Christ can be your backup. +
Index: src/backend/utils/misc/guc-file.l =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v retrieving revision 1.42 retrieving revision 1.43 diff -c -r1.42 -r1.43 *** src/backend/utils/misc/guc-file.l 12 Aug 2006 04:12:41 -0000 1.42 --- src/backend/utils/misc/guc-file.l 13 Aug 2006 01:30:17 -0000 1.43 *************** *** 4,10 **** * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.42 2006/08/12 04:12:41 momjian Exp $ */ %{ --- 4,10 ---- * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.43 2006/08/13 01:30:17 momjian Exp $ */ %{ *************** *** 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); *************** *** 114,121 **** void ProcessConfigFile(GucContext context) { ! int elevel; struct name_value_pair *item, *head, *tail; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); --- 115,124 ---- void ProcessConfigFile(GucContext context) { ! int elevel, i; struct name_value_pair *item, *head, *tail; + bool *apply_list = NULL; + int varcount = 0; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); *************** *** 134,158 **** 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); } --- 137,192 ---- 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; + } + + 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); ! ! cleanup_list: ! if (apply_list) ! pfree(apply_list); free_name_value_list(head); } *************** *** 189,201 **** 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 --- 223,236 ---- 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 *************** *** 289,295 **** if (!ParseConfigFile(opt_value, config_file, depth + 1, context, elevel, ! head_p, tail_p)) { pfree(opt_name); pfree(opt_value); --- 324,330 ---- if (!ParseConfigFile(opt_value, config_file, depth + 1, context, elevel, ! head_p, tail_p, varcount)) { pfree(opt_name); pfree(opt_value); *************** *** 333,338 **** --- 368,374 ---- else (*tail_p)->next = item; *tail_p = item; + (*varcount)++; } /* break out of loop if read EOF, else loop for next line */ Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.337 retrieving revision 1.338 diff -c -r1.337 -r1.338 *** src/backend/utils/misc/guc.c 12 Aug 2006 04:12:41 -0000 1.337 --- src/backend/utils/misc/guc.c 13 Aug 2006 01:30:17 -0000 1.338 *************** *** 10,16 **** * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.337 2006/08/12 04:12:41 momjian Exp $ * *-------------------------------------------------------------------- */ --- 10,16 ---- * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.338 2006/08/13 01:30:17 momjian Exp $ * *-------------------------------------------------------------------- */ *************** *** 2313,2319 **** * states). */ static void ! set_string_field(struct config_string * conf, char **field, char *newval) { char *oldval = *field; GucStack *stack; --- 2313,2319 ---- * states). */ static void ! set_string_field(struct config_string *conf, char **field, char *newval) { char *oldval = *field; GucStack *stack; *************** *** 2535,2542 **** gen = &var->gen; memset(var, 0, sz); ! gen->name = guc_strdup(elevel, name); ! if (gen->name == NULL) { free(var); return NULL; --- 2535,2541 ---- gen = &var->gen; memset(var, 0, sz); ! if ((gen->name = guc_strdup(elevel, name)) == NULL) { free(var); return NULL; *************** *** 3690,3785 **** 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 ! * 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; --- 3689,3942 ---- 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) { ! /* ! * 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) ! 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) ! 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) ! retval->realval = newval; ! break; ! } ! ! case PGC_STRING: ! { ! struct config_string *conf = (struct config_string *) record; ! char *newval; ! ! if (value) ! { ! if ((newval = guc_strdup(elevel, value)) == 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. ! */ ! if ((newval = guc_strdup(elevel, conf->reset_val)) == 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 (retval) ! retval->stringval = newval; ! else ! free(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; *************** *** 3789,3795 **** ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed now", ! name))); return false; } --- 3946,3952 ---- ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed now", ! record->name))); return false; } *************** *** 3812,3825 **** * 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; --- 3969,3984 ---- * 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; *************** *** 3829,3835 **** ereport(elevel, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set parameter \"%s\"", ! name))); return false; } break; --- 3988,3994 ---- ereport(elevel, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to set parameter \"%s\"", ! record->name))); return false; } break; *************** *** 3837,3842 **** --- 3996,4109 ---- /* 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) + { + 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, NULL)) + { + 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 *************** *** 3871,3914 **** { 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); if (changeVal) { *conf->variable = newval; conf->gen.source = source; } if (makeDefault) { GucStack *stack; --- 4138,4160 ---- { 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); + if (changeVal) { *conf->variable = newval; conf->gen.source = source; } + if (makeDefault) { GucStack *stack; *************** *** 3948,3998 **** 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", ! 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); if (changeVal) { *conf->variable = newval; conf->gen.source = source; } if (makeDefault) { GucStack *stack; --- 4194,4215 ---- 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); + if (changeVal) { *conf->variable = newval; conf->gen.source = source; } + if (makeDefault) { GucStack *stack; *************** *** 4032,4082 **** 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); if (changeVal) { *conf->variable = newval; conf->gen.source = source; } if (makeDefault) { GucStack *stack; --- 4249,4270 ---- 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); + if (changeVal) { *conf->variable = newval; conf->gen.source = source; } + if (makeDefault) { GucStack *stack; *************** *** 4116,4197 **** 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); if (changeVal) { set_string_field(conf, conf->variable, newval); conf->gen.source = source; } if (makeDefault) { GucStack *stack; --- 4304,4325 ---- 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) { /* Save old value to support transaction abort */ if (!makeDefault) push_old_value(&conf->gen); + if (changeVal) { set_string_field(conf, conf->variable, newval); conf->gen.source = source; } + if (makeDefault) { GucStack *stack; *************** *** 4767,4774 **** /* need a tuple descriptor representing a single TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, ! TEXTOID, -1, 0); } return tupdesc; } --- 4895,4901 ---- /* need a tuple descriptor representing a single TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); ! TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); } return tupdesc; } *************** *** 4860,4866 **** /* send it to dest */ do_tup_output(tstate, values); - /* clean up */ if (values[1] != NULL) pfree(values[1]); } --- 4987,4992 ---- *************** *** 5314,5319 **** --- 5440,5448 ---- 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: /cvsroot/pgsql/src/include/utils/guc.h,v retrieving revision 1.73 retrieving revision 1.74 diff -c -r1.73 -r1.74 *** src/include/utils/guc.h 12 Aug 2006 04:12:41 -0000 1.73 --- src/include/utils/guc.h 13 Aug 2006 01:30:17 -0000 1.74 *************** *** 7,13 **** * Copyright (c) 2000-2006, PostgreSQL Global Development Group * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * ! * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.73 2006/08/12 04:12:41 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H --- 7,13 ---- * Copyright (c) 2000-2006, PostgreSQL Global Development Group * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * ! * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.74 2006/08/13 01:30:17 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H *************** *** 193,198 **** --- 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);
Index: src/backend/utils/misc/guc-file.l =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v retrieving revision 1.43 retrieving revision 1.44 diff -c -r1.43 -r1.44 *** src/backend/utils/misc/guc-file.l 13 Aug 2006 01:30:17 -0000 1.43 --- src/backend/utils/misc/guc-file.l 13 Aug 2006 02:22:24 -0000 1.44 *************** *** 4,10 **** * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.43 2006/08/13 01:30:17 momjian Exp $ */ %{ --- 4,10 ---- * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.44 2006/08/13 02:22:24 momjian Exp $ */ %{ *************** *** 117,122 **** --- 117,123 ---- { int elevel, i; struct name_value_pair *item, *head, *tail; + char *env; bool *apply_list = NULL; int varcount = 0; *************** *** 183,188 **** --- 184,242 ---- 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->reset_source == PGC_S_FILE && + !(gconf->status & GUC_IN_CONFFILE)) + { + if (gconf->context == PGC_BACKEND && IsUnderPostmaster) + ; /* Be silent. Does any body want message from each session? */ + else 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; + + 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 returned 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) Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.338 retrieving revision 1.339 diff -c -r1.338 -r1.339 *** src/backend/utils/misc/guc.c 13 Aug 2006 01:30:17 -0000 1.338 --- src/backend/utils/misc/guc.c 13 Aug 2006 02:22:24 -0000 1.339 *************** *** 10,16 **** * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.338 2006/08/13 01:30:17 momjian Exp $ * *-------------------------------------------------------------------- */ --- 10,16 ---- * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.339 2006/08/13 02:22:24 momjian Exp $ * *-------------------------------------------------------------------- */ *************** *** 2692,2731 **** { 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: --- 2692,2731 ---- { struct config_bool *conf = (struct config_bool *) gconf; ! if (conf->assign_hook && ! !(*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 && ! !(*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 && ! !(*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: *************** *** 2738,2747 **** conf->tentative_val = NULL; if (conf->boot_val == NULL) - { /* Cannot set value yet */ break; - } str = guc_strdup(FATAL, conf->boot_val); conf->reset_val = str; --- 2738,2745 ---- *************** *** 2753,2762 **** newstr = (*conf->assign_hook) (str, true, PGC_S_DEFAULT); if (newstr == NULL) - { elog(FATAL, "failed to initialize %s to \"%s\"", conf->gen.name, str); - } else if (newstr != str) { free(str); --- 2751,2758 ---- *************** *** 2796,2807 **** if (env != NULL) SetConfigOption("port", env, PGC_POSTMASTER, PGC_S_ENV_VAR); ! env = getenv("PGDATESTYLE"); ! if (env != NULL) SetConfigOption("datestyle", env, PGC_POSTMASTER, PGC_S_ENV_VAR); ! env = getenv("PGCLIENTENCODING"); ! if (env != NULL) SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR); } --- 2792,2801 ---- if (env != NULL) SetConfigOption("port", env, PGC_POSTMASTER, PGC_S_ENV_VAR); ! if ((env = getenv("PGDATESTYLE")) != NULL) SetConfigOption("datestyle", env, PGC_POSTMASTER, PGC_S_ENV_VAR); ! if ((env = getenv("PGCLIENTENCODING")) != NULL) SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR); } *************** *** 3178,3184 **** 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; --- 3172,3178 ---- 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; *************** *** 3723,3734 **** } 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), --- 3717,3738 ---- } 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 && ! !(*conf->assign_hook) (newval, changeVal, *source)) { ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), *************** *** 3767,3774 **** } else { ! newval = conf->reset_val; ! *source = conf->gen.reset_source; } if (conf->assign_hook) --- 3771,3788 ---- } 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) *************** *** 3811,3822 **** } 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), --- 3825,3846 ---- } 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 && ! !(*conf->assign_hook) (newval, changeVal, *source)) { ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), *************** *** 3845,3850 **** --- 3869,3886 ---- 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) { /* *************** *** 3856,3865 **** *source = conf->gen.reset_source; } else - { /* Nothing to reset to, as yet; so do nothing */ break; - } if (conf->assign_hook) { --- 3892,3899 ---- *************** *** 4047,4052 **** --- 4081,4093 ---- if (parse_value(elevel, record, value, &source, false, NULL)) { + /* + * Mark record like presented in the config file. Be carefull if + * you use this function for another purpose than config file + * verification. It causes confusion configfile parser. + */ + record->status |= GUC_IN_CONFFILE; + if (isNewEqual != NULL) *isNewEqual = is_newvalue_equal(record, value); if (isContextOK != NULL) *************** *** 4109,4115 **** * 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. --- 4150,4157 ---- * 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. Index: src/include/utils/guc_tables.h =================================================================== RCS file: /cvsroot/pgsql/src/include/utils/guc_tables.h,v retrieving revision 1.26 retrieving revision 1.27 diff -c -r1.26 -r1.27 *** src/include/utils/guc_tables.h 12 Aug 2006 04:11:50 -0000 1.26 --- src/include/utils/guc_tables.h 13 Aug 2006 02:22:24 -0000 1.27 *************** *** 7,13 **** * * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.26 2006/08/12 04:11:50 momjian Exp $ * *------------------------------------------------------------------------- */ --- 7,13 ---- * * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * ! * $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.27 2006/08/13 02:22:24 momjian Exp $ * *------------------------------------------------------------------------- */ *************** *** 143,149 **** #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 */ --- 143,150 ---- #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 */ *************** *** 153,163 **** /* 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 --- 154,165 ---- /* 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 *************** *** 166,178 **** /* 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 --- 168,181 ---- /* 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 *************** *** 181,193 **** /* 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 --- 184,197 ---- /* 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
Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.339 retrieving revision 1.340 diff -c -r1.339 -r1.340 *** src/backend/utils/misc/guc.c 13 Aug 2006 02:22:24 -0000 1.339 --- src/backend/utils/misc/guc.c 13 Aug 2006 15:37:02 -0000 1.340 *************** *** 10,16 **** * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.339 2006/08/13 02:22:24 momjian Exp $ * *-------------------------------------------------------------------- */ --- 10,16 ---- * Written by Peter Eisentraut <[EMAIL PROTECTED]>. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.340 2006/08/13 15:37:02 momjian Exp $ * *-------------------------------------------------------------------- */ *************** *** 4082,4090 **** if (parse_value(elevel, record, value, &source, false, NULL)) { /* ! * Mark record like presented in the config file. Be carefull if * you use this function for another purpose than config file ! * verification. It causes confusion configfile parser. */ record->status |= GUC_IN_CONFFILE; --- 4082,4090 ---- if (parse_value(elevel, record, value, &source, false, NULL)) { /* ! * Mark record as present in the config file. Be carefull if * you use this function for another purpose than config file ! * verification. It causes confusion in the config file parser. */ record->status |= GUC_IN_CONFFILE; *************** *** 5512,5518 **** { struct config_string *conf = (struct config_string *) record; ! return strcmp(*conf->variable, newvalue) == 0; } } --- 5512,5521 ---- { struct config_string *conf = (struct config_string *) record; ! if (!*conf->variable) /* custom variable with no value yet */ ! return false; ! else ! return strcmp(*conf->variable, newvalue) == 0; } }
---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match