X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fmisc%2Fguc.c;h=38d577f87bb9a892a8908bc8f7d3dcc2eafe02bb;hb=901be0fad4034c9cf8a3588fd6cf2ece82e4b8ce;hp=00dac28c27e14f32d89ad0790694cc2bd0140e25;hpb=c98c9114cb3bff8a493333ef48d16eb55601af54;p=postgresql diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 00dac28c27..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.482 2008/12/02 02:00:32 alvherre 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,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" @@ -86,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) @@ -113,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; @@ -138,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 @@ -150,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); @@ -163,13 +169,15 @@ 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, - const char *separator); +static char *config_enum_get_options(struct config_enum * record, + const char *prefix, const char *suffix, + const char *separator); /* @@ -178,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) @@ -239,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}, @@ -311,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 */ @@ -358,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; @@ -396,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; @@ -430,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", @@ -635,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."), @@ -663,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."), @@ -716,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.") @@ -1027,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."), @@ -1144,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 }, @@ -1189,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."), @@ -1210,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 @@ -1245,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, @@ -1317,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, @@ -1330,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."), @@ -1456,7 +1509,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &VacuumCostDelay, - 0, 0, 1000, NULL, NULL + 0, 0, 100, NULL, NULL }, { @@ -1466,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 }, { @@ -1487,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 @@ -1533,9 +1589,30 @@ static struct config_int ConfigureNamesInt[] = NULL }, &vacuum_freeze_min_age, - 100000000, 0, 1000000000, NULL, NULL + 50000000, 0, 1000000000, NULL, NULL + }, + + { + {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."), + NULL + }, + &vacuum_freeze_table_age, + 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."), @@ -1648,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 }, { @@ -1692,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."), @@ -1754,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 @@ -1779,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 }, @@ -1817,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 }, { @@ -1986,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, @@ -2123,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 @@ -2143,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 }, @@ -2151,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, @@ -2284,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 @@ -2295,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 @@ -2493,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 @@ -2511,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."), @@ -2521,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."), @@ -2530,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, @@ -2589,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."), @@ -2608,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."), @@ -2623,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 }, @@ -2690,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); @@ -2821,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; } @@ -3258,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; @@ -3270,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; - newstr = (*conf->assign_hook) (str, true, - PGC_S_DEFAULT); - if (newstr == NULL) + *conf->variable = NULL; + conf->reset_val = 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; + } } } @@ -3552,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; } @@ -3563,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; } @@ -3574,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; } @@ -3593,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) { /* @@ -3613,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; } @@ -3884,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; } @@ -3901,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; } @@ -3918,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; } @@ -3939,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) { /* @@ -3971,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; } @@ -4060,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 @@ -4174,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) { @@ -4380,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; @@ -4392,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 */ } @@ -4403,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; @@ -4429,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++) { @@ -4450,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) { @@ -4540,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; @@ -4594,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; @@ -4640,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), @@ -4664,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 @@ -4775,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("%s", hintmsg) : 0)); + hintmsg ? errhint("%s", _(hintmsg)) : 0)); return false; } if (newval < conf->min || newval > conf->max) @@ -5033,17 +5137,17 @@ 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), - hintmsg ? errhint("%s", hintmsg) : 0)); + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), + hintmsg ? errhint("%s", _(hintmsg)) : 0)); if (hintmsg) pfree(hintmsg); @@ -5067,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; } @@ -5117,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; @@ -5156,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]; @@ -5170,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))); @@ -5195,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; } @@ -5243,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 @@ -5273,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. @@ -5295,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; } @@ -5353,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)) @@ -5389,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 @@ -5401,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 = @@ -5625,6 +5719,17 @@ 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. + */ + 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); @@ -5650,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; /* @@ -5697,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) { @@ -5707,10 +5812,20 @@ define_custom_variable(struct config_generic * variable) case PGC_S_ENV_VAR: case PGC_S_FILE: case PGC_S_ARGV: - phcontext = PGC_SIGHUP; + + /* + * 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: @@ -5728,9 +5843,17 @@ define_custom_variable(struct config_generic * variable) value = *pHolder->variable; if (value) - set_config_option(name, value, - phcontext, 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 @@ -5854,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, @@ -5877,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))); } } } @@ -5925,7 +6047,6 @@ GetPGVariableResultDesc(const char *name) TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0); - } else { @@ -5981,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); @@ -5992,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); @@ -6169,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; @@ -6194,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; @@ -6217,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); } @@ -6233,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; @@ -6242,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; @@ -6271,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()) @@ -6383,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", @@ -6480,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; @@ -6596,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); @@ -6677,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); @@ -6708,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)) @@ -7144,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; } @@ -7283,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; @@ -7322,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) { @@ -7383,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; } @@ -7524,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; } @@ -7536,33 +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; - 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"