X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fmisc%2Fguc.c;h=38d577f87bb9a892a8908bc8f7d3dcc2eafe02bb;hb=901be0fad4034c9cf8a3588fd6cf2ece82e4b8ce;hp=8b072895b1e72f2d75b260984f7fa723a605541b;hpb=7a97abe818798b710d468e2994e211227c95e0d1;p=postgresql diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 8b072895b1..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-2008, 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.464 2008/07/10 22:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.532 2010/01/07 04:53:35 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -37,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" @@ -54,13 +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 "storage/freespace.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; @@ -129,6 +133,8 @@ extern bool optimize_bounded_sort; extern char *SSLCipherSuites; #endif +static void set_config_sourcefile(const char *name, char *sourcefile, + int sourceline); static const char *assign_log_destination(const char *value, bool doit, GucSource source); @@ -137,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 @@ -149,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); @@ -162,17 +169,29 @@ static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source) static const char *show_tcp_keepalives_idle(void); static const char *show_tcp_keepalives_interval(void); static const char *show_tcp_keepalives_count(void); -static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source); 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); +static char *config_enum_get_options(struct config_enum * record, + const char *prefix, const char *suffix, + const char *separator); /* * Options for enum values defined in this module. + * + * 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) @@ -211,6 +230,14 @@ static const struct config_enum_entry server_message_level_options[] = { {NULL, 0, false} }; +static const struct config_enum_entry intervalstyle_options[] = { + {"postgres", INTSTYLE_POSTGRES, false}, + {"postgres_verbose", INTSTYLE_POSTGRES_VERBOSE, false}, + {"sql_standard", INTSTYLE_SQL_STANDARD, false}, + {"iso_8601", INTSTYLE_ISO_8601, false}, + {NULL, 0, false} +}; + static const struct config_enum_entry log_error_verbosity_options[] = { {"terse", PGERROR_TERSE, false}, {"default", PGERROR_DEFAULT, false}, @@ -226,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}, @@ -298,6 +318,23 @@ static const struct config_enum_entry backslash_quote_options[] = { {NULL, 0, false} }; +/* + * Although only "on", "off", and "partition" are documented, we + * accept all the likely variants of "on" and "off". + */ +static const struct config_enum_entry constraint_exclusion_options[] = { + {"partition", CONSTRAINT_EXCLUSION_PARTITION, false}, + {"on", CONSTRAINT_EXCLUSION_ON, false}, + {"off", CONSTRAINT_EXCLUSION_OFF, false}, + {"true", CONSTRAINT_EXCLUSION_ON, true}, + {"false", CONSTRAINT_EXCLUSION_OFF, true}, + {"yes", CONSTRAINT_EXCLUSION_ON, true}, + {"no", CONSTRAINT_EXCLUSION_OFF, true}, + {"1", CONSTRAINT_EXCLUSION_ON, true}, + {"0", CONSTRAINT_EXCLUSION_OFF, true}, + {NULL, 0, false} +}; + /* * Options for enum values stored in other modules */ @@ -315,7 +352,7 @@ bool log_duration = false; bool Debug_print_plan = false; bool Debug_print_parse = false; bool Debug_print_rewritten = false; -bool Debug_pretty_print = false; +bool Debug_pretty_print = true; bool log_parser_stats = false; bool log_planner_stats = false; @@ -343,6 +380,12 @@ char *HbaFileName; char *IdentFileName; 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; @@ -381,6 +424,7 @@ static int segment_size; static int wal_block_size; static int wal_segment_size; static bool integer_datetimes; +static int effective_io_concurrency; /* should be static, but commands/variable.c needs to get at these */ char *role_string; @@ -415,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", @@ -441,8 +486,6 @@ const char *const config_group_names[] = gettext_noop("Resource Usage"), /* RESOURCES_MEM */ gettext_noop("Resource Usage / Memory"), - /* RESOURCES_FSM */ - gettext_noop("Resource Usage / Free Space Map"), /* RESOURCES_KERNEL */ gettext_noop("Resource Usage / Kernel Resources"), /* WAL */ @@ -622,15 +665,6 @@ static struct config_bool ConfigureNamesBool[] = &enable_hashjoin, true, NULL, NULL }, - { - {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER, - gettext_noop("Enables the planner to use constraints to optimize queries."), - gettext_noop("Child table scans will be skipped if their " - "constraints guarantee that no rows match the query.") - }, - &constraint_exclusion, - false, NULL, NULL - }, { {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Enables genetic query optimization."), @@ -650,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."), @@ -703,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.") @@ -769,7 +811,7 @@ static struct config_bool ConfigureNamesBool[] = }, { {"debug_print_parse", PGC_USERSET, LOGGING_WHAT, - gettext_noop("Prints the parse tree to the server log."), + gettext_noop("Logs each query's parse tree."), NULL }, &Debug_print_parse, @@ -777,7 +819,7 @@ static struct config_bool ConfigureNamesBool[] = }, { {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT, - gettext_noop("Prints the parse tree after rewriting to server log."), + gettext_noop("Logs each query's rewritten parse tree."), NULL }, &Debug_print_rewritten, @@ -785,7 +827,7 @@ static struct config_bool ConfigureNamesBool[] = }, { {"debug_print_plan", PGC_USERSET, LOGGING_WHAT, - gettext_noop("Prints the execution plan to server log."), + gettext_noop("Logs each query's execution plan."), NULL }, &Debug_print_plan, @@ -797,7 +839,7 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &Debug_pretty_print, - false, NULL, NULL + true, NULL, NULL }, { {"log_parser_stats", PGC_SUSET, STATS_MONITORING, @@ -1014,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."), @@ -1131,7 +1165,7 @@ static struct config_bool ConfigureNamesBool[] = }, { - {"krb_caseins_users", PGC_POSTMASTER, CONN_AUTH_SECURITY, + {"krb_caseins_users", PGC_SIGHUP, CONN_AUTH_SECURITY, gettext_noop("Sets whether Kerberos and GSSAPI user names should be treated as case-insensitive."), NULL }, @@ -1176,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."), @@ -1197,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 @@ -1232,7 +1287,7 @@ static struct config_int ConfigureNamesInt[] = "column-specific target set via ALTER TABLE SET STATISTICS.") }, &default_statistics_target, - 10, 1, 1000, NULL, NULL + 100, 1, 10000, NULL, NULL }, { {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER, @@ -1304,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, @@ -1317,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."), @@ -1443,7 +1509,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &VacuumCostDelay, - 0, 0, 1000, NULL, NULL + 0, 0, 100, NULL, NULL }, { @@ -1453,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 }, { @@ -1474,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 @@ -1520,26 +1589,30 @@ static struct config_int ConfigureNamesInt[] = NULL }, &vacuum_freeze_min_age, - 100000000, 0, 1000000000, NULL, NULL + 50000000, 0, 1000000000, NULL, NULL }, { - {"max_fsm_relations", PGC_POSTMASTER, RESOURCES_FSM, - gettext_noop("Sets the maximum number of tables and indexes for which free space is tracked."), + {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."), NULL }, - &MaxFSMRelations, - 1000, 100, INT_MAX, NULL, NULL + &vacuum_freeze_table_age, + 150000000, 0, 2000000000, NULL, NULL }, + { - {"max_fsm_pages", PGC_POSTMASTER, RESOURCES_FSM, - gettext_noop("Sets the maximum number of disk pages for which free space is tracked."), + {"vacuum_defer_cleanup_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Age by which VACUUM and HOT cleanup should be deferred, if any."), NULL }, - &MaxFSMPages, - 20000, 1000, INT_MAX, NULL, 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."), @@ -1652,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 }, { @@ -1696,6 +1769,26 @@ static struct config_int ConfigureNamesInt[] = 100, 0, 1000, NULL, NULL }, + { + {"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.") + }, + &effective_io_concurrency, +#ifdef USE_PREFETCH + 1, 0, 1000, +#else + 0, 0, 0, +#endif + assign_effective_io_concurrency, NULL + }, + { {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE, gettext_noop("Automatic log file rotation will occur after N minutes."), @@ -1758,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 @@ -1783,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 }, @@ -1821,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 }, { @@ -1898,7 +1992,7 @@ static struct config_int ConfigureNamesInt[] = }, { - {"log_temp_files", PGC_USERSET, LOGGING_WHAT, + {"log_temp_files", PGC_SUSET, LOGGING_WHAT, gettext_noop("Log the use of temporary files larger than this number of kilobytes."), gettext_noop("Zero logs all files. The default is -1 (turning this feature off)."), GUC_UNIT_KB @@ -1990,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, @@ -2127,17 +2229,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"krb_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY, - gettext_noop("Sets realm to match Kerberos and GSSAPI users against."), - NULL, - GUC_SUPERUSER_ONLY - }, - &pg_krb_realm, - NULL, NULL, NULL - }, - - { - {"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY, + {"krb_server_keyfile", PGC_SIGHUP, CONN_AUTH_SECURITY, gettext_noop("Sets the location of the Kerberos server key file."), NULL, GUC_SUPERUSER_ONLY @@ -2147,7 +2239,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"krb_srvname", PGC_POSTMASTER, CONN_AUTH_SECURITY, + {"krb_srvname", PGC_SIGHUP, CONN_AUTH_SECURITY, gettext_noop("Sets the name of the Kerberos service."), NULL }, @@ -2155,18 +2247,9 @@ static struct config_string ConfigureNamesString[] = PG_KRB_SRVNAM, NULL, NULL }, - { - {"krb_server_hostname", PGC_POSTMASTER, CONN_AUTH_SECURITY, - gettext_noop("Sets the hostname of the Kerberos server."), - NULL - }, - &pg_krb_server_hostname, - NULL, NULL, NULL - }, - { {"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, @@ -2288,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 @@ -2299,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 @@ -2466,6 +2549,16 @@ static struct config_string ConfigureNamesString[] = NULL, assign_canonical_path, NULL }, + { + {"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR, + gettext_noop("Writes temporary statistics files to the specified directory."), + NULL, + GUC_SUPERUSER_ONLY + }, + &pgstat_temp_directory, + "pg_stat_tmp", assign_pgstat_temp_directory, NULL + }, + { {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets default text search configuration."), @@ -2487,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 @@ -2505,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."), @@ -2515,6 +2636,17 @@ static struct config_enum ConfigureNamesEnum[] = NOTICE, client_message_level_options, NULL, NULL }, + { + {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER, + gettext_noop("Enables the planner to use constraints to optimize queries."), + gettext_noop("Table scans will be skipped if their constraints" + " guarantee that no rows match the query.") + }, + &constraint_exclusion, + CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options, + NULL, NULL + }, + { {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the transaction isolation level of each new transaction."), @@ -2524,6 +2656,16 @@ 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, + GUC_REPORT + }, + &IntervalStyle, + INTSTYLE_POSTGRES, intervalstyle_options, NULL, NULL + }, + { {"log_error_verbosity", PGC_SUSET, LOGGING_WHEN, gettext_noop("Sets the verbosity of logged messages."), @@ -2573,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."), @@ -2592,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."), @@ -2607,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 }, @@ -2674,6 +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 push_old_value(struct config_generic * gconf, GucAction action); static void ReportGUCOption(struct config_generic * record); static void ShowGUCConfigOption(const char *name, DestReceiver *dest); @@ -2804,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; } @@ -3181,111 +3325,7 @@ InitializeGUCOptions(void) */ for (i = 0; i < num_guc_variables; i++) { - struct config_generic *gconf = guc_variables[i]; - - gconf->status = 0; - gconf->reset_source = PGC_S_DEFAULT; - gconf->source = PGC_S_DEFAULT; - gconf->stack = NULL; - - 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 */ - break; - } - - str = guc_strdup(FATAL, conf->boot_val); - conf->reset_val = str; - - if (conf->assign_hook) - { - const char *newstr; - - 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; - } - 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; - } - } + InitializeOneGUCOption(guc_variables[i]); } guc_dirty = false; @@ -3341,63 +3381,176 @@ InitializeGUCOptions(void) } } - /* - * Select the configuration files and data directory to be used, and - * do the initial read of postgresql.conf. - * - * This is called after processing command-line switches. - * userDoption is the -D switch value if any (NULL if unspecified). - * progname is just for use in error messages. - * - * Returns true on success; on failure, prints a suitable error message - * to stderr and returns false. + * Initialize one GUC option variable to its compiled-in default. */ -bool -SelectConfigFiles(const char *userDoption, const char *progname) +static void +InitializeOneGUCOption(struct config_generic * gconf) { - char *configdir; - char *fname; - struct stat stat_buf; - - /* configdir is -D option, or $PGDATA if no -D */ - if (userDoption) - configdir = make_absolute_path(userDoption); - else - configdir = make_absolute_path(getenv("PGDATA")); + gconf->status = 0; + gconf->reset_source = PGC_S_DEFAULT; + gconf->source = PGC_S_DEFAULT; + gconf->stack = NULL; + gconf->sourcefile = NULL; + gconf->sourceline = 0; - /* - * Find the configuration file: if config_file was specified on the - * command line, use it, else use configdir/postgresql.conf. In any case - * ensure the result is an absolute path, so that it will be interpreted - * the same way by future backends. - */ - if (ConfigFileName) - fname = make_absolute_path(ConfigFileName); - else if (configdir) - { - fname = guc_malloc(FATAL, - strlen(configdir) + strlen(CONFIG_FILENAME) + 2); - sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME); - } - else + switch (gconf->vartype) { - write_stderr("%s does not know where to find the server configuration file.\n" - "You must specify the --config-file or -D invocation " - "option or set the PGDATA environment variable.\n", - progname); - return false; - } - - /* - * Set the ConfigFileName GUC variable to its final value, ensuring that - * it can't be overridden later. - */ - SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); - free(fname); + case PGC_BOOL: + { + struct config_bool *conf = (struct config_bool *) gconf; - /* - * Now read the config file for the first time. + 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 */ + break; + } + + str = guc_strdup(FATAL, conf->boot_val); + conf->reset_val = str; + + if (conf->assign_hook) + { + const char *newstr; + + 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; + } + 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; + } + } +} + + +/* + * Select the configuration files and data directory to be used, and + * do the initial read of postgresql.conf. + * + * This is called after processing command-line switches. + * userDoption is the -D switch value if any (NULL if unspecified). + * progname is just for use in error messages. + * + * Returns true on success; on failure, prints a suitable error message + * to stderr and returns false. + */ +bool +SelectConfigFiles(const char *userDoption, const char *progname) +{ + char *configdir; + char *fname; + struct stat stat_buf; + + /* configdir is -D option, or $PGDATA if no -D */ + if (userDoption) + configdir = make_absolute_path(userDoption); + else + configdir = make_absolute_path(getenv("PGDATA")); + + /* + * Find the configuration file: if config_file was specified on the + * command line, use it, else use configdir/postgresql.conf. In any case + * ensure the result is an absolute path, so that it will be interpreted + * the same way by future backends. + */ + if (ConfigFileName) + fname = make_absolute_path(ConfigFileName); + else if (configdir) + { + fname = guc_malloc(FATAL, + strlen(configdir) + strlen(CONFIG_FILENAME) + 2); + sprintf(fname, "%s/%s", configdir, CONFIG_FILENAME); + } + else + { + write_stderr("%s does not know where to find the server configuration file.\n" + "You must specify the --config-file or -D invocation " + "option or set the PGDATA environment variable.\n", + progname); + return false; + } + + /* + * Set the ConfigFileName GUC variable to its final value, ensuring that + * it can't be overridden later. + */ + SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE); + free(fname); + + /* + * Now read the config file for the first time. */ if (stat(ConfigFileName, &stat_buf) != 0) { @@ -3526,9 +3679,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 %d", + conf->gen.name, (int) conf->reset_val); *conf->variable = conf->reset_val; - conf->gen.source = conf->gen.reset_source; break; } case PGC_INT: @@ -3538,9 +3691,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 %d", + conf->gen.name, conf->reset_val); *conf->variable = conf->reset_val; - conf->gen.source = conf->gen.reset_source; break; } case PGC_REAL: @@ -3550,9 +3703,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 %g", + conf->gen.name, conf->reset_val); *conf->variable = conf->reset_val; - conf->gen.source = conf->gen.reset_source; break; } case PGC_STRING: @@ -3570,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) { /* @@ -3581,7 +3735,6 @@ ResetAllOptions(void) } set_string_field(conf, conf->variable, str); - conf->gen.source = conf->gen.reset_source; break; } case PGC_ENUM: @@ -3591,13 +3744,16 @@ 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; - conf->gen.source = conf->gen.reset_source; break; } } + gconf->source = gconf->reset_source; + if (gconf->flags & GUC_REPORT) ReportGUCOption(gconf); } @@ -3861,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; } @@ -3878,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; } @@ -3895,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; } @@ -3916,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) { /* @@ -3948,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; } @@ -4037,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 @@ -4115,7 +4204,7 @@ parse_bool(const char *value, bool *result) * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable * HINT message, or NULL if no hint provided. */ -static bool +bool parse_int(const char *value, int *result, int flags, const char **hintmsg) { int64 val; @@ -4151,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) { @@ -4322,7 +4407,7 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg) * If the string parses okay, return true, else false. * If okay and result is not NULL, return the value in *result. */ -static bool +bool parse_real(const char *value, double *result) { double val; @@ -4357,18 +4442,19 @@ 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 = record->options; - while (entry && entry->name) + const struct config_enum_entry *entry; + + for (entry = record->options; entry && entry->name; entry++) { 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 */ + return NULL; /* silence compiler */ } @@ -4377,88 +4463,73 @@ config_enum_lookup_by_value(struct config_enum *record, int val) * (case-insensitive). * If the enum option is found, sets the retval value and returns * 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, int *retval) +config_enum_lookup_by_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) + const struct config_enum_entry *entry; + + for (entry = record->options; entry && entry->name; entry++) { if (pg_strcasecmp(value, entry->name) == 0) { *retval = entry->val; return TRUE; } - entry++; } + + *retval = 0; return FALSE; } /* * Return a list of all available options for an enum, excluding - * hidden ones, separated by ", " (comma-space). + * hidden ones, separated by the given separator. * If prefix is non-NULL, it is added before the first enum 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, const char *suffix) +config_enum_get_options(struct config_enum * record, const char *prefix, + const char *suffix, const char *separator) { - const struct config_enum_entry *entry = record->options; - int len = 0; - char *hintmsg; + const struct config_enum_entry *entry; + StringInfoData retstr; + int seplen; - if (!entry || !entry->name) - return NULL; /* Should not happen */ + initStringInfo(&retstr); + appendStringInfoString(&retstr, prefix); - while (entry && entry->name) - { - if (!entry->hidden) - len += strlen(entry->name) + 2; /* string and ", " */ - - entry++; - } - - hintmsg = palloc(len + strlen(prefix) + strlen(suffix) + 2); - - strcpy(hintmsg, prefix); - - entry = record->options; - while (entry && entry->name) + seplen = strlen(separator); + for (entry = record->options; entry && entry->name; entry++) { if (!entry->hidden) { - strcat(hintmsg, entry->name); - strcat(hintmsg, ", "); + appendStringInfoString(&retstr, entry->name); + appendBinaryStringInfo(&retstr, separator, seplen); } - - entry++; } - len = strlen(hintmsg); - /* - * 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 (len > 1) - /* Replace final comma/space */ - hintmsg[len-2] = '\0'; + if (retstr.len >= seplen) + { + /* Replace final separator */ + retstr.data[retstr.len - seplen] = '\0'; + retstr.len -= seplen; + } - strcat(hintmsg, suffix); + appendStringInfoString(&retstr, suffix); - return hintmsg; + return retstr.data; } - /* * Call a GucStringAssignHook function, being careful to free the * "newval" string if the hook ereports. @@ -4531,8 +4602,9 @@ set_config_option(const char *name, const char *value, */ elevel = IsUnderPostmaster ? DEBUG3 : LOG; } - else if (source == PGC_S_DATABASE || source == PGC_S_USER) - elevel = INFO; + else if (source == PGC_S_DATABASE || source == PGC_S_USER || + source == PGC_S_DATABASE_USER) + elevel = WARNING; else elevel = ERROR; @@ -4585,16 +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("parameter \"%s\" cannot be changed after server start; configuration file change ignored", - name))); + 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("parameter \"%s\" cannot be changed after server start", - name))); + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); return false; } break; @@ -4629,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), @@ -4653,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 @@ -4764,7 +4879,7 @@ set_config_option(const char *name, const char *value, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid value for parameter \"%s\": \"%s\"", name, value), - hintmsg ? errhint(hintmsg) : 0)); + hintmsg ? errhint("%s", _(hintmsg)) : 0)); return false; } if (newval < conf->min || newval > conf->max) @@ -5022,13 +5137,17 @@ set_config_option(const char *name, const char *value, { if (!config_enum_lookup_by_name(conf, value, &newval)) { - char *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), - hintmsg ? errhint(hintmsg) : 0)); + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), + hintmsg ? errhint("%s", _(hintmsg)) : 0)); if (hintmsg) pfree(hintmsg); @@ -5052,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; } @@ -5092,10 +5211,40 @@ set_config_option(const char *name, const char *value, } +/* + * Set the fields for source file and line number the setting came from. + */ +static void +set_config_sourcefile(const char *name, char *sourcefile, int sourceline) +{ + struct config_generic *record; + int elevel; + + /* + * To avoid cluttering the log, only the postmaster bleats loudly about + * problems with the config file. + */ + elevel = IsUnderPostmaster ? DEBUG3 : LOG; + + record = find_option(name, true, elevel); + /* should not happen */ + if (record == NULL) + elog(ERROR, "unrecognized configuration parameter \"%s\"", name); + + sourcefile = guc_strdup(elevel, sourcefile); + if (record->sourcefile) + free(record->sourcefile); + record->sourcefile = sourcefile; + record->sourceline = sourceline; +} + /* * Set a config option to the given value. See also set_config_option, * this is just the wrapper to be called from outside GUC. NB: this * is used only for non-transactional operations. + * + * Note: there is no support here for setting source file/line, as it + * is currently not needed. */ void SetConfigOption(const char *name, const char *value, @@ -5111,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]; @@ -5125,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))); @@ -5150,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; } @@ -5198,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 @@ -5228,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. @@ -5250,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; } @@ -5308,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)) @@ -5344,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 @@ -5356,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 = @@ -5574,11 +5713,23 @@ init_custom_variable(const char *name, const char *short_desc, const char *long_desc, GucContext context, + int flags, enum config_type type, size_t sz) { 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. + */ + if (context == PGC_POSTMASTER && + !process_shared_preload_libraries_in_progress) + elog(FATAL, "cannot create PGC_POSTMASTER variables after startup"); + gen = (struct config_generic *) guc_malloc(ERROR, sz); memset(gen, 0, sz); @@ -5587,6 +5738,7 @@ init_custom_variable(const char *name, gen->group = CUSTOM_OPTIONS; gen->short_desc = short_desc; gen->long_desc = long_desc; + gen->flags = flags; gen->vartype = type; return gen; @@ -5603,8 +5755,12 @@ define_custom_variable(struct config_generic * variable) const char **nameAddr = &name; const char *value; struct config_string *pHolder; + GucContext phcontext; struct config_generic **res; + /* + * See if there's a placeholder by the same name. + */ res = (struct config_generic **) bsearch((void *) &nameAddr, (void *) guc_variables, num_guc_variables, @@ -5612,7 +5768,11 @@ define_custom_variable(struct config_generic * variable) guc_var_compare); if (res == NULL) { - /* No placeholder to replace, so just add it */ + /* + * No placeholder to replace, so we can just add it ... but first, + * make sure it's initialized to its default value. + */ + InitializeOneGUCOption(variable); add_guc_variable(variable, ERROR); return; } @@ -5628,25 +5788,72 @@ define_custom_variable(struct config_generic * variable) Assert((*res)->vartype == PGC_STRING); pHolder = (struct config_string *) (*res); + /* + * First, set the variable to its default value. We must do this even + * though we intend to immediately apply a new value, since it's possible + * that the new value is invalid. + */ + InitializeOneGUCOption(variable); + /* * Replace the placeholder. We aren't changing the name, so no re-sorting * is necessary */ *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. + */ + switch (pHolder->gen.source) + { + case PGC_S_DEFAULT: + 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 (variable->context == PGC_POSTMASTER) + phcontext = PGC_POSTMASTER; + else + phcontext = PGC_SIGHUP; + break; + case PGC_S_DATABASE: + case PGC_S_USER: + case PGC_S_DATABASE_USER: + case PGC_S_CLIENT: + case PGC_S_SESSION: + default: + phcontext = PGC_USERSET; + break; + } + /* * Assign the string value stored in the placeholder to the real variable. * * XXX this is not really good enough --- it should be a nontransactional * assignment, since we don't want it to roll back if the current xact - * fails later. + * fails later. (Or do we?) */ value = *pHolder->variable; if (value) - set_config_option(name, value, - pHolder->gen.context, pHolder->gen.source, - GUC_ACTION_SET, true); + { + if (set_config_option(name, value, + phcontext, pHolder->gen.source, + GUC_ACTION_SET, true)) + { + /* Also copy over any saved source-location information */ + if (pHolder->gen.sourcefile) + set_config_sourcefile(name, pHolder->gen.sourcefile, + pHolder->gen.sourceline); + } + } /* * Free up as much as we conveniently can of the placeholder structure @@ -5663,18 +5870,20 @@ DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, + bool bootValue, GucContext context, + int flags, GucBoolAssignHook assign_hook, GucShowHook show_hook) { struct config_bool *var; var = (struct config_bool *) - init_custom_variable(name, short_desc, long_desc, context, + init_custom_variable(name, short_desc, long_desc, context, flags, PGC_BOOL, sizeof(struct config_bool)); var->variable = valueAddr; - var->boot_val = *valueAddr; - var->reset_val = *valueAddr; + var->boot_val = bootValue; + var->reset_val = bootValue; var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -5685,20 +5894,22 @@ DefineCustomIntVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, + int bootValue, int minValue, int maxValue, GucContext context, + int flags, GucIntAssignHook assign_hook, GucShowHook show_hook) { struct config_int *var; var = (struct config_int *) - init_custom_variable(name, short_desc, long_desc, context, + init_custom_variable(name, short_desc, long_desc, context, flags, PGC_INT, sizeof(struct config_int)); var->variable = valueAddr; - var->boot_val = *valueAddr; - var->reset_val = *valueAddr; + var->boot_val = bootValue; + var->reset_val = bootValue; var->min = minValue; var->max = maxValue; var->assign_hook = assign_hook; @@ -5711,20 +5922,22 @@ DefineCustomRealVariable(const char *name, const char *short_desc, const char *long_desc, double *valueAddr, + double bootValue, double minValue, double maxValue, GucContext context, + int flags, GucRealAssignHook assign_hook, GucShowHook show_hook) { struct config_real *var; var = (struct config_real *) - init_custom_variable(name, short_desc, long_desc, context, + init_custom_variable(name, short_desc, long_desc, context, flags, PGC_REAL, sizeof(struct config_real)); var->variable = valueAddr; - var->boot_val = *valueAddr; - var->reset_val = *valueAddr; + var->boot_val = bootValue; + var->reset_val = bootValue; var->min = minValue; var->max = maxValue; var->assign_hook = assign_hook; @@ -5737,17 +5950,19 @@ DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, + const char *bootValue, GucContext context, + int flags, GucStringAssignHook assign_hook, GucShowHook show_hook) { struct config_string *var; var = (struct config_string *) - init_custom_variable(name, short_desc, long_desc, context, + init_custom_variable(name, short_desc, long_desc, context, flags, PGC_STRING, sizeof(struct config_string)); var->variable = valueAddr; - var->boot_val = *valueAddr; + var->boot_val = bootValue; /* we could probably do without strdup, but keep it like normal case */ if (var->boot_val) var->reset_val = guc_strdup(ERROR, var->boot_val); @@ -5761,19 +5976,21 @@ DefineCustomEnumVariable(const char *name, const char *short_desc, const char *long_desc, int *valueAddr, - const struct config_enum_entry *options, + int bootValue, + const struct config_enum_entry * options, GucContext context, + int flags, GucEnumAssignHook assign_hook, GucShowHook show_hook) { struct config_enum *var; var = (struct config_enum *) - init_custom_variable(name, short_desc, long_desc, context, + init_custom_variable(name, short_desc, long_desc, context, flags, PGC_ENUM, sizeof(struct config_enum)); var->variable = valueAddr; - var->boot_val = *valueAddr; - var->reset_val = *valueAddr; + var->boot_val = bootValue; + var->reset_val = bootValue; var->options = options; var->assign_hook = assign_hook; var->show_hook = show_hook; @@ -5783,22 +6000,21 @@ DefineCustomEnumVariable(const char *name, void EmitWarningsOnPlaceholders(const char *className) { - struct config_generic **vars = guc_variables; - struct config_generic **last = vars + num_guc_variables; - - int nameLen = strlen(className); + int classLen = strlen(className); + int i; - while (vars < last) + for (i = 0; i < num_guc_variables; i++) { - struct config_generic *var = *vars++; + struct config_generic *var = guc_variables[i]; if ((var->flags & GUC_CUSTOM_PLACEHOLDER) != 0 && - strncmp(className, var->name, nameLen) == 0 && - var->name[nameLen] == GUC_QUALIFIER_SEPARATOR) + strncmp(className, var->name, classLen) == 0 && + var->name[classLen] == GUC_QUALIFIER_SEPARATOR) { - ereport(INFO, + ereport(WARNING, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("unrecognized configuration parameter \"%s\"", var->name))); + errmsg("unrecognized configuration parameter \"%s\"", + var->name))); } } } @@ -5831,7 +6047,6 @@ GetPGVariableResultDesc(const char *name) TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0); - } else { @@ -5887,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); @@ -5898,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); @@ -6041,6 +6274,8 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) { case PGC_BOOL: { + struct config_bool *lconf = (struct config_bool *) conf; + /* min_val */ values[9] = NULL; @@ -6049,6 +6284,12 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; + + /* boot_val */ + values[12] = pstrdup(lconf->boot_val ? "on" : "off"); + + /* reset_val */ + values[13] = pstrdup(lconf->reset_val ? "on" : "off"); } break; @@ -6066,6 +6307,14 @@ 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); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val); + values[13] = pstrdup(buffer); } break; @@ -6083,11 +6332,21 @@ 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); + + /* 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; + /* min_val */ values[9] = NULL; @@ -6096,11 +6355,25 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = 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; + else + values[13] = pstrdup(lconf->reset_val); } break; case PGC_ENUM: { + struct config_enum *lconf = (struct config_enum *) conf; + /* min_val */ values[9] = NULL; @@ -6108,7 +6381,21 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) values[10] = NULL; /* enumvals */ - values[11] = config_enum_get_options((struct config_enum *) conf, "", ""); + + /* + * NOTE! enumvals with double quotes in them are not + * supported! + */ + values[11] = config_enum_get_options((struct config_enum *) conf, + "{\"", "\"}", "\",\""); + + /* boot_val */ + values[12] = pstrdup(config_enum_lookup_by_value(lconf, + lconf->boot_val)); + + /* reset_val */ + values[13] = pstrdup(config_enum_lookup_by_value(lconf, + lconf->reset_val)); } break; @@ -6126,9 +6413,32 @@ GetConfigOptionByNum(int varnum, const char **values, bool *noshow) /* enumvals */ values[11] = NULL; + + /* boot_val */ + values[12] = 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 + * non-superusers. + */ + if (conf->source == PGC_S_FILE && superuser()) + { + values[14] = conf->sourcefile; + snprintf(buffer, sizeof(buffer), "%d", conf->sourceline); + values[15] = pstrdup(buffer); + } + else + { + values[14] = NULL; + values[15] = NULL; + } } /* @@ -6164,7 +6474,7 @@ show_config_by_name(PG_FUNCTION_ARGS) * show_all_settings - equiv to SHOW ALL command but implemented as * a Table Function. */ -#define NUM_PG_SETTINGS_ATTS 12 +#define NUM_PG_SETTINGS_ATTS 16 Datum show_all_settings(PG_FUNCTION_ARGS) @@ -6215,7 +6525,15 @@ show_all_settings(PG_FUNCTION_ARGS) TupleDescInitEntry(tupdesc, (AttrNumber) 11, "max_val", 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) 15, "sourcefile", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 16, "sourceline", + INT4OID, -1, 0); /* * Generate attribute metadata needed later to produce tuples from raw @@ -6305,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; @@ -6421,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); @@ -6490,8 +6805,8 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue) struct config_enum *conf = (struct config_enum *) record; int newval; - return config_enum_lookup_by_name(conf, newvalue, &newval) - && *conf->variable == newval; + return config_enum_lookup_by_name(conf, newvalue, &newval) && + *conf->variable == newval; } } @@ -6502,19 +6817,82 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue) #ifdef EXEC_BACKEND /* - * This routine dumps out all non-default GUC options into a binary + * These routines dump out all non-default GUC options into a binary * file that is read by all exec'ed backends. The format is: * * variable name, string, null terminated * variable value, string, null terminated * variable source, integer */ +static void +write_one_nondefault_variable(FILE *fp, struct config_generic * gconf) +{ + if (gconf->source == PGC_S_DEFAULT) + return; + + fprintf(fp, "%s", gconf->name); + fputc(0, fp); + + switch (gconf->vartype) + { + case PGC_BOOL: + { + struct config_bool *conf = (struct config_bool *) gconf; + + if (*conf->variable) + fprintf(fp, "true"); + else + fprintf(fp, "false"); + } + break; + + case PGC_INT: + { + struct config_int *conf = (struct config_int *) gconf; + + fprintf(fp, "%d", *conf->variable); + } + break; + + case PGC_REAL: + { + struct config_real *conf = (struct config_real *) gconf; + + /* Could lose precision here? */ + fprintf(fp, "%f", *conf->variable); + } + break; + + case PGC_STRING: + { + struct config_string *conf = (struct config_string *) gconf; + + 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; + } + + fputc(0, fp); + + fwrite(&gconf->source, sizeof(gconf->source), 1, fp); +} + void write_nondefault_variables(GucContext context) { - int i; int elevel; FILE *fp; + struct config_generic *cvc_conf; + int i; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); @@ -6533,66 +6911,20 @@ write_nondefault_variables(GucContext context) return; } + /* + * custom_variable_classes must be written out first; otherwise we might + * reject custom variable values while reading the file. + */ + cvc_conf = find_option("custom_variable_classes", false, ERROR); + if (cvc_conf) + write_one_nondefault_variable(fp, cvc_conf); + for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; - if (gconf->source != PGC_S_DEFAULT) - { - fprintf(fp, "%s", gconf->name); - fputc(0, fp); - - switch (gconf->vartype) - { - case PGC_BOOL: - { - struct config_bool *conf = (struct config_bool *) gconf; - - if (*conf->variable == 0) - fprintf(fp, "false"); - else - fprintf(fp, "true"); - } - break; - - case PGC_INT: - { - struct config_int *conf = (struct config_int *) gconf; - - fprintf(fp, "%d", *conf->variable); - } - break; - - case PGC_REAL: - { - struct config_real *conf = (struct config_real *) gconf; - - /* Could lose precision here? */ - fprintf(fp, "%f", *conf->variable); - } - break; - - case PGC_STRING: - { - struct config_string *conf = (struct config_string *) gconf; - - 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; - } - - fputc(0, fp); - - fwrite(&gconf->source, sizeof(gconf->source), 1, fp); - } + if (gconf != cvc_conf) + write_one_nondefault_variable(fp, gconf); } if (FreeFile(fp)) @@ -6969,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; } @@ -7108,11 +7440,11 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) continue; } - if (hasSpaceAfterToken || !isalnum((unsigned char) c)) + if (hasSpaceAfterToken || !(isalnum((unsigned char) c) || c == '_')) { /* - * Syntax error due to token following space after token or non - * alpha numeric character + * Syntax error due to token following space after token or + * non-identifier character */ pfree(buf.data); return NULL; @@ -7147,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) { @@ -7208,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; } @@ -7349,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; } @@ -7361,13 +7720,121 @@ 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; } +static bool +assign_effective_io_concurrency(int newval, bool doit, GucSource source) +{ +#ifdef USE_PREFETCH + double new_prefetch_pages = 0.0; + int i; + + /*---------- + * The user-visible GUC parameter is the number of drives (spindles), + * which we need to translate to a number-of-pages-to-prefetch target. + * + * The expected number of prefetch pages needed to keep N drives busy is: + * + * 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) + * + * 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 + * reasonable numbers of drives we might as well just compute the series. + * + * Alternatively we could set the target to the number of pages necessary + * so that the expected number of active spindles is some arbitrary + * percentage of the total. This sounds the same but is actually slightly + * different. The result ends up being ln(1-P)/ln((n-1)/n) where P is + * that desired fraction. + * + * Experimental results show that both of these formulas aren't aggressive + * enough, but we don't really have any better proposals. + * + * Note that if newval = 0 (disabled), we must set target = 0. + *---------- + */ + + for (i = 1; i <= newval; i++) + new_prefetch_pages += (double) newval / (double) i; + + /* This range check shouldn't fail, but let's be paranoid */ + if (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX) + { + if (doit) + target_prefetch_pages = (int) rint(new_prefetch_pages); + return true; + } + else + return false; +#else + return true; +#endif /* USE_PREFETCH */ +} + +static const char * +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; + + return canon_val; + } + else + 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"