Index: src/backend/catalog/system_views.sql =================================================================== RCS file: /cvsroot/pgsql/src/backend/catalog/system_views.sql,v retrieving revision 1.48 diff -c -r1.48 system_views.sql *** src/backend/catalog/system_views.sql 1 Jan 2008 19:45:48 -0000 1.48 --- src/backend/catalog/system_views.sql 3 Mar 2008 20:20:28 -0000 *************** *** 173,179 **** SELECT * FROM pg_show_all_settings() AS A (name text, setting text, unit text, category text, short_desc text, extra_desc text, ! context text, vartype text, source text, min_val text, max_val text); CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings --- 173,179 ---- SELECT * FROM pg_show_all_settings() AS A (name text, setting text, unit text, category text, short_desc text, extra_desc text, ! context text, vartype text, source text, min_val text, max_val text, enumvals text); CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.432 diff -c -r1.432 guc.c *** src/backend/utils/misc/guc.c 30 Jan 2008 18:35:55 -0000 1.432 --- src/backend/utils/misc/guc.c 3 Mar 2008 20:20:29 -0000 *************** *** 144,161 **** GucSource source); static const char *assign_session_replication_role(const char *newval, bool doit, GucSource source); - static const char *assign_log_min_messages(const char *newval, bool doit, - GucSource source); - static const char *assign_client_min_messages(const char *newval, - bool doit, GucSource source); - static const char *assign_min_error_statement(const char *newval, bool doit, - GucSource source); - static const char *assign_msglvl(int *var, const char *newval, bool doit, - GucSource source); - static const char *assign_log_error_verbosity(const char *newval, bool doit, - GucSource source); - static const char *assign_log_statement(const char *newval, bool doit, - GucSource source); static const char *show_num_temp_buffers(void); static bool assign_phony_autocommit(bool newval, bool doit, GucSource source); static const char *assign_custom_variable_classes(const char *newval, bool doit, --- 144,149 ---- *************** *** 181,186 **** --- 169,209 ---- static bool assign_maxconnections(int newval, bool doit, GucSource source); /* + * Options for enum values exported from this module + */ + static const struct config_enum_entry config_message_levels[] = { + {"debug", DEBUG2}, + {"debug5", DEBUG5}, + {"debug4", DEBUG4}, + {"debug3", DEBUG3}, + {"debug2", DEBUG2}, + {"debug1", DEBUG1}, + {"log", LOG}, + {"info", INFO}, + {"notice", NOTICE}, + {"warning", WARNING}, + {"error", ERROR}, + {"fatal", FATAL}, + {"panic", PANIC}, + {NULL, 0} + }; + + static const struct config_enum_entry log_error_verbosity_options[] = { + {"default", PGERROR_DEFAULT}, + {"terse", PGERROR_TERSE}, + {"verbose", PGERROR_VERBOSE}, + {NULL, 0} + }; + + static const struct config_enum_entry log_statement_options[] = { + {"none", LOGSTMT_NONE}, + {"ddl", LOGSTMT_DDL}, + {"mod", LOGSTMT_MOD}, + {"all", LOGSTMT_ALL}, + {NULL, 0} + }; + + /* * GUC option variables that are exported from this module */ #ifdef USE_ASSERT_CHECKING *************** *** 230,240 **** * cases provide the value for SHOW to display. The real state is elsewhere * and is kept in sync by assign_hooks. */ - static char *client_min_messages_str; - static char *log_min_messages_str; - static char *log_error_verbosity_str; - static char *log_statement_str; - static char *log_min_error_statement_str; static char *log_destination_string; #ifdef HAVE_SYSLOG --- 253,258 ---- *************** *** 400,406 **** /* PGC_BOOL */ "bool", /* PGC_INT */ "integer", /* PGC_REAL */ "real", ! /* PGC_STRING */ "string" }; --- 418,425 ---- /* PGC_BOOL */ "bool", /* PGC_INT */ "integer", /* PGC_REAL */ "real", ! /* PGC_STRING */ "string", ! /* PGC_ENUM */ "enum" }; *************** *** 1922,1977 **** }, { - {"client_min_messages", PGC_USERSET, LOGGING_WHEN, - gettext_noop("Sets the message levels that are sent to the client."), - gettext_noop("Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, " - "DEBUG1, LOG, NOTICE, WARNING, and ERROR. Each level includes all the " - "levels that follow it. The later the level, the fewer messages are " - "sent.") - }, - &client_min_messages_str, - "notice", assign_client_min_messages, NULL - }, - - { - {"log_min_messages", PGC_SUSET, LOGGING_WHEN, - gettext_noop("Sets the message levels that are logged."), - gettext_noop("Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, " - "INFO, NOTICE, WARNING, ERROR, LOG, FATAL, and PANIC. Each level " - "includes all the levels that follow it.") - }, - &log_min_messages_str, - "notice", assign_log_min_messages, NULL - }, - - { - {"log_error_verbosity", PGC_SUSET, LOGGING_WHEN, - gettext_noop("Sets the verbosity of logged messages."), - gettext_noop("Valid values are \"terse\", \"default\", and \"verbose\".") - }, - &log_error_verbosity_str, - "default", assign_log_error_verbosity, NULL - }, - { - {"log_statement", PGC_SUSET, LOGGING_WHAT, - gettext_noop("Sets the type of statements logged."), - gettext_noop("Valid values are \"none\", \"ddl\", \"mod\", and \"all\".") - }, - &log_statement_str, - "none", assign_log_statement, NULL - }, - - { - {"log_min_error_statement", PGC_SUSET, LOGGING_WHEN, - gettext_noop("Causes all statements generating error at or above this level to be logged."), - gettext_noop("All SQL statements that cause an error of the " - "specified level or a higher level are logged.") - }, - &log_min_error_statement_str, - "error", assign_min_error_statement, NULL - }, - - { {"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Controls information prefixed to each log line."), gettext_noop("If blank, no prefix is used.") --- 1941,1946 ---- *************** *** 2467,2472 **** --- 2436,2503 ---- }; + static struct config_enum ConfigureNamesEnum[] = + { + { + {"client_min_messages", PGC_USERSET, LOGGING_WHEN, + gettext_noop("Sets the message levels that are sent to the client."), + gettext_noop("Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, " + "DEBUG1, LOG, NOTICE, WARNING, and ERROR. Each level includes all the " + "levels that follow it. The later the level, the fewer messages are " + "sent.") + }, + &client_min_messages, + NOTICE, config_message_levels,NULL, NULL + }, + + { + {"log_error_verbosity", PGC_SUSET, LOGGING_WHEN, + gettext_noop("Sets the verbosity of logged messages."), + gettext_noop("Valid values are \"terse\", \"default\", and \"verbose\".") + }, + (int *)&Log_error_verbosity, + PGERROR_DEFAULT, log_error_verbosity_options, NULL, NULL + }, + + { + {"log_min_messages", PGC_SUSET, LOGGING_WHEN, + gettext_noop("Sets the message levels that are logged."), + gettext_noop("Valid values are DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, " + "INFO, NOTICE, WARNING, ERROR, LOG, FATAL, and PANIC. Each level " + "includes all the levels that follow it.") + }, + &log_min_messages, + NOTICE, config_message_levels, NULL, NULL + }, + + { + {"log_min_error_statement", PGC_SUSET, LOGGING_WHEN, + gettext_noop("Causes all statements generating error at or above this level to be logged."), + gettext_noop("All SQL statements that cause an error of the " + "specified level or a higher level are logged.") + }, + &log_min_error_statement, + ERROR, config_message_levels, NULL, NULL + }, + + { + {"log_statement", PGC_SUSET, LOGGING_WHAT, + gettext_noop("Sets the type of statements logged."), + gettext_noop("Valid values are \"none\", \"ddl\", \"mod\", and \"all\".") + }, + (int *)&log_statement, + LOGSTMT_NONE, log_statement_options, NULL, NULL + }, + + + + + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL + } + }; + /******** end of options list ********/ *************** *** 2633,2638 **** --- 2664,2673 ---- &(val->stringval), *((struct config_string *) gconf)->variable); break; + case PGC_ENUM: + val->enumval = + *((struct config_enum *) gconf)->variable; + break; } } *************** *** 2647,2652 **** --- 2682,2688 ---- case PGC_BOOL: case PGC_INT: case PGC_REAL: + case PGC_ENUM: /* no need to do anything */ break; case PGC_STRING: *************** *** 2714,2719 **** --- 2750,2763 ---- num_vars++; } + for (i = 0; ConfigureNamesEnum[i].gen.name; i++) + { + struct config_enum *conf = &ConfigureNamesEnum[i]; + + conf->gen.vartype = PGC_ENUM; + num_vars++; + } + /* * Create table with 20% slack */ *************** *** 2736,2741 **** --- 2780,2788 ---- for (i = 0; ConfigureNamesString[i].gen.name; i++) guc_vars[num_vars++] = &ConfigureNamesString[i].gen; + for (i = 0; ConfigureNamesEnum[i].gen.name; i++) + guc_vars[num_vars++] = &ConfigureNamesEnum[i].gen; + if (guc_variables) free(guc_variables); guc_variables = guc_vars; *************** *** 3086,3091 **** --- 3133,3150 ---- *conf->variable = str; break; } + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + + if (conf->assign_hook) + if (!(*conf->assign_hook) (conf->boot_val, true, + PGC_S_DEFAULT)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, conf->boot_val); + *conf->variable = conf->reset_val = conf->boot_val; + break; + } } } *************** *** 3385,3390 **** --- 3444,3461 ---- conf->gen.source = conf->gen.reset_source; break; } + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + + if (conf->assign_hook) + if (!(*conf->assign_hook) (conf->reset_val, true, + PGC_S_SESSION)) + elog(ERROR, "failed to reset %s", conf->gen.name); + *conf->variable = conf->reset_val; + conf->gen.source = conf->gen.reset_source; + break; + } } if (gconf->flags & GUC_REPORT) *************** *** 3734,3739 **** --- 3805,3827 ---- set_string_field(conf, &stack->masked.stringval, NULL); break; } + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + int newval = newvalue.enumval; + + if (*conf->variable != newval) + { + if (conf->assign_hook) + if (!(*conf->assign_hook) (newval, + true, PGC_S_OVERRIDE)) + elog(LOG, "failed to commit %s", + conf->gen.name); + *conf->variable = newval; + changed = true; + } + break; + } } gconf->source = newsource; *************** *** 4147,4152 **** --- 4235,4312 ---- return result; } + static const char * + config_enum_lookup_value(struct config_enum *record, int val) + { + const struct config_enum_entry *entry = record->options; + while (entry && entry->name) + { + if (entry->val == val) + return entry->name; + entry++; + } + elog(ERROR, "could not find enum option %d for %s", + val, record->gen.name); + return NULL; /* silence compiler */ + } + + static bool + config_enum_lookup_name(struct config_enum *record, const char *value, int *retval) + { + const struct config_enum_entry *entry = record->options; + + if (retval) + *retval = 0; /* suppress compiler warning */ + + while (entry && entry->name) + { + if (!pg_strcasecmp(value, entry->name)) + { + *retval = entry->val; + return TRUE; + } + entry++; + } + return FALSE; + } + + static char * + config_enum_get_options(struct config_enum *record, const char *prefix, const char *suffix) + { + const struct config_enum_entry *entry = record->options; + int len = 0; + char *hintmsg; + + if (!entry || !entry->name) + return NULL; /* Should not happen */ + + while (entry && entry->name) + { + len += strlen(entry->name) + 2; /* string and ", " */ + entry++; + } + + hintmsg = malloc(len + strlen(prefix) + strlen(suffix) + 2); + if (!hintmsg) + return NULL; + + strcpy(hintmsg, prefix); + + entry = record->options; + while (entry && entry->name) + { + strcat(hintmsg, entry->name); + strcat(hintmsg, ", "); + entry++; + } + + /* Replace final comma/space */ + hintmsg[strlen(hintmsg)-2] = '\0'; + strcat(hintmsg, suffix); + + return hintmsg; + } + /* * Sets option `name' to given value. The value should be a string *************** *** 4684,4689 **** --- 4844,4920 ---- free(newval); break; } + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) record; + int newval; + + if (value) + { + if (!config_enum_lookup_name(conf, value, &newval)) + { + char *hintmsg = config_enum_get_options(conf, "Available values: ", "."); + + ereport(elevel, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), + hintmsg ? errhint(hintmsg) : 0)); + + if (hintmsg) + free(hintmsg); + return false; + } + } + else if (source == PGC_S_DEFAULT) + newval = conf->boot_val; + else + { + newval = conf->reset_val; + source = conf->gen.reset_source; + } + + if (conf->assign_hook) + if (!(*conf->assign_hook) (newval, changeVal, source)) + { + ereport(elevel, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for parameter \"%s\": \"%d\"", + name, newval))); + return false; + } + + if (changeVal || makeDefault) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen, action); + if (changeVal) + { + *conf->variable = newval; + conf->gen.source = source; + } + if (makeDefault) + { + GucStack *stack; + + if (conf->gen.reset_source <= source) + { + conf->reset_val = newval; + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + stack->prior.enumval = newval; + stack->source = source; + } + } + } + } + break; + } } if (changeVal && (record->flags & GUC_REPORT)) *************** *** 4748,4753 **** --- 4979,4988 ---- case PGC_STRING: return *((struct config_string *) record)->variable; + + case PGC_ENUM: + return config_enum_lookup_value((struct config_enum *) record, + *((struct config_enum *) record)->variable); } return NULL; } *************** *** 4792,4797 **** --- 5027,5036 ---- case PGC_STRING: return ((struct config_string *) record)->reset_val; + + case PGC_ENUM: + return config_enum_lookup_value((struct config_enum *) record, + ((struct config_enum *) record)->reset_val); } return NULL; } *************** *** 5603,5608 **** --- 5842,5850 ---- /* max_val */ values[10] = NULL; + + /* enumvals */ + values[11] = NULL; } break; *************** *** 5617,5622 **** --- 5859,5867 ---- /* max_val */ snprintf(buffer, sizeof(buffer), "%d", lconf->max); values[10] = pstrdup(buffer); + + /* enumvals */ + values[11] = NULL; } break; *************** *** 5631,5636 **** --- 5876,5884 ---- /* max_val */ snprintf(buffer, sizeof(buffer), "%g", lconf->max); values[10] = pstrdup(buffer); + + /* enumvals */ + values[11] = NULL; } break; *************** *** 5641,5646 **** --- 5889,5910 ---- /* max_val */ values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + } + break; + + case PGC_ENUM: + { + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = config_enum_get_options((struct config_enum *) conf, "", ""); } break; *************** *** 5655,5660 **** --- 5919,5927 ---- /* max_val */ values[10] = NULL; + + /* enumvals */ + values[11] = NULL; } break; } *************** *** 5697,5703 **** * show_all_settings - equiv to SHOW ALL command but implemented as * a Table Function. */ ! #define NUM_PG_SETTINGS_ATTS 11 Datum show_all_settings(PG_FUNCTION_ARGS) --- 5964,5970 ---- * show_all_settings - equiv to SHOW ALL command but implemented as * a Table Function. */ ! #define NUM_PG_SETTINGS_ATTS 12 Datum show_all_settings(PG_FUNCTION_ARGS) *************** *** 5747,5752 **** --- 6014,6021 ---- TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals", + TEXTOID, -1, 0); /* * Generate attribute metadata needed later to produce tuples from raw *************** *** 5939,5944 **** --- 6208,6224 ---- } break; + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) record; + + if(conf->show_hook) + val = (*conf->show_hook) (); + else + val = config_enum_lookup_value(conf, *conf->variable); + } + break; + default: /* just to keep compiler quiet */ val = "???"; *************** *** 5995,6000 **** --- 6275,6289 ---- return *conf->variable != NULL && strcmp(*conf->variable, newvalue) == 0; } + + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) record; + int newval; + + return config_enum_lookup_name(conf, newvalue, &newval) + && *conf->variable == newval; + } } return false; *************** *** 6081,6086 **** --- 6370,6383 ---- fprintf(fp, "%s", *conf->variable); } break; + + case PGC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + + fprintf(fp, "%s", config_enum_lookup_value(gconf, *gconf->variable)); + } + break; } fputc(0, fp); *************** *** 6607,6760 **** } static const char * - assign_log_min_messages(const char *newval, bool doit, GucSource source) - { - return (assign_msglvl(&log_min_messages, newval, doit, source)); - } - - static const char * - assign_client_min_messages(const char *newval, bool doit, GucSource source) - { - return (assign_msglvl(&client_min_messages, newval, doit, source)); - } - - static const char * - assign_min_error_statement(const char *newval, bool doit, GucSource source) - { - return (assign_msglvl(&log_min_error_statement, newval, doit, source)); - } - - static const char * - assign_msglvl(int *var, const char *newval, bool doit, GucSource source) - { - if (pg_strcasecmp(newval, "debug") == 0) - { - if (doit) - (*var) = DEBUG2; - } - else if (pg_strcasecmp(newval, "debug5") == 0) - { - if (doit) - (*var) = DEBUG5; - } - else if (pg_strcasecmp(newval, "debug4") == 0) - { - if (doit) - (*var) = DEBUG4; - } - else if (pg_strcasecmp(newval, "debug3") == 0) - { - if (doit) - (*var) = DEBUG3; - } - else if (pg_strcasecmp(newval, "debug2") == 0) - { - if (doit) - (*var) = DEBUG2; - } - else if (pg_strcasecmp(newval, "debug1") == 0) - { - if (doit) - (*var) = DEBUG1; - } - else if (pg_strcasecmp(newval, "log") == 0) - { - if (doit) - (*var) = LOG; - } - - /* - * Client_min_messages always prints 'info', but we allow it as a value - * anyway. - */ - else if (pg_strcasecmp(newval, "info") == 0) - { - if (doit) - (*var) = INFO; - } - else if (pg_strcasecmp(newval, "notice") == 0) - { - if (doit) - (*var) = NOTICE; - } - else if (pg_strcasecmp(newval, "warning") == 0) - { - if (doit) - (*var) = WARNING; - } - else if (pg_strcasecmp(newval, "error") == 0) - { - if (doit) - (*var) = ERROR; - } - /* We allow FATAL/PANIC for client-side messages too. */ - else if (pg_strcasecmp(newval, "fatal") == 0) - { - if (doit) - (*var) = FATAL; - } - else if (pg_strcasecmp(newval, "panic") == 0) - { - if (doit) - (*var) = PANIC; - } - else - return NULL; /* fail */ - return newval; /* OK */ - } - - static const char * - assign_log_error_verbosity(const char *newval, bool doit, GucSource source) - { - if (pg_strcasecmp(newval, "terse") == 0) - { - if (doit) - Log_error_verbosity = PGERROR_TERSE; - } - else if (pg_strcasecmp(newval, "default") == 0) - { - if (doit) - Log_error_verbosity = PGERROR_DEFAULT; - } - else if (pg_strcasecmp(newval, "verbose") == 0) - { - if (doit) - Log_error_verbosity = PGERROR_VERBOSE; - } - else - return NULL; /* fail */ - return newval; /* OK */ - } - - static const char * - assign_log_statement(const char *newval, bool doit, GucSource source) - { - if (pg_strcasecmp(newval, "none") == 0) - { - if (doit) - log_statement = LOGSTMT_NONE; - } - else if (pg_strcasecmp(newval, "ddl") == 0) - { - if (doit) - log_statement = LOGSTMT_DDL; - } - else if (pg_strcasecmp(newval, "mod") == 0) - { - if (doit) - log_statement = LOGSTMT_MOD; - } - else if (pg_strcasecmp(newval, "all") == 0) - { - if (doit) - log_statement = LOGSTMT_ALL; - } - else - return NULL; /* fail */ - return newval; /* OK */ - } - - static const char * show_num_temp_buffers(void) { /* --- 6904,6909 ---- Index: src/include/utils/guc.h =================================================================== RCS file: /cvsroot/pgsql/src/include/utils/guc.h,v retrieving revision 1.90 diff -c -r1.90 guc.h *** src/include/utils/guc.h 1 Jan 2008 19:45:59 -0000 1.90 --- src/include/utils/guc.h 3 Mar 2008 20:20:29 -0000 *************** *** 97,102 **** --- 97,103 ---- typedef bool (*GucBoolAssignHook) (bool newval, bool doit, GucSource source); typedef bool (*GucIntAssignHook) (int newval, bool doit, GucSource source); typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source); + typedef bool (*GucEnumAssignHook) (int newval, bool doit, GucSource source); typedef const char *(*GucShowHook) (void); Index: src/include/utils/guc_tables.h =================================================================== RCS file: /cvsroot/pgsql/src/include/utils/guc_tables.h,v retrieving revision 1.38 diff -c -r1.38 guc_tables.h *** src/include/utils/guc_tables.h 1 Jan 2008 19:45:59 -0000 1.38 --- src/include/utils/guc_tables.h 3 Mar 2008 20:20:29 -0000 *************** *** 24,30 **** PGC_BOOL, PGC_INT, PGC_REAL, ! PGC_STRING }; union config_var_value --- 24,31 ---- PGC_BOOL, PGC_INT, PGC_REAL, ! PGC_STRING, ! PGC_ENUM }; union config_var_value *************** *** 33,38 **** --- 34,49 ---- int intval; double realval; char *stringval; + int enumval; + }; + + /* + * Enum values are made up of an array of name-value pairs + */ + struct config_enum_entry + { + const char *name; + int val; }; /* *************** *** 210,215 **** --- 221,239 ---- char *reset_val; }; + struct config_enum + { + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + int *variable; + int boot_val; + const struct config_enum_entry *options; + GucEnumAssignHook assign_hook; + GucShowHook show_hook; + /* variable fields, initialized at runtime: */ + int reset_val; + }; + /* constant tables corresponding to enums above and in guc.h */ extern const char *const config_group_names[]; extern const char *const config_type_names[];