* 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 <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.479 2008/11/19 02:07:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.532 2010/01/07 04:53:35 tgl Exp $
*
*--------------------------------------------------------------------
*/
#include <ctype.h>
#include <float.h>
+#include <math.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#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"
#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"
#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)
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;
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
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);
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)
{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},
{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
*/
char *pgstat_temp_directory;
+char *default_do_language;
+
+char *application_name;
+
int tcp_keepalives_idle;
int tcp_keepalives_interval;
int tcp_keepalives_count;
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;
/* 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",
&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."),
&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."),
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.")
&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."),
},
{
- {"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
},
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."),
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
"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,
* 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,
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."),
GUC_UNIT_MS
},
&VacuumCostDelay,
- 0, 0, 1000, NULL, NULL
+ 0, 0, 100, NULL, NULL
},
{
GUC_UNIT_MS
},
&autovacuum_vac_cost_delay,
- 20, -1, 1000, NULL, NULL
+ 20, -1, 100, NULL, NULL
},
{
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
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."),
"(FLT_DIG or DBL_DIG as appropriate).")
},
&extra_float_digits,
- 0, -15, 2, NULL, NULL
+ 0, -15, 3, NULL, NULL
},
{
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."),
{
{"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
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
},
NULL
},
&autovacuum_freeze_max_age,
+ /* see pg_resetxlog if you change the upper-limit value */
200000000, 100000000, 2000000000, NULL, NULL
},
{
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,
},
{
- {"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
},
{
- {"krb_srvname", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ {"krb_srvname", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the name of the Kerberos service."),
NULL
},
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,
{"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
{"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
},
#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
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."),
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."),
XACT_READ_COMMITTED, isolation_level_options, NULL, NULL
},
- {
+ {
{"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
},
#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."),
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."),
NULL
},
&sync_method,
- DEFAULT_SYNC_METHOD, sync_method_options,
+ DEFAULT_SYNC_METHOD, sync_method_options,
assign_xlog_sync_method, NULL
},
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);
*((struct config_string *) gconf)->variable);
break;
case PGC_ENUM:
- val->enumval =
+ val->enumval =
*((struct config_enum *) gconf)->variable;
break;
}
* 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;
switch (gconf->vartype)
{
case PGC_BOOL:
- {
- struct config_bool *conf = (struct config_bool *) gconf;
-
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->boot_val, true,
- PGC_S_DEFAULT))
- elog(FATAL, "failed to initialize %s to %d",
- conf->gen.name, (int) conf->boot_val);
- *conf->variable = conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_INT:
- {
- struct config_int *conf = (struct config_int *) gconf;
-
- Assert(conf->boot_val >= conf->min);
- Assert(conf->boot_val <= conf->max);
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->boot_val, true,
- PGC_S_DEFAULT))
- elog(FATAL, "failed to initialize %s to %d",
- conf->gen.name, conf->boot_val);
- *conf->variable = conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_REAL:
- {
- struct config_real *conf = (struct config_real *) gconf;
-
- Assert(conf->boot_val >= conf->min);
- Assert(conf->boot_val <= conf->max);
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->boot_val, true,
- PGC_S_DEFAULT))
- elog(FATAL, "failed to initialize %s to %g",
- conf->gen.name, conf->boot_val);
- *conf->variable = conf->reset_val = conf->boot_val;
- break;
- }
- case PGC_STRING:
- {
- struct config_string *conf = (struct config_string *) gconf;
- char *str;
-
- *conf->variable = NULL;
- conf->reset_val = NULL;
-
- if (conf->boot_val == NULL)
{
- /* leave the value NULL, do not call assign hook */
+ struct config_bool *conf = (struct config_bool *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->boot_val, true,
+ PGC_S_DEFAULT))
+ elog(FATAL, "failed to initialize %s to %d",
+ conf->gen.name, (int) conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) gconf;
- str = guc_strdup(FATAL, conf->boot_val);
- conf->reset_val = str;
+ Assert(conf->boot_val >= conf->min);
+ Assert(conf->boot_val <= conf->max);
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->boot_val, true,
+ PGC_S_DEFAULT))
+ elog(FATAL, "failed to initialize %s to %d",
+ conf->gen.name, conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
+ break;
+ }
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
- if (conf->assign_hook)
+ Assert(conf->boot_val >= conf->min);
+ Assert(conf->boot_val <= conf->max);
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->boot_val, true,
+ PGC_S_DEFAULT))
+ elog(FATAL, "failed to initialize %s to %g",
+ conf->gen.name, conf->boot_val);
+ *conf->variable = conf->reset_val = conf->boot_val;
+ break;
+ }
+ case PGC_STRING:
{
- const char *newstr;
+ struct config_string *conf = (struct config_string *) gconf;
+ char *str;
+
+ *conf->variable = NULL;
+ conf->reset_val = NULL;
- newstr = (*conf->assign_hook) (str, true,
- PGC_S_DEFAULT);
- if (newstr == NULL)
+ if (conf->boot_val == NULL)
{
- elog(FATAL, "failed to initialize %s to \"%s\"",
- conf->gen.name, str);
+ /* leave the value NULL, do not call assign hook */
+ break;
}
- else if (newstr != str)
+
+ str = guc_strdup(FATAL, conf->boot_val);
+ conf->reset_val = str;
+
+ if (conf->assign_hook)
{
- free(str);
+ const char *newstr;
- /*
- * See notes in set_config_option about casting
- */
- str = (char *) newstr;
- conf->reset_val = str;
+ newstr = (*conf->assign_hook) (str, true,
+ PGC_S_DEFAULT);
+ if (newstr == NULL)
+ {
+ elog(FATAL, "failed to initialize %s to \"%s\"",
+ conf->gen.name, str);
+ }
+ else if (newstr != str)
+ {
+ free(str);
+
+ /*
+ * See notes in set_config_option about casting
+ */
+ str = (char *) newstr;
+ conf->reset_val = str;
+ }
}
+ *conf->variable = str;
+ break;
}
- *conf->variable = str;
- break;
- }
case PGC_ENUM:
- {
- struct config_enum *conf = (struct config_enum *) gconf;
-
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (conf->boot_val, true,
- PGC_S_DEFAULT))
- elog(FATAL, "failed to initialize %s to %s",
- conf->gen.name,
- config_enum_lookup_by_value(conf, conf->boot_val));
- *conf->variable = conf->reset_val = conf->boot_val;
- break;
- }
+ {
+ struct config_enum *conf = (struct config_enum *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->boot_val, true,
+ PGC_S_DEFAULT))
+ elog(FATAL, "failed to initialize %s to %s",
+ conf->gen.name,
+ config_enum_lookup_by_value(conf, conf->boot_val));
+ *conf->variable = conf->reset_val = conf->boot_val;
+ break;
+ }
}
}
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;
}
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;
}
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;
}
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)
{
/*
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;
}
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;
}
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;
}
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;
}
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)
{
/*
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;
}
}
}
-
-/*
- * 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
/*
* 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)
{
* 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 */
}
* (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.
*/
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;
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;
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),
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
(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)
{
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);
{
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;
}
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;
* 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];
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)));
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;
}
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
* 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.
{
/*
* 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;
}
/*
* 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))
break;
case T_String:
val = strVal(&con->val);
- if (typename != NULL)
+ if (typeName != NULL)
{
/*
* Must be a ConstInterval argument for TIME ZONE. Coerce
Datum interval;
char *intervalout;
- typoid = typenameTypeId(NULL, typename, &typmod);
+ typoid = typenameTypeId(NULL, typeName, &typmod);
Assert(typoid == INTERVALOID);
interval =
{
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);
const char **nameAddr = &name;
const char *value;
struct config_string *pHolder;
- GucContext phcontext;
+ GucContext phcontext;
struct config_generic **res;
/*
*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)
{
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:
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
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,
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)));
}
}
}
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description",
TEXTOID, -1, 0);
-
}
else
{
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);
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);
/* 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;
/* 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;
/* 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);
}
case PGC_ENUM:
{
- struct config_enum *lconf = (struct config_enum *) conf;
+ struct config_enum *lconf = (struct config_enum *) conf;
/* min_val */
values[9] = NULL;
values[10] = NULL;
/* enumvals */
- values[11] = config_enum_get_options((struct config_enum *) conf, "", "");
- /* boot_val */
- values[12] = pstrdup(config_enum_lookup_by_value(lconf, lconf->boot_val));
+ /*
+ * 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));
+ /* reset_val */
+ values[13] = pstrdup(config_enum_lookup_by_value(lconf,
+ lconf->reset_val));
}
break;
/* 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())
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) 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",
{
/*
* 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;
{
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);
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;
}
}
#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);
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))
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;
}
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;
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)
{
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;
}
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;
}
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 *
{
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"