X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fmisc%2Fguc.c;h=38d577f87bb9a892a8908bc8f7d3dcc2eafe02bb;hb=901be0fad4034c9cf8a3588fd6cf2ece82e4b8ce;hp=40b087a8f3adc541c3442c931a73be63ca90fabd;hpb=fa40ca42a6bc3da349eaf661ef9e25c0064d110d;p=postgresql diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 40b087a8f3..38d577f87b 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -6,11 +6,11 @@ * See src/backend/utils/misc/README for more information. * * - * Copyright (c) 2000-2009, PostgreSQL Global Development Group + * Copyright (c) 2000-2010, PostgreSQL Global Development Group * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.495 2009/01/21 09:28:26 mha Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.532 2010/01/07 04:53:35 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -38,16 +38,16 @@ #include "commands/trigger.h" #include "funcapi.h" #include "libpq/auth.h" +#include "libpq/be-fsstubs.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "optimizer/cost.h" #include "optimizer/geqo.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" -#include "parser/gramparse.h" #include "parser/parse_expr.h" -#include "parser/parse_relation.h" #include "parser/parse_type.h" +#include "parser/parser.h" #include "parser/scansup.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -55,12 +55,12 @@ #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" -#include "regex/regex.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "tsearch/ts_cache.h" #include "utils/builtins.h" +#include "utils/bytea.h" #include "utils/guc_tables.h" #include "utils/memutils.h" #include "utils/pg_locale.h" @@ -87,7 +87,8 @@ #endif /* upper limit for GUC variables measured in kilobytes of memory */ -#if SIZEOF_SIZE_T > 4 +/* note that various places assume the byte size fits in a "long" variable */ +#if SIZEOF_SIZE_T > 4 && SIZEOF_LONG > 4 #define MAX_KILOBYTES INT_MAX #else #define MAX_KILOBYTES (INT_MAX / 1024) @@ -114,6 +115,9 @@ extern char *default_tablespace; extern char *temp_tablespaces; extern bool synchronize_seqscans; extern bool fullPageWrites; +extern int vacuum_defer_cleanup_age; + +int trace_recovery_messages = LOG; #ifdef TRACE_SORT extern bool trace_sort; @@ -139,7 +143,7 @@ static const char *assign_log_destination(const char *value, static int syslog_facility = LOG_LOCAL0; static bool assign_syslog_facility(int newval, - bool doit, GucSource source); + bool doit, GucSource source); static const char *assign_syslog_ident(const char *ident, bool doit, GucSource source); #endif @@ -151,6 +155,7 @@ static bool assign_phony_autocommit(bool newval, bool doit, GucSource source); static const char *assign_custom_variable_classes(const char *newval, bool doit, GucSource source); static bool assign_debug_assertions(bool newval, bool doit, GucSource source); +static bool assign_bonjour(bool newval, bool doit, GucSource source); static bool assign_ssl(bool newval, bool doit, GucSource source); static bool assign_stage_log_stats(bool newval, bool doit, GucSource source); static bool assign_log_stats(bool newval, bool doit, GucSource source); @@ -168,10 +173,11 @@ static bool assign_maxconnections(int newval, bool doit, GucSource source); static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source); static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source); static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source); +static const char *assign_application_name(const char *newval, bool doit, GucSource source); -static char *config_enum_get_options(struct config_enum *record, - const char *prefix, const char *suffix, - const char *separator); +static char *config_enum_get_options(struct config_enum * record, + const char *prefix, const char *suffix, + const char *separator); /* @@ -180,6 +186,12 @@ static char *config_enum_get_options(struct config_enum *record, * NOTE! Option values may not contain double quotes! */ +static const struct config_enum_entry bytea_output_options[] = { + {"escape", BYTEA_OUTPUT_ESCAPE, false}, + {"hex", BYTEA_OUTPUT_HEX, false}, + {NULL, 0, false} +}; + /* * We have different sets for client and server message level options because * they sort slightly different (see "log" level) @@ -241,13 +253,6 @@ static const struct config_enum_entry log_statement_options[] = { {NULL, 0, false} }; -static const struct config_enum_entry regex_flavor_options[] = { - {"advanced", REG_ADVANCED, false}, - {"extended", REG_EXTENDED, false}, - {"basic", REG_BASIC, false}, - {NULL, 0, false} -}; - static const struct config_enum_entry isolation_level_options[] = { {"serializable", XACT_SERIALIZABLE, false}, {"repeatable read", XACT_REPEATABLE_READ, false}, @@ -377,6 +382,10 @@ char *external_pid_file; char *pgstat_temp_directory; +char *default_do_language; + +char *application_name; + int tcp_keepalives_idle; int tcp_keepalives_interval; int tcp_keepalives_count; @@ -450,6 +459,7 @@ const char *const GucSource_Names[] = /* PGC_S_ARGV */ "command line", /* PGC_S_DATABASE */ "database", /* PGC_S_USER */ "user", + /* PGC_S_DATABASE_USER */ "database user", /* PGC_S_CLIENT */ "client", /* PGC_S_OVERRIDE */ "override", /* PGC_S_INTERACTIVE */ "interactive", @@ -674,6 +684,14 @@ static struct config_bool ConfigureNamesBool[] = &session_auth_is_superuser, false, NULL, NULL }, + { + {"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS, + gettext_noop("Enables advertising the server via Bonjour."), + NULL + }, + &enable_bonjour, + false, assign_bonjour, NULL + }, { {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY, gettext_noop("Enables SSL connections."), @@ -727,7 +745,7 @@ static struct config_bool ConfigureNamesBool[] = true, NULL, NULL }, { - {"silent_mode", PGC_POSTMASTER, LOGGING_WHEN, + {"silent_mode", PGC_POSTMASTER, LOGGING_WHERE, gettext_noop("Runs the server silently."), gettext_noop("If this parameter is set, the server will automatically run in the " "background and any controlling terminals are dissociated.") @@ -1038,14 +1056,6 @@ static struct config_bool ConfigureNamesBool[] = &XactReadOnly, false, assign_transaction_read_only, NULL }, - { - {"add_missing_from", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, - gettext_noop("Automatically adds missing table references to FROM clauses."), - NULL - }, - &add_missing_from, - false, NULL, NULL - }, { {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Check function bodies during CREATE FUNCTION."), @@ -1200,6 +1210,17 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, + { + {"recovery_connections", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("During recovery, allows connections and queries. " + " During normal running, causes additional info to be written" + " to WAL to enable hot standby mode on WAL standby nodes."), + NULL + }, + &XLogRequestRecoveryConnections, + true, NULL, NULL + }, + { {"allow_system_table_mods", PGC_POSTMASTER, DEVELOPER_OPTIONS, gettext_noop("Allows modifications of the structure of system tables."), @@ -1221,6 +1242,16 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, + { + {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS, + gettext_noop("Enables backward compatibility mode for privilege checks on large objects"), + gettext_noop("Skips privilege checks when reading or modifying large objects, " + "for compatibility with PostgreSQL releases prior to 8.5.") + }, + &lo_compat_privileges, + false, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL @@ -1328,9 +1359,11 @@ static struct config_int ConfigureNamesInt[] = * Note: MaxBackends is limited to INT_MAX/4 because some places compute * 4*MaxBackends without any overflow check. This check is made in * assign_maxconnections, since MaxBackends is computed as MaxConnections - * plus autovacuum_max_workers. + * plus autovacuum_max_workers plus one (for the autovacuum launcher). * * Likewise we have to limit NBuffers to INT_MAX/2. + * + * See also CheckRequiredParameterValues() if this parameter changes */ { {"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS, @@ -1341,6 +1374,15 @@ static struct config_int ConfigureNamesInt[] = 100, 1, INT_MAX / 4, assign_maxconnections, NULL }, + { + {"max_standby_delay", PGC_SIGHUP, WAL_SETTINGS, + gettext_noop("Sets the maximum delay to avoid conflict processing on Hot Standby servers."), + NULL + }, + &MaxStandbyDelay, + 30, -1, INT_MAX, NULL, NULL + }, + { {"superuser_reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS, gettext_noop("Sets the number of connection slots reserved for superusers."), @@ -1467,7 +1509,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &VacuumCostDelay, - 0, 0, 1000, NULL, NULL + 0, 0, 100, NULL, NULL }, { @@ -1477,7 +1519,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &autovacuum_vac_cost_delay, - 20, -1, 1000, NULL, NULL + 20, -1, 100, NULL, NULL }, { @@ -1498,13 +1540,16 @@ static struct config_int ConfigureNamesInt[] = 1000, 25, INT_MAX, NULL, NULL }, + /* + * See also CheckRequiredParameterValues() if this parameter changes + */ { {"max_prepared_transactions", PGC_POSTMASTER, RESOURCES, gettext_noop("Sets the maximum number of simultaneously prepared transactions."), NULL }, &max_prepared_xacts, - 5, 0, INT_MAX, NULL, NULL + 0, 0, INT_MAX / 4, NULL, NULL }, #ifdef LOCK_DEBUG @@ -1556,6 +1601,18 @@ static struct config_int ConfigureNamesInt[] = 150000000, 0, 2000000000, NULL, NULL }, + { + {"vacuum_defer_cleanup_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Age by which VACUUM and HOT cleanup should be deferred, if any."), + NULL + }, + &vacuum_defer_cleanup_age, + 0, 0, 1000000, NULL, NULL + }, + + /* + * See also CheckRequiredParameterValues() if this parameter changes + */ { {"max_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT, gettext_noop("Sets the maximum number of locks per transaction."), @@ -1668,7 +1725,7 @@ static struct config_int ConfigureNamesInt[] = "(FLT_DIG or DBL_DIG as appropriate).") }, &extra_float_digits, - 0, -15, 2, NULL, NULL + 0, -15, 3, NULL, NULL }, { @@ -1713,7 +1770,13 @@ static struct config_int ConfigureNamesInt[] = }, { - {"effective_io_concurrency", PGC_USERSET, RESOURCES, + {"effective_io_concurrency", +#ifdef USE_PREFETCH + PGC_USERSET, +#else + PGC_INTERNAL, +#endif + RESOURCES, gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."), gettext_noop("For RAID arrays, this should be approximately the number of drive spindles in the array.") }, @@ -1788,9 +1851,9 @@ static struct config_int ConfigureNamesInt[] = { {"segment_size", PGC_INTERNAL, PRESET_OPTIONS, - gettext_noop("Shows the number of pages per disk file."), - NULL, - GUC_UNIT_BLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + gettext_noop("Shows the number of pages per disk file."), + NULL, + GUC_UNIT_BLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &segment_size, RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE, NULL, NULL @@ -1813,8 +1876,8 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &wal_segment_size, - (XLOG_SEG_SIZE / XLOG_BLCKSZ), - (XLOG_SEG_SIZE / XLOG_BLCKSZ), + (XLOG_SEG_SIZE / XLOG_BLCKSZ), + (XLOG_SEG_SIZE / XLOG_BLCKSZ), (XLOG_SEG_SIZE / XLOG_BLCKSZ), NULL, NULL }, @@ -1851,6 +1914,7 @@ static struct config_int ConfigureNamesInt[] = NULL }, &autovacuum_freeze_max_age, + /* see pg_resetxlog if you change the upper-limit value */ 200000000, 100000000, 2000000000, NULL, NULL }, { @@ -2020,6 +2084,14 @@ static struct config_real ConfigureNamesReal[] = DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS, MAX_GEQO_SELECTION_BIAS, NULL, NULL }, + { + {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO, + gettext_noop("GEQO: seed for random path selection."), + NULL + }, + &Geqo_seed, + 0.0, 0.0, 1.0, NULL, NULL + }, { {"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES, @@ -2177,7 +2249,7 @@ static struct config_string ConfigureNamesString[] = { {"bonjour_name", PGC_POSTMASTER, CONN_AUTH_SETTINGS, - gettext_noop("Sets the Bonjour broadcast service name."), + gettext_noop("Sets the Bonjour service name."), NULL }, &bonjour_name, @@ -2299,7 +2371,7 @@ static struct config_string ConfigureNamesString[] = {"role", PGC_USERSET, UNGROUPED, gettext_noop("Sets the current role."), NULL, - GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST }, &role_string, "none", assign_role, show_role @@ -2310,7 +2382,7 @@ static struct config_string ConfigureNamesString[] = {"session_authorization", PGC_USERSET, UNGROUPED, gettext_noop("Sets the session user name."), NULL, - GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST }, &session_authorization_string, NULL, assign_session_authorization, show_session_authorization @@ -2508,6 +2580,25 @@ static struct config_string ConfigureNamesString[] = }, #endif /* USE_SSL */ + { + {"default_do_language", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the language used in DO statement if LANGUAGE is not specified."), + NULL + }, + &default_do_language, + "plpgsql", NULL, NULL + }, + + { + {"application_name", PGC_USERSET, LOGGING, + gettext_noop("Sets the application name to be reported in statistics and logs."), + NULL, + GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE + }, + &application_name, + "", assign_application_name, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL @@ -2526,6 +2617,15 @@ static struct config_enum ConfigureNamesEnum[] = BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options, NULL, NULL }, + { + {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the output format for bytea."), + NULL + }, + &bytea_output, + BYTEA_OUTPUT_HEX, bytea_output_options, NULL, NULL + }, + { {"client_min_messages", PGC_USERSET, LOGGING_WHEN, gettext_noop("Sets the message levels that are sent to the client."), @@ -2556,7 +2656,7 @@ static struct config_enum ConfigureNamesEnum[] = XACT_READ_COMMITTED, isolation_level_options, NULL, NULL }, - { + { {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets the display format for interval values."), NULL, @@ -2615,15 +2715,6 @@ static struct config_enum ConfigureNamesEnum[] = }, #endif - { - {"regex_flavor", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, - gettext_noop("Sets the regular expression \"flavor\"."), - NULL - }, - ®ex_flavor, - REG_ADVANCED, regex_flavor_options, NULL, NULL - }, - { {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the session's behavior for triggers and rewrite rules."), @@ -2634,6 +2725,16 @@ static struct config_enum ConfigureNamesEnum[] = assign_session_replication_role, NULL }, + { + {"trace_recovery_messages", PGC_SUSET, LOGGING_WHEN, + gettext_noop("Sets the message levels that are logged during recovery."), + gettext_noop("Each level includes all the levels that follow it. The later" + " the level, the fewer messages are sent.") + }, + &trace_recovery_messages, + DEBUG1, server_message_level_options, NULL, NULL + }, + { {"track_functions", PGC_SUSET, STATS_COLLECTOR, gettext_noop("Collects function-level statistics on database activity."), @@ -2649,7 +2750,7 @@ static struct config_enum ConfigureNamesEnum[] = NULL }, &sync_method, - DEFAULT_SYNC_METHOD, sync_method_options, + DEFAULT_SYNC_METHOD, sync_method_options, assign_xlog_sync_method, NULL }, @@ -2716,7 +2817,7 @@ static int GUCNestLevel = 0; /* 1 when in main transaction */ static int guc_var_compare(const void *a, const void *b); static int guc_name_compare(const char *namea, const char *nameb); -static void InitializeOneGUCOption(struct config_generic *gconf); +static void InitializeOneGUCOption(struct config_generic * gconf); static void push_old_value(struct config_generic * gconf, GucAction action); static void ReportGUCOption(struct config_generic * record); static void ShowGUCConfigOption(const char *name, DestReceiver *dest); @@ -2847,7 +2948,7 @@ set_stack_value(struct config_generic * gconf, union config_var_value * val) *((struct config_string *) gconf)->variable); break; case PGC_ENUM: - val->enumval = + val->enumval = *((struct config_enum *) gconf)->variable; break; } @@ -3284,7 +3385,7 @@ InitializeGUCOptions(void) * Initialize one GUC option variable to its compiled-in default. */ static void -InitializeOneGUCOption(struct config_generic *gconf) +InitializeOneGUCOption(struct config_generic * gconf) { gconf->status = 0; gconf->reset_source = PGC_S_DEFAULT; @@ -3296,100 +3397,100 @@ InitializeOneGUCOption(struct config_generic *gconf) switch (gconf->vartype) { case PGC_BOOL: - { - struct config_bool *conf = (struct config_bool *) gconf; - - if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %d", - conf->gen.name, (int) conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; - break; - } - case PGC_INT: - { - struct config_int *conf = (struct config_int *) gconf; - - Assert(conf->boot_val >= conf->min); - Assert(conf->boot_val <= conf->max); - if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %d", - conf->gen.name, conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; - break; - } - case PGC_REAL: - { - struct config_real *conf = (struct config_real *) gconf; - - Assert(conf->boot_val >= conf->min); - Assert(conf->boot_val <= conf->max); - if (conf->assign_hook) - if (!(*conf->assign_hook) (conf->boot_val, true, - PGC_S_DEFAULT)) - elog(FATAL, "failed to initialize %s to %g", - conf->gen.name, conf->boot_val); - *conf->variable = conf->reset_val = conf->boot_val; - break; - } - case PGC_STRING: - { - struct config_string *conf = (struct config_string *) gconf; - char *str; - - *conf->variable = NULL; - conf->reset_val = NULL; - - if (conf->boot_val == NULL) { - /* leave the value NULL, do not call assign hook */ + struct config_bool *conf = (struct config_bool *) gconf; + + if (conf->assign_hook) + if (!(*conf->assign_hook) (conf->boot_val, true, + PGC_S_DEFAULT)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, (int) conf->boot_val); + *conf->variable = conf->reset_val = conf->boot_val; break; } + case PGC_INT: + { + struct config_int *conf = (struct config_int *) gconf; - str = guc_strdup(FATAL, conf->boot_val); - conf->reset_val = str; + Assert(conf->boot_val >= conf->min); + Assert(conf->boot_val <= conf->max); + if (conf->assign_hook) + if (!(*conf->assign_hook) (conf->boot_val, true, + PGC_S_DEFAULT)) + elog(FATAL, "failed to initialize %s to %d", + conf->gen.name, conf->boot_val); + *conf->variable = conf->reset_val = conf->boot_val; + break; + } + case PGC_REAL: + { + struct config_real *conf = (struct config_real *) gconf; - if (conf->assign_hook) + Assert(conf->boot_val >= conf->min); + Assert(conf->boot_val <= conf->max); + if (conf->assign_hook) + if (!(*conf->assign_hook) (conf->boot_val, true, + PGC_S_DEFAULT)) + elog(FATAL, "failed to initialize %s to %g", + conf->gen.name, conf->boot_val); + *conf->variable = conf->reset_val = conf->boot_val; + break; + } + case PGC_STRING: { - const char *newstr; + struct config_string *conf = (struct config_string *) gconf; + char *str; + + *conf->variable = NULL; + conf->reset_val = NULL; - newstr = (*conf->assign_hook) (str, true, - PGC_S_DEFAULT); - if (newstr == NULL) + if (conf->boot_val == NULL) { - elog(FATAL, "failed to initialize %s to \"%s\"", - conf->gen.name, str); + /* leave the value NULL, do not call assign hook */ + break; } - else if (newstr != str) + + str = guc_strdup(FATAL, conf->boot_val); + conf->reset_val = str; + + if (conf->assign_hook) { - free(str); + const char *newstr; - /* - * See notes in set_config_option about casting - */ - str = (char *) newstr; - conf->reset_val = str; + 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); + + /* + * See notes in set_config_option about casting + */ + str = (char *) newstr; + conf->reset_val = str; + } } + *conf->variable = str; + break; } - *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 %s", - conf->gen.name, - config_enum_lookup_by_value(conf, conf->boot_val)); - *conf->variable = conf->reset_val = conf->boot_val; - break; - } + { + 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 %s", + conf->gen.name, + config_enum_lookup_by_value(conf, conf->boot_val)); + *conf->variable = conf->reset_val = conf->boot_val; + break; + } } } @@ -3578,7 +3679,8 @@ ResetAllOptions(void) if (conf->assign_hook) if (!(*conf->assign_hook) (conf->reset_val, true, PGC_S_SESSION)) - elog(ERROR, "failed to reset %s", conf->gen.name); + elog(ERROR, "failed to reset %s to %d", + conf->gen.name, (int) conf->reset_val); *conf->variable = conf->reset_val; break; } @@ -3589,7 +3691,8 @@ ResetAllOptions(void) if (conf->assign_hook) if (!(*conf->assign_hook) (conf->reset_val, true, PGC_S_SESSION)) - elog(ERROR, "failed to reset %s", conf->gen.name); + elog(ERROR, "failed to reset %s to %d", + conf->gen.name, conf->reset_val); *conf->variable = conf->reset_val; break; } @@ -3600,7 +3703,8 @@ ResetAllOptions(void) if (conf->assign_hook) if (!(*conf->assign_hook) (conf->reset_val, true, PGC_S_SESSION)) - elog(ERROR, "failed to reset %s", conf->gen.name); + elog(ERROR, "failed to reset %s to %g", + conf->gen.name, conf->reset_val); *conf->variable = conf->reset_val; break; } @@ -3619,7 +3723,8 @@ ResetAllOptions(void) newstr = (*conf->assign_hook) (str, true, PGC_S_SESSION); if (newstr == NULL) - elog(ERROR, "failed to reset %s", conf->gen.name); + elog(ERROR, "failed to reset %s to \"%s\"", + conf->gen.name, str); else if (newstr != str) { /* @@ -3639,7 +3744,9 @@ ResetAllOptions(void) if (conf->assign_hook) if (!(*conf->assign_hook) (conf->reset_val, true, PGC_S_SESSION)) - elog(ERROR, "failed to reset %s", conf->gen.name); + elog(ERROR, "failed to reset %s to %s", + conf->gen.name, + config_enum_lookup_by_value(conf, conf->reset_val)); *conf->variable = conf->reset_val; break; } @@ -3910,8 +4017,8 @@ AtEOXact_GUC(bool isCommit, int nestLevel) if (conf->assign_hook) if (!(*conf->assign_hook) (newval, true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); + elog(LOG, "failed to commit %s as %d", + conf->gen.name, (int) newval); *conf->variable = newval; changed = true; } @@ -3927,8 +4034,8 @@ AtEOXact_GUC(bool isCommit, int nestLevel) if (conf->assign_hook) if (!(*conf->assign_hook) (newval, true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); + elog(LOG, "failed to commit %s as %d", + conf->gen.name, newval); *conf->variable = newval; changed = true; } @@ -3944,8 +4051,8 @@ AtEOXact_GUC(bool isCommit, int nestLevel) if (conf->assign_hook) if (!(*conf->assign_hook) (newval, true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); + elog(LOG, "failed to commit %s as %g", + conf->gen.name, newval); *conf->variable = newval; changed = true; } @@ -3965,8 +4072,8 @@ AtEOXact_GUC(bool isCommit, int nestLevel) newstr = (*conf->assign_hook) (newval, true, PGC_S_OVERRIDE); if (newstr == NULL) - elog(LOG, "failed to commit %s", - conf->gen.name); + elog(LOG, "failed to commit %s as \"%s\"", + conf->gen.name, newval); else if (newstr != newval) { /* @@ -3997,15 +4104,16 @@ AtEOXact_GUC(bool isCommit, int nestLevel) case PGC_ENUM: { struct config_enum *conf = (struct config_enum *) gconf; - int newval = newvalue.enumval; + 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); + true, PGC_S_OVERRIDE)) + elog(LOG, "failed to commit %s as %s", + conf->gen.name, + config_enum_lookup_by_value(conf, newval)); *conf->variable = newval; changed = true; } @@ -4086,74 +4194,6 @@ ReportGUCOption(struct config_generic * record) } } - -/* - * Try to interpret value as boolean value. Valid values are: true, - * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof. - * If the string parses okay, return true, else false. - * If okay and result is not NULL, return the value in *result. - */ -bool -parse_bool(const char *value, bool *result) -{ - size_t len = strlen(value); - - if (pg_strncasecmp(value, "true", len) == 0) - { - if (result) - *result = true; - } - else if (pg_strncasecmp(value, "false", len) == 0) - { - if (result) - *result = false; - } - - else if (pg_strncasecmp(value, "yes", len) == 0) - { - if (result) - *result = true; - } - else if (pg_strncasecmp(value, "no", len) == 0) - { - if (result) - *result = false; - } - - /* 'o' is not unique enough */ - else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) - { - if (result) - *result = true; - } - else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) - { - if (result) - *result = false; - } - - else if (pg_strcasecmp(value, "1") == 0) - { - if (result) - *result = true; - } - else if (pg_strcasecmp(value, "0") == 0) - { - if (result) - *result = false; - } - - else - { - if (result) - *result = false; /* suppress compiler warning */ - return false; - } - return true; -} - - - /* * Try to parse value as an integer. The accepted formats are the * usual decimal, octal, or hexadecimal formats, optionally followed by @@ -4200,10 +4240,6 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg) /* * Note: the multiple-switch coding technique here is a bit tedious, * but seems necessary to avoid intermediate-value overflows. - * - * If INT64_IS_BUSTED (ie, it's really int32) we will fail to detect - * overflow due to units conversion, but there are few enough such - * machines that it does not seem worth trying to be smarter. */ if (flags & GUC_UNIT_MEMORY) { @@ -4406,7 +4442,7 @@ parse_real(const char *value, double *result) * allocated for modification. */ const char * -config_enum_lookup_by_value(struct config_enum *record, int val) +config_enum_lookup_by_value(struct config_enum * record, int val) { const struct config_enum_entry *entry; @@ -4418,7 +4454,7 @@ config_enum_lookup_by_value(struct config_enum *record, int val) elog(ERROR, "could not find enum option %d for %s", val, record->gen.name); - return NULL; /* silence compiler */ + return NULL; /* silence compiler */ } @@ -4429,7 +4465,7 @@ config_enum_lookup_by_value(struct config_enum *record, int val) * true. If it's not found, return FALSE and retval is set to 0. */ bool -config_enum_lookup_by_name(struct config_enum *record, const char *value, +config_enum_lookup_by_name(struct config_enum * record, const char *value, int *retval) { const struct config_enum_entry *entry; @@ -4455,16 +4491,16 @@ config_enum_lookup_by_name(struct config_enum *record, const char *value, * If suffix is non-NULL, it is added to the end of the string. */ static char * -config_enum_get_options(struct config_enum *record, const char *prefix, +config_enum_get_options(struct config_enum * record, const char *prefix, const char *suffix, const char *separator) { const struct config_enum_entry *entry; - StringInfoData retstr; + StringInfoData retstr; int seplen; initStringInfo(&retstr); appendStringInfoString(&retstr, prefix); - + seplen = strlen(separator); for (entry = record->options; entry && entry->name; entry++) { @@ -4476,11 +4512,11 @@ config_enum_get_options(struct config_enum *record, const char *prefix, } /* - * All the entries may have been hidden, leaving the string empty - * if no prefix was given. This indicates a broken GUC setup, since - * there is no use for an enum without any values, so we just check - * to make sure we don't write to invalid memory instead of actually - * trying to do something smart with it. + * All the entries may have been hidden, leaving the string empty if no + * prefix was given. This indicates a broken GUC setup, since there is no + * use for an enum without any values, so we just check to make sure we + * don't write to invalid memory instead of actually trying to do + * something smart with it. */ if (retstr.len >= seplen) { @@ -4566,7 +4602,8 @@ set_config_option(const char *name, const char *value, */ elevel = IsUnderPostmaster ? DEBUG3 : LOG; } - else if (source == PGC_S_DATABASE || source == PGC_S_USER) + else if (source == PGC_S_DATABASE || source == PGC_S_USER || + source == PGC_S_DATABASE_USER) elevel = WARNING; else elevel = ERROR; @@ -4620,18 +4657,16 @@ set_config_option(const char *name, const char *value, if (changeVal && !is_newvalue_equal(record, value)) ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), - errmsg("attempted change of parameter \"%s\" ignored", - name), - errdetail("This parameter cannot be changed after server start."))); + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); return true; } if (context != PGC_POSTMASTER) { ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), - errmsg("attempted change of parameter \"%s\" ignored", - name), - errdetail("This parameter cannot be changed after server start."))); + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); return false; } break; @@ -4666,7 +4701,8 @@ set_config_option(const char *name, const char *value, if (IsUnderPostmaster) return true; } - else if (context != PGC_BACKEND && context != PGC_POSTMASTER) + else if (context != PGC_POSTMASTER && context != PGC_BACKEND && + source != PGC_S_CLIENT) { ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), @@ -4690,6 +4726,48 @@ set_config_option(const char *name, const char *value, break; } + /* + * Disallow changing GUC_NOT_WHILE_SEC_REST values if we are inside a + * security restriction context. We can reject this regardless of + * the GUC context or source, mainly because sources that it might be + * reasonable to override for won't be seen while inside a function. + * + * Note: variables marked GUC_NOT_WHILE_SEC_REST should usually be marked + * GUC_NO_RESET_ALL as well, because ResetAllOptions() doesn't check this. + * An exception might be made if the reset value is assumed to be "safe". + * + * Note: this flag is currently used for "session_authorization" and + * "role". We need to prohibit changing these inside a local userid + * context because when we exit it, GUC won't be notified, leaving things + * out of sync. (This could be fixed by forcing a new GUC nesting level, + * but that would change behavior in possibly-undesirable ways.) Also, + * we prohibit changing these in a security-restricted operation because + * otherwise RESET could be used to regain the session user's privileges. + */ + if (record->flags & GUC_NOT_WHILE_SEC_REST) + { + if (InLocalUserIdChange()) + { + /* + * Phrasing of this error message is historical, but it's the + * most common case. + */ + ereport(elevel, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot set parameter \"%s\" within security-definer function", + name))); + return false; + } + if (InSecurityRestrictedOperation()) + { + ereport(elevel, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("cannot set parameter \"%s\" within security-restricted operation", + name))); + return false; + } + } + /* * Should we set reset/stacked values? (If so, the behavior is not * transactional.) This is done either when we get a default value from @@ -5059,16 +5137,16 @@ set_config_option(const char *name, const char *value, { if (!config_enum_lookup_by_name(conf, value, &newval)) { - char *hintmsg; - - hintmsg = config_enum_get_options(conf, - "Available values: ", + char *hintmsg; + + hintmsg = config_enum_get_options(conf, + "Available values: ", ".", ", "); ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - name, value), + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), hintmsg ? errhint("%s", _(hintmsg)) : 0)); if (hintmsg) @@ -5093,9 +5171,9 @@ set_config_option(const char *name, const char *value, { ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - name, - config_enum_lookup_by_value(conf, newval)))); + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, + config_enum_lookup_by_value(conf, newval)))); return false; } @@ -5143,8 +5221,8 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline) int elevel; /* - * To avoid cluttering the log, only the postmaster bleats loudly - * about problems with the config file. + * To avoid cluttering the log, only the postmaster bleats loudly about + * problems with the config file. */ elevel = IsUnderPostmaster ? DEBUG3 : LOG; @@ -5182,11 +5260,15 @@ SetConfigOption(const char *name, const char *value, * Fetch the current value of the option `name'. If the option doesn't exist, * throw an ereport and don't return. * + * If restrict_superuser is true, we also enforce that only superusers can + * see GUC_SUPERUSER_ONLY variables. This should only be passed as true + * in user-driven calls. + * * The string is *not* allocated for modification and is really only * valid until the next call to configuration related functions. */ const char * -GetConfigOption(const char *name) +GetConfigOption(const char *name, bool restrict_superuser) { struct config_generic *record; static char buffer[256]; @@ -5196,7 +5278,9 @@ GetConfigOption(const char *name) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized configuration parameter \"%s\"", name))); - if ((record->flags & GUC_SUPERUSER_ONLY) && !superuser()) + if (restrict_superuser && + (record->flags & GUC_SUPERUSER_ONLY) && + !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to examine \"%s\"", name))); @@ -5221,7 +5305,7 @@ GetConfigOption(const char *name) case PGC_ENUM: return config_enum_lookup_by_value((struct config_enum *) record, - *((struct config_enum *) record)->variable); + *((struct config_enum *) record)->variable); } return NULL; } @@ -5269,27 +5353,11 @@ GetConfigOptionResetString(const char *name) case PGC_ENUM: return config_enum_lookup_by_value((struct config_enum *) record, - ((struct config_enum *) record)->reset_val); + ((struct config_enum *) record)->reset_val); } return NULL; } -/* - * Detect whether the given configuration option can only be set by - * a superuser. - */ -bool -IsSuperuserConfigOption(const char *name) -{ - struct config_generic *record; - - record = find_option(name, false, ERROR); - /* On an unrecognized name, don't error, just return false. */ - if (record == NULL) - return false; - return (record->context == PGC_SUSET); -} - /* * GUC_complaint_elevel @@ -5299,7 +5367,7 @@ IsSuperuserConfigOption(const char *name) * report (in addition to the generic "invalid value for option FOO" that * guc.c will provide). Note that the result might be ERROR or a lower * level, so the caller must be prepared for control to return from ereport, - * or not. If control does return, return false/NULL from the hook function. + * or not. If control does return, return false/NULL from the hook function. * * At some point it'd be nice to replace this with a mechanism that allows * the custom message to become the DETAIL line of guc.c's generic message. @@ -5321,9 +5389,9 @@ GUC_complaint_elevel(GucSource source) { /* * If we're a postmaster child, this is probably "undo" during - * transaction abort, so we don't want to clutter the log. There's - * a small chance of a real problem with an OVERRIDE setting, - * though, so suppressing the message entirely wouldn't be desirable. + * transaction abort, so we don't want to clutter the log. There's a + * small chance of a real problem with an OVERRIDE setting, though, so + * suppressing the message entirely wouldn't be desirable. */ elevel = IsUnderPostmaster ? DEBUG5 : LOG; } @@ -5379,25 +5447,25 @@ flatten_set_variable_args(const char *name, List *args) /* * Each list member may be a plain A_Const node, or an A_Const within a - * TypeCast; the latter case is supported only for ConstInterval - * arguments (for SET TIME ZONE). + * TypeCast; the latter case is supported only for ConstInterval arguments + * (for SET TIME ZONE). */ foreach(l, args) { - Node *arg = (Node *) lfirst(l); + Node *arg = (Node *) lfirst(l); char *val; - TypeName *typename = NULL; - A_Const *con; + TypeName *typeName = NULL; + A_Const *con; if (l != list_head(args)) appendStringInfo(&buf, ", "); if (IsA(arg, TypeCast)) { - TypeCast *tc = (TypeCast *) arg; + TypeCast *tc = (TypeCast *) arg; arg = tc->arg; - typename = tc->typename; + typeName = tc->typeName; } if (!IsA(arg, A_Const)) @@ -5415,7 +5483,7 @@ flatten_set_variable_args(const char *name, List *args) break; case T_String: val = strVal(&con->val); - if (typename != NULL) + if (typeName != NULL) { /* * Must be a ConstInterval argument for TIME ZONE. Coerce @@ -5427,7 +5495,7 @@ flatten_set_variable_args(const char *name, List *args) Datum interval; char *intervalout; - typoid = typenameTypeId(NULL, typename, &typmod); + typoid = typenameTypeId(NULL, typeName, &typmod); Assert(typoid == INTERVALOID); interval = @@ -5652,11 +5720,11 @@ init_custom_variable(const char *name, struct config_generic *gen; /* - * Only allow custom PGC_POSTMASTER variables to be created during - * shared library preload; any later than that, we can't ensure that - * the value doesn't change after startup. This is a fatal elog if it - * happens; just erroring out isn't safe because we don't know what - * the calling loadable module might already have hooked into. + * Only allow custom PGC_POSTMASTER variables to be created during shared + * library preload; any later than that, we can't ensure that the value + * doesn't change after startup. This is a fatal elog if it happens; just + * erroring out isn't safe because we don't know what the calling loadable + * module might already have hooked into. */ if (context == PGC_POSTMASTER && !process_shared_preload_libraries_in_progress) @@ -5687,7 +5755,7 @@ define_custom_variable(struct config_generic * variable) const char **nameAddr = &name; const char *value; struct config_string *pHolder; - GucContext phcontext; + GucContext phcontext; struct config_generic **res; /* @@ -5734,9 +5802,9 @@ define_custom_variable(struct config_generic * variable) *res = variable; /* - * Infer context for assignment based on source of existing value. - * We can't tell this with exact accuracy, but we can at least do - * something reasonable in typical cases. + * Infer context for assignment based on source of existing value. We + * can't tell this with exact accuracy, but we can at least do something + * reasonable in typical cases. */ switch (pHolder->gen.source) { @@ -5744,10 +5812,11 @@ define_custom_variable(struct config_generic * variable) case PGC_S_ENV_VAR: case PGC_S_FILE: case PGC_S_ARGV: + /* - * If we got past the check in init_custom_variable, we can - * safely assume that any existing value for a PGC_POSTMASTER - * variable was set in postmaster context. + * If we got past the check in init_custom_variable, we can safely + * assume that any existing value for a PGC_POSTMASTER variable + * was set in postmaster context. */ if (variable->context == PGC_POSTMASTER) phcontext = PGC_POSTMASTER; @@ -5756,6 +5825,7 @@ define_custom_variable(struct config_generic * variable) break; case PGC_S_DATABASE: case PGC_S_USER: + case PGC_S_DATABASE_USER: case PGC_S_CLIENT: case PGC_S_SESSION: default: @@ -5907,7 +5977,7 @@ DefineCustomEnumVariable(const char *name, const char *long_desc, int *valueAddr, int bootValue, - const struct config_enum_entry *options, + const struct config_enum_entry * options, GucContext context, int flags, GucEnumAssignHook assign_hook, @@ -6032,7 +6102,8 @@ ShowAllGUCConfig(DestReceiver *dest) int i; TupOutputState *tstate; TupleDesc tupdesc; - char *values[3]; + Datum values[3]; + bool isnull[3] = { false, false, false }; /* need a tuple descriptor representing three TEXT columns */ tupdesc = CreateTemplateTupleDesc(3, false); @@ -6043,29 +6114,46 @@ ShowAllGUCConfig(DestReceiver *dest) TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0); - /* prepare for projection of tuples */ tstate = begin_tup_output_tupdesc(dest, tupdesc); for (i = 0; i < num_guc_variables; i++) { struct config_generic *conf = guc_variables[i]; + char *setting; if ((conf->flags & GUC_NO_SHOW_ALL) || ((conf->flags & GUC_SUPERUSER_ONLY) && !am_superuser)) continue; /* assign to the values array */ - values[0] = (char *) conf->name; - values[1] = _ShowOption(conf, true); - values[2] = (char *) conf->short_desc; + values[0] = PointerGetDatum(cstring_to_text(conf->name)); + + setting = _ShowOption(conf, true); + if (setting) + { + values[1] = PointerGetDatum(cstring_to_text(setting)); + isnull[1] = false; + } + else + { + values[1] = PointerGetDatum(NULL); + isnull[1] = true; + } + + values[2] = PointerGetDatum(cstring_to_text(conf->short_desc)); /* send it to dest */ - do_tup_output(tstate, values); + do_tup_output(tstate, values, isnull); /* clean up */ - if (values[1] != NULL) - pfree(values[1]); + pfree(DatumGetPointer(values[0])); + if (setting) + { + pfree(setting); + pfree(DatumGetPointer(values[1])); + } + pfree(DatumGetPointer(values[2])); } end_tup_output(tstate); @@ -6220,13 +6308,13 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; - /* boot_val */ - snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val); - values[12] = pstrdup(buffer); + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val); + values[12] = pstrdup(buffer); - /* reset_val */ - snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val); - values[13] = pstrdup(buffer); + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val); + values[13] = pstrdup(buffer); } break; @@ -6245,19 +6333,19 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; - /* boot_val */ - snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val); - values[12] = pstrdup(buffer); + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val); + values[12] = pstrdup(buffer); - /* reset_val */ - snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val); - values[13] = pstrdup(buffer); + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val); + values[13] = pstrdup(buffer); } break; case PGC_STRING: { - struct config_string *lconf = (struct config_string *) conf; + struct config_string *lconf = (struct config_string *) conf; /* min_val */ values[9] = NULL; @@ -6268,15 +6356,15 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; - /* boot_val */ - if (lconf->boot_val == NULL) - values[12] = NULL; + /* boot_val */ + if (lconf->boot_val == NULL) + values[12] = NULL; else values[12] = pstrdup(lconf->boot_val); - /* reset_val */ - if (lconf->reset_val == NULL) - values[13] = NULL; + /* reset_val */ + if (lconf->reset_val == NULL) + values[13] = NULL; else values[13] = pstrdup(lconf->reset_val); } @@ -6284,7 +6372,7 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) case PGC_ENUM: { - struct config_enum *lconf = (struct config_enum *) conf; + struct config_enum *lconf = (struct config_enum *) conf; /* min_val */ values[9] = NULL; @@ -6293,17 +6381,21 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) values[10] = NULL; /* enumvals */ - /* NOTE! enumvals with double quotes in them are not supported! */ + + /* + * NOTE! enumvals with double quotes in them are not + * supported! + */ values[11] = config_enum_get_options((struct config_enum *) conf, "{\"", "\"}", "\",\""); - /* boot_val */ + /* boot_val */ values[12] = pstrdup(config_enum_lookup_by_value(lconf, - lconf->boot_val)); + lconf->boot_val)); - /* reset_val */ + /* reset_val */ values[13] = pstrdup(config_enum_lookup_by_value(lconf, - lconf->reset_val)); + lconf->reset_val)); } break; @@ -6322,18 +6414,18 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; - /* boot_val */ - values[12] = NULL; + /* boot_val */ + values[12] = NULL; - /* reset_val */ - values[13] = NULL; + /* reset_val */ + values[13] = NULL; } break; } - /* - * If the setting came from a config file, set the source location. - * For security reasons, we don't show source file/line number for + /* + * If the setting came from a config file, set the source location. For + * security reasons, we don't show source file/line number for * non-superusers. */ if (conf->source == PGC_S_FILE && superuser()) @@ -6434,10 +6526,10 @@ show_all_settings(PG_FUNCTION_ARGS) TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "enumvals", TEXTARRAYOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val", - TEXTOID, -1, 0); - TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val", - TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 13, "boot_val", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reset_val", + TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 15, "sourcefile", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline", @@ -6531,10 +6623,7 @@ _ShowOption(struct config_generic * record, bool use_units) { /* * Use int64 arithmetic to avoid overflows in units - * conversion. If INT64_IS_BUSTED we might overflow - * anyway and print bogus answers, but there are few - * enough such machines that it doesn't seem worth - * trying harder. + * conversion. */ int64 result = *conf->variable; const char *unit; @@ -6647,7 +6736,7 @@ _ShowOption(struct config_generic * record, bool use_units) { struct config_enum *conf = (struct config_enum *) record; - if(conf->show_hook) + if (conf->show_hook) val = (*conf->show_hook) (); else val = config_enum_lookup_by_value(conf, *conf->variable); @@ -6736,7 +6825,7 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue) * variable source, integer */ static void -write_one_nondefault_variable(FILE *fp, struct config_generic *gconf) +write_one_nondefault_variable(FILE *fp, struct config_generic * gconf) { if (gconf->source == PGC_S_DEFAULT) return; @@ -6747,49 +6836,49 @@ write_one_nondefault_variable(FILE *fp, struct config_generic *gconf) switch (gconf->vartype) { case PGC_BOOL: - { - struct config_bool *conf = (struct config_bool *) gconf; + { + struct config_bool *conf = (struct config_bool *) gconf; - if (*conf->variable) - fprintf(fp, "true"); - else - fprintf(fp, "false"); - } - break; + if (*conf->variable) + fprintf(fp, "true"); + else + fprintf(fp, "false"); + } + break; case PGC_INT: - { - struct config_int *conf = (struct config_int *) gconf; + { + struct config_int *conf = (struct config_int *) gconf; - fprintf(fp, "%d", *conf->variable); - } - break; + fprintf(fp, "%d", *conf->variable); + } + break; case PGC_REAL: - { - struct config_real *conf = (struct config_real *) gconf; + { + struct config_real *conf = (struct config_real *) gconf; - /* Could lose precision here? */ - fprintf(fp, "%f", *conf->variable); - } - break; + /* Could lose precision here? */ + fprintf(fp, "%f", *conf->variable); + } + break; case PGC_STRING: - { - struct config_string *conf = (struct config_string *) gconf; + { + struct config_string *conf = (struct config_string *) gconf; - fprintf(fp, "%s", *conf->variable); - } - break; + fprintf(fp, "%s", *conf->variable); + } + break; case PGC_ENUM: - { - struct config_enum *conf = (struct config_enum *) gconf; - - fprintf(fp, "%s", - config_enum_lookup_by_value(conf, *conf->variable)); - } - break; + { + struct config_enum *conf = (struct config_enum *) gconf; + + fprintf(fp, "%s", + config_enum_lookup_by_value(conf, *conf->variable)); + } + break; } fputc(0, fp); @@ -7212,7 +7301,7 @@ assign_log_destination(const char *value, bool doit, GucSource source) list_free(elemlist); ereport(GUC_complaint_elevel(source), (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid list syntax for parameter \"log_destination\""))); + errmsg("invalid list syntax for parameter \"log_destination\""))); return NULL; } @@ -7390,6 +7479,21 @@ assign_debug_assertions(bool newval, bool doit, GucSource source) return true; } +static bool +assign_bonjour(bool newval, bool doit, GucSource source) +{ +#ifndef USE_BONJOUR + if (newval) + { + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Bonjour is not supported by this build"))); + return false; + } +#endif + return true; +} + static bool assign_ssl(bool newval, bool doit, GucSource source) { @@ -7451,6 +7555,18 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source) if (source != PGC_S_OVERRIDE) return false; } + + /* Can't go to r/w mode while recovery is still active */ + if (newval == false && XactReadOnly && RecoveryInProgress()) + { + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot set transaction read-write mode during recovery"))); + /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ + if (source != PGC_S_OVERRIDE) + return false; + } + return true; } @@ -7592,11 +7708,11 @@ show_tcp_keepalives_count(void) static bool assign_maxconnections(int newval, bool doit, GucSource source) { - if (newval + autovacuum_max_workers > INT_MAX / 4) + if (newval + autovacuum_max_workers + 1 > INT_MAX / 4) return false; if (doit) - MaxBackends = newval + autovacuum_max_workers; + MaxBackends = newval + autovacuum_max_workers + 1; return true; } @@ -7604,11 +7720,11 @@ assign_maxconnections(int newval, bool doit, GucSource source) static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source) { - if (newval + MaxConnections > INT_MAX / 4) + if (MaxConnections + newval + 1 > INT_MAX / 4) return false; if (doit) - MaxBackends = newval + MaxConnections; + MaxBackends = MaxConnections + newval + 1; return true; } @@ -7628,11 +7744,11 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source) * * drives | I/O requests * -------+---------------- - * 1 | 1 - * 2 | 2/1 + 2/2 = 3 - * 3 | 3/1 + 3/2 + 3/3 = 5 1/2 - * 4 | 4/1 + 4/2 + 4/3 + 4/4 = 8 1/3 - * n | n * H(n) + * 1 | 1 + * 2 | 2/1 + 2/2 = 3 + * 3 | 3/1 + 3/2 + 3/3 = 5 1/2 + * 4 | 4/1 + 4/2 + 4/3 + 4/4 = 8 1/3 + * n | n * H(n) * * This is called the "coupon collector problem" and H(n) is called the * harmonic series. This could be approximated by n * ln(n), but for @@ -7665,7 +7781,7 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source) return false; #else return true; -#endif /* USE_PREFETCH */ +#endif /* USE_PREFETCH */ } static const char * @@ -7673,19 +7789,52 @@ assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source) { if (doit) { + char *canon_val = guc_strdup(ERROR, newval); + char *tname; + char *fname; + + canonicalize_path(canon_val); + + tname = guc_malloc(ERROR, strlen(canon_val) + 12); /* /pgstat.tmp */ + sprintf(tname, "%s/pgstat.tmp", canon_val); + fname = guc_malloc(ERROR, strlen(canon_val) + 13); /* /pgstat.stat */ + sprintf(fname, "%s/pgstat.stat", canon_val); + if (pgstat_stat_tmpname) free(pgstat_stat_tmpname); + pgstat_stat_tmpname = tname; if (pgstat_stat_filename) free(pgstat_stat_filename); + pgstat_stat_filename = fname; - pgstat_stat_tmpname = guc_malloc(FATAL, strlen(newval) + 12); /* /pgstat.tmp */ - pgstat_stat_filename = guc_malloc(FATAL, strlen(newval) + 13); /* /pgstat.stat */ - - sprintf(pgstat_stat_tmpname, "%s/pgstat.tmp", newval); - sprintf(pgstat_stat_filename, "%s/pgstat.stat", newval); + return canon_val; } + else + return newval; +} - return newval; +static const char * +assign_application_name(const char *newval, bool doit, GucSource source) +{ + if (doit) + { + /* Only allow clean ASCII chars in the application name */ + char *repval = guc_strdup(ERROR, newval); + char *p; + + for (p = repval; *p; p++) + { + if (*p < 32 || *p > 126) + *p = '?'; + } + + /* Update the pg_stat_activity view */ + pgstat_report_appname(repval); + + return repval; + } + else + return newval; } #include "guc-file.c"