* See src/backend/utils/misc/README for more information.
*
*
- * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2014, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
#include "funcapi.h"
#include "libpq/auth.h"
#include "libpq/be-fsstubs.h"
+#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
+#include "postmaster/bgworker.h"
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "postmaster/walwriter.h"
+#include "replication/slot.h"
#include "replication/syncrep.h"
#include "replication/walreceiver.h"
#include "replication/walsender.h"
#include "storage/bufmgr.h"
+#include "storage/dsm_impl.h"
#include "storage/standby.h"
#include "storage/fd.h"
+#include "storage/pg_shmem.h"
+#include "storage/proc.h"
#include "storage/predicate.h"
#include "tcop/tcopprot.h"
#include "tsearch/ts_cache.h"
#include "utils/plancache.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
+#include "utils/snapmgr.h"
#include "utils/tzparser.h"
#include "utils/xml.h"
#ifndef PG_KRB_SRVTAB
#define PG_KRB_SRVTAB ""
#endif
-#ifndef PG_KRB_SRVNAM
-#define PG_KRB_SRVNAM ""
-#endif
#define CONFIG_FILENAME "postgresql.conf"
#define HBA_FILENAME "pg_hba.conf"
#define MAX_KILOBYTES (INT_MAX / 1024)
#endif
-/*
- * Note: MAX_BACKENDS is limited to 2^23-1 because inval.c stores the
- * backend ID as a 3-byte signed integer. Even if that limitation were
- * removed, we still could not exceed INT_MAX/4 because some places compute
- * 4*MaxBackends without any overflow check. This is rechecked in
- * check_maxconnections, since MaxBackends is computed as MaxConnections
- * plus autovacuum_max_workers plus one (for the autovacuum launcher).
- */
-#define MAX_BACKENDS 0x7fffff
-
#define KB_PER_MB (1024)
#define KB_PER_GB (1024*1024)
+#define KB_PER_TB (1024*1024*1024)
#define MS_PER_S 1000
#define S_PER_MIN 60
extern int CommitSiblings;
extern char *default_tablespace;
extern char *temp_tablespaces;
+extern bool ignore_checksum_failure;
extern bool synchronize_seqscans;
-extern bool fullPageWrites;
-extern int ssl_renegotiation_limit;
extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
#ifdef TRACE_SORT
extern bool trace_sort;
static void assign_session_replication_role(int newval, void *extra);
static bool check_temp_buffers(int *newval, void **extra, GucSource source);
static bool check_phony_autocommit(bool *newval, void **extra, GucSource source);
-static bool check_debug_assertions(bool *newval, void **extra, GucSource source);
static bool check_bonjour(bool *newval, void **extra, GucSource source);
static bool check_ssl(bool *newval, void **extra, GucSource source);
static bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
static const char *show_tcp_keepalives_interval(void);
static const char *show_tcp_keepalives_count(void);
static bool check_maxconnections(int *newval, void **extra, GucSource source);
-static void assign_maxconnections(int newval, void *extra);
+static bool check_max_worker_processes(int *newval, void **extra, GucSource source);
static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
-static void assign_autovacuum_max_workers(int newval, void *extra);
+static bool check_autovacuum_work_mem(int *newval, void **extra, GucSource source);
static bool check_effective_io_concurrency(int *newval, void **extra, GucSource source);
static void assign_effective_io_concurrency(int newval, void *extra);
static void assign_pgstat_temp_directory(const char *newval, void *extra);
static bool check_application_name(char **newval, void **extra, GucSource source);
static void assign_application_name(const char *newval, void *extra);
+static bool check_cluster_name(char **newval, void **extra, GucSource source);
static const char *show_unix_socket_permissions(void);
static const char *show_log_file_mode(void);
const char *prefix, const char *suffix,
const char *separator);
+static bool validate_conf_option(struct config_generic * record,
+ const char *name, const char *value, GucSource source,
+ int elevel, bool freemem, void *newval, void **newextra);
+
/*
* Options for enum values defined in this module.
};
/*
- * Although only "on", "off", and "local" are documented, we
+ * Although only "on", "off", "remote_write", and "local" are documented, we
* accept all the likely variants of "on" and "off".
*/
static const struct config_enum_entry synchronous_commit_options[] = {
{"local", SYNCHRONOUS_COMMIT_LOCAL_FLUSH, false},
+ {"remote_write", SYNCHRONOUS_COMMIT_REMOTE_WRITE, false},
{"on", SYNCHRONOUS_COMMIT_ON, false},
{"off", SYNCHRONOUS_COMMIT_OFF, false},
{"true", SYNCHRONOUS_COMMIT_ON, true},
{NULL, 0, false}
};
+/*
+ * Although only "on", "off", "try" are documented, we accept all the likely
+ * variants of "on" and "off".
+ */
+static const struct config_enum_entry huge_pages_options[] = {
+ {"off", HUGE_PAGES_OFF, false},
+ {"on", HUGE_PAGES_ON, false},
+ {"try", HUGE_PAGES_TRY, false},
+ {"true", HUGE_PAGES_ON, true},
+ {"false", HUGE_PAGES_OFF, true},
+ {"yes", HUGE_PAGES_ON, true},
+ {"no", HUGE_PAGES_OFF, true},
+ {"1", HUGE_PAGES_ON, true},
+ {"0", HUGE_PAGES_OFF, true},
+ {NULL, 0, false}
+};
+
/*
* Options for enum values stored in other modules
*/
extern const struct config_enum_entry wal_level_options[];
extern const struct config_enum_entry sync_method_options[];
+extern const struct config_enum_entry dynamic_shared_memory_options[];
/*
* GUC option variables that are exported from this module
*/
-#ifdef USE_ASSERT_CHECKING
-bool assert_enabled = true;
-#else
-bool assert_enabled = false;
-#endif
bool log_duration = false;
bool Debug_print_plan = false;
bool Debug_print_parse = false;
bool log_statement_stats = false; /* this is sort of all three
* above together */
bool log_btree_build_stats = false;
+char *event_source;
bool check_function_bodies = true;
bool default_with_oids = false;
int num_temp_buffers = 1024;
+char *cluster_name = "";
char *data_directory;
char *ConfigFileName;
char *HbaFileName;
int tcp_keepalives_interval;
int tcp_keepalives_count;
+/*
+ * This really belongs in pg_shmem.c, but is defined here so that it doesn't
+ * need to be duplicated in all the different implementations of pg_shmem.c.
+ */
+int huge_pages;
+
/*
* These variables are all dummies that don't do anything, except in some
* cases provide the value for SHOW to display. The real state is elsewhere
* and is kept in sync by assign_hooks.
*/
-static char *log_destination_string;
-
static char *syslog_ident_str;
static bool phony_autocommit;
static bool session_auth_is_superuser;
static int block_size;
static int segment_size;
static int wal_block_size;
+static bool data_checksums;
static int wal_segment_size;
static bool integer_datetimes;
static int effective_io_concurrency;
+static bool assert_enabled;
/* should be static, but commands/variable.c needs to get at this */
char *role_string;
/* PGC_S_ENV_VAR */ "environment variable",
/* PGC_S_FILE */ "configuration file",
/* PGC_S_ARGV */ "command line",
+ /* PGC_S_GLOBAL */ "global",
/* PGC_S_DATABASE */ "database",
/* PGC_S_USER */ "user",
/* PGC_S_DATABASE_USER */ "database user",
gettext_noop("Client Connection Defaults / Statement Behavior"),
/* CLIENT_CONN_LOCALE */
gettext_noop("Client Connection Defaults / Locale and Formatting"),
+ /* CLIENT_CONN_PRELOAD */
+ gettext_noop("Client Connection Defaults / Shared Library Preloading"),
/* CLIENT_CONN_OTHER */
gettext_noop("Client Connection Defaults / Other Defaults"),
/* LOCK_MANAGEMENT */
true,
NULL, NULL, NULL
},
+ {
+ {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ gettext_noop("Enables the planner's use of index-only-scan plans."),
+ NULL
+ },
+ &enable_indexonlyscan,
+ true,
+ NULL, NULL, NULL
+ },
{
{"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
false,
check_ssl, NULL, NULL
},
+ {
+ {"ssl_prefer_server_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Give priority to server ciphersuite order."),
+ NULL
+ },
+ &SSLPreferServerCiphers,
+ true,
+ NULL, NULL, NULL
+ },
{
{"fsync", PGC_SIGHUP, WAL_SETTINGS,
gettext_noop("Forces synchronization of updates to disk."),
true,
NULL, NULL, NULL
},
+ {
+ {"ignore_checksum_failure", PGC_SUSET, DEVELOPER_OPTIONS,
+ gettext_noop("Continues processing after a checksum failure."),
+ gettext_noop("Detection of a checksum failure normally causes PostgreSQL to "
+ "report an error, aborting the current transaction. Setting "
+ "ignore_checksum_failure to true causes the system to ignore the failure "
+ "(but still report a warning), and continue processing. This "
+ "behavior could cause crashes or other serious problems. Only "
+ "has an effect if checksums are enabled."),
+ GUC_NOT_IN_SAMPLE
+ },
+ &ignore_checksum_failure,
+ false,
+ NULL, NULL, NULL
+ },
{
{"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Continues processing past damaged page headers."),
true,
NULL, NULL, NULL
},
+
+ {
+ {"wal_log_hints", PGC_POSTMASTER, WAL_SETTINGS,
+ gettext_noop("Writes full pages to WAL when first modified after a checkpoint, even for a non-critical modifications"),
+ NULL
+ },
+ &wal_log_hints,
+ false,
+ NULL, NULL, NULL
+ },
+
{
{"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
gettext_noop("Logs each checkpoint."),
NULL, NULL, NULL
},
{
- {"debug_assertions", PGC_USERSET, DEVELOPER_OPTIONS,
- gettext_noop("Turns on various assertion checks."),
- gettext_noop("This is a debugging aid."),
- GUC_NOT_IN_SAMPLE
+ {"debug_assertions", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows whether the running server has assertion checks enabled."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
&assert_enabled,
#ifdef USE_ASSERT_CHECKING
#else
false,
#endif
- check_debug_assertions, NULL, NULL
+ NULL, NULL, NULL
},
{
#ifdef BTREE_BUILD_STATS
{
{"log_btree_build_stats", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Logs system resource usage statistics (memory and CPU) on various B-tree operations."),
NULL,
GUC_NOT_IN_SAMPLE
},
true,
NULL, NULL, NULL
},
+ {
+ {"track_io_timing", PGC_SUSET, STATS_COLLECTOR,
+ gettext_noop("Collects timing statistics for database I/O activity."),
+ NULL
+ },
+ &track_io_timing,
+ false,
+ NULL, NULL, NULL
+ },
{
{"update_process_title", PGC_SUSET, STATS_COLLECTOR,
#ifdef LOCK_DEBUG
{
{"trace_locks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Emits information about lock usage."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Emits information about user lock usage."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Emits information about lightweight lock usage."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Dumps information about all current locks when a deadlock timeout occurs."),
NULL,
GUC_NOT_IN_SAMPLE
},
NULL, NULL, NULL
},
+ {
+ {"data_checksums", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows whether data checksums are turned on for this cluster."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &data_checksums,
+ false,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
GUC_UNIT_S
},
&XLogArchiveTimeout,
- 0, 0, INT_MAX,
+ 0, 0, INT_MAX / 2,
NULL, NULL, NULL
},
{
GUC_NOT_IN_SAMPLE | GUC_UNIT_S
},
&PostAuthDelay,
- 0, 0, INT_MAX,
+ 0, 0, INT_MAX / 1000000,
NULL, NULL, NULL
},
{
NULL, NULL, NULL
},
+ {
+ {"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY,
+ gettext_noop("Sets the maximum wait time to receive data from the primary."),
+ NULL,
+ GUC_UNIT_MS
+ },
+ &wal_receiver_timeout,
+ 60 * 1000, 0, INT_MAX,
+ NULL, NULL, NULL
+ },
+
{
{"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
gettext_noop("Sets the maximum number of concurrent connections."),
},
&MaxConnections,
100, 1, MAX_BACKENDS,
- check_maxconnections, assign_maxconnections, NULL
+ check_maxconnections, NULL, NULL
},
{
GUC_UNIT_KB
},
&work_mem,
- 1024, 64, MAX_KILOBYTES,
+ 4096, 64, MAX_KILOBYTES,
NULL, NULL, NULL
},
GUC_UNIT_KB
},
&maintenance_work_mem,
- 16384, 1024, MAX_KILOBYTES,
+ 65536, 1024, MAX_KILOBYTES,
NULL, NULL, NULL
},
{
{"temp_file_limit", PGC_SUSET, RESOURCES_DISK,
- gettext_noop("Limits the total size of all temp files used by each session."),
+ gettext_noop("Limits the total size of all temporary files used by each session."),
gettext_noop("-1 means no limit."),
GUC_UNIT_KB
},
#ifdef LOCK_DEBUG
{
{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
- NULL,
+ gettext_noop("Sets the minimum OID of tables for tracking locks."),
+ gettext_noop("Is used to avoid output on system tables."),
GUC_NOT_IN_SAMPLE
},
&Trace_lock_oidmin,
},
{
{"trace_lock_table", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("No description available."),
+ gettext_noop("Sets the OID of the table with unconditionally lock tracing."),
NULL,
GUC_NOT_IN_SAMPLE
},
NULL, NULL, NULL
},
+ {
+ {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
+ gettext_noop("A value of 0 turns off the timeout."),
+ GUC_UNIT_MS
+ },
+ &LockTimeout,
+ 0, 0, INT_MAX,
+ NULL, NULL, NULL
+ },
+
{
{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL, NULL, NULL
},
+ {
+ {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
+ NULL
+ },
+ &vacuum_multixact_freeze_min_age,
+ 5000000, 0, 1000000000,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
+ NULL
+ },
+ &vacuum_multixact_freeze_table_age,
+ 150000000, 0, 2000000000,
+ NULL, NULL, NULL
+ },
+
{
{"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_MASTER,
gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
NULL, NULL, NULL
},
+ {
+ {"xloginsert_locks", PGC_POSTMASTER, WAL_SETTINGS,
+ gettext_noop("Sets the number of locks used for concurrent xlog insertions."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &num_xloginsert_locks,
+ 8, 1, 1000,
+ NULL, NULL, NULL
+ },
+
{
/* see max_connections */
{"max_wal_senders", PGC_POSTMASTER, REPLICATION_SENDING,
},
{
- {"replication_timeout", PGC_SIGHUP, REPLICATION_SENDING,
+ /* see max_connections */
+ {"max_replication_slots", PGC_POSTMASTER, REPLICATION_SENDING,
+ gettext_noop("Sets the maximum number of simultaneously defined replication slots."),
+ NULL
+ },
+ &max_replication_slots,
+ 0, 0, MAX_BACKENDS /* XXX? */ ,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"wal_sender_timeout", PGC_SIGHUP, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
},
- &replication_timeout,
+ &wal_sender_timeout,
60 * 1000, 0, INT_MAX,
NULL, NULL, NULL
},
{
- {"commit_delay", PGC_USERSET, WAL_SETTINGS,
+ {"commit_delay", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the delay in microseconds between transaction commit and "
"flushing WAL to disk."),
NULL
+ /* we have no microseconds designation, so can't supply units here */
},
&CommitDelay,
0, 0, 100000,
check_effective_io_concurrency, assign_effective_io_concurrency, NULL
},
+ {
+ {"max_worker_processes",
+ PGC_POSTMASTER,
+ RESOURCES_ASYNCHRONOUS,
+ gettext_noop("Maximum number of concurrent worker processes."),
+ NULL,
+ },
+ &max_worker_processes,
+ 8, 1, MAX_BACKENDS,
+ check_max_worker_processes, NULL, NULL
+ },
+
{
{"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Automatic log file rotation will occur after N minutes."),
GUC_UNIT_MIN
},
&Log_RotationAge,
- HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / MINS_PER_HOUR,
+ HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / SECS_PER_MINUTE,
NULL, NULL, NULL
},
},
{
{"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
- gettext_noop("Minimum number of tuple inserts, updates or deletes prior to analyze."),
+ gettext_noop("Minimum number of tuple inserts, updates, or deletes prior to analyze."),
NULL
},
&autovacuum_anl_thresh,
200000000, 100000000, 2000000000,
NULL, NULL, NULL
},
+ {
+ /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
+ {"autovacuum_multixact_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Multixact age at which to autovacuum a table to prevent multixact wraparound."),
+ NULL
+ },
+ &autovacuum_multixact_freeze_max_age,
+ 400000000, 10000000, 2000000000,
+ NULL, NULL, NULL
+ },
{
/* see max_connections */
{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
},
&autovacuum_max_workers,
3, 1, MAX_BACKENDS,
- check_autovacuum_max_workers, assign_autovacuum_max_workers, NULL
+ check_autovacuum_max_workers, NULL, NULL
+ },
+
+ {
+ {"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
+ gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
+ NULL,
+ GUC_UNIT_KB
+ },
+ &autovacuum_work_mem,
+ -1, -1, MAX_KILOBYTES,
+ check_autovacuum_work_mem, NULL, NULL
},
{
{
{"track_activity_query_size", PGC_POSTMASTER, RESOURCES_MEM,
- gettext_noop("Sets the size reserved for pg_stat_activity.current_query, in bytes."),
+ gettext_noop("Sets the size reserved for pg_stat_activity.query, in bytes."),
NULL,
+
+ /*
+ * There is no _bytes_ unit, so the user can't supply units for
+ * this.
+ */
},
&pgstat_track_activity_query_size,
1024, 100, 102400,
},
{
{"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM,
- gettext_noop("Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples."),
+ gettext_noop("Number of tuple inserts, updates, or deletes prior to analyze as a fraction of reltuples."),
NULL
},
&autovacuum_anl_scale,
NULL, NULL, NULL
},
- {
- {"krb_srvname", PGC_SIGHUP, CONN_AUTH_SECURITY,
- gettext_noop("Sets the name of the Kerberos service."),
- NULL
- },
- &pg_krb_srvnam,
- PG_KRB_SRVNAM,
- NULL, NULL, NULL
- },
-
{
{"bonjour_name", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
gettext_noop("Sets the Bonjour service name."),
},
{
- {"shared_preload_libraries", PGC_POSTMASTER, RESOURCES_KERNEL,
+ {"session_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
+ gettext_noop("Lists shared libraries to preload into each backend."),
+ NULL,
+ GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
+ },
+ &session_preload_libraries_string,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"shared_preload_libraries", PGC_POSTMASTER, CLIENT_CONN_PRELOAD,
gettext_noop("Lists shared libraries to preload into server."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
},
{
- {"local_preload_libraries", PGC_BACKEND, CLIENT_CONN_OTHER,
- gettext_noop("Lists shared libraries to preload into each backend."),
+ {"local_preload_libraries", PGC_BACKEND, CLIENT_CONN_PRELOAD,
+ gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
},
"depending on the platform."),
GUC_LIST_INPUT
},
- &log_destination_string,
+ &Log_destination_string,
"stderr",
check_log_destination, assign_log_destination, NULL
},
NULL, assign_syslog_ident, NULL
},
+ {
+ {"event_source", PGC_POSTMASTER, LOGGING_WHERE,
+ gettext_noop("Sets the application name used to identify "
+ "PostgreSQL messages in the event log."),
+ NULL
+ },
+ &event_source,
+ DEFAULT_EVENT_SOURCE,
+ NULL, NULL, NULL
+ },
+
{
{"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
},
{
- {"unix_socket_directory", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
- gettext_noop("Sets the directory where the Unix-domain socket will be created."),
+ {"unix_socket_directories", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
+ gettext_noop("Sets the directories where Unix-domain sockets will be created."),
NULL,
GUC_SUPERUSER_ONLY
},
- &UnixSocketDir,
+ &Unix_socket_directories,
+#ifdef HAVE_UNIX_SOCKETS
+ DEFAULT_PGSOCKET_DIR,
+#else
"",
- check_canonical_path, NULL, NULL
+#endif
+ NULL, NULL, NULL
},
{
},
{
+ /*
+ * Can't be set by ALTER SYSTEM as it can lead to recursive definition
+ * of data_directory.
+ */
{"data_directory", PGC_POSTMASTER, FILE_LOCATIONS,
gettext_noop("Sets the server's data directory."),
NULL,
- GUC_SUPERUSER_ONLY
+ GUC_SUPERUSER_ONLY | GUC_DISALLOW_IN_AUTO_FILE
},
&data_directory,
NULL,
check_canonical_path, NULL, NULL
},
+ {
+ {"ssl_cert_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Location of the SSL server certificate file."),
+ NULL
+ },
+ &ssl_cert_file,
+ "server.crt",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"ssl_key_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Location of the SSL server private key file."),
+ NULL
+ },
+ &ssl_key_file,
+ "server.key",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"ssl_ca_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Location of the SSL certificate authority file."),
+ NULL
+ },
+ &ssl_ca_file,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"ssl_crl_file", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Location of the SSL certificate revocation list file."),
+ NULL
+ },
+ &ssl_crl_file,
+ "",
+ NULL, NULL, NULL
+ },
+
{
{"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR,
gettext_noop("Writes temporary statistics files to the specified directory."),
GUC_SUPERUSER_ONLY
},
&pgstat_temp_directory,
- "pg_stat_tmp",
+ PG_STAT_TMP_DIR,
check_canonical_path, assign_pgstat_temp_directory, NULL
},
},
&SSLCipherSuites,
#ifdef USE_SSL
- "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH",
+ "HIGH:MEDIUM:+3DES:!aNULL",
+#else
+ "none",
+#endif
+ NULL, NULL, NULL
+ },
+
+ {
+ {"ssl_ecdh_curve", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Sets the curve to use for ECDH."),
+ NULL,
+ GUC_SUPERUSER_ONLY
+ },
+ &SSLECDHCurve,
+#ifdef USE_SSL
+ "prime256v1",
#else
"none",
#endif
check_application_name, assign_application_name, NULL
},
+ {
+ {"cluster_name", PGC_POSTMASTER, LOGGING_WHAT,
+ gettext_noop("Sets the name of the cluster which is included in the process title."),
+ NULL,
+ GUC_IS_NAME
+ },
+ &cluster_name,
+ "",
+ check_cluster_name, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
},
&synchronous_commit,
SYNCHRONOUS_COMMIT_ON, synchronous_commit_options,
- NULL, NULL, NULL
+ NULL, assign_synchronous_commit, NULL
},
{
NULL, NULL, NULL
},
+ {
+ {"dynamic_shared_memory_type", PGC_POSTMASTER, RESOURCES_MEM,
+ gettext_noop("Selects the dynamic shared memory implementation used."),
+ NULL
+ },
+ &dynamic_shared_memory_type,
+ DEFAULT_DYNAMIC_SHARED_MEMORY_TYPE, dynamic_shared_memory_options,
+ NULL, NULL, NULL
+ },
+
{
{"wal_sync_method", PGC_SIGHUP, WAL_SETTINGS,
gettext_noop("Selects the method used for forcing WAL updates to disk."),
NULL, NULL, NULL
},
+ {
+ {"huge_pages", PGC_POSTMASTER, RESOURCES_MEM,
+ gettext_noop("Use of huge pages on Linux"),
+ NULL
+ },
+ &huge_pages,
+ HUGE_PAGES_TRY, huge_pages_options,
+ NULL, NULL, NULL
+ },
/* End-of-list marker */
{
static void push_old_value(struct config_generic * gconf, GucAction action);
static void ReportGUCOption(struct config_generic * record);
static void reapply_stacked_values(struct config_generic * variable,
- struct config_string *pHolder,
+ struct config_string * pHolder,
GucStack *stack,
const char *curvalue,
GucContext curscontext, GucSource cursource);
static char *_ShowOption(struct config_generic * record, bool use_units);
static bool validate_option_array_item(const char *name, const char *value,
bool skipIfNoPermissions);
+static void write_auto_conf_file(int fd, const char *filename, ConfigVariable **head_p);
+static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
+ char *config_file, char *name, char *value);
/*
{
void *data;
+ /* Avoid unportable behavior of malloc(0) */
+ if (size == 0)
+ size = 1;
data = malloc(size);
if (data == NULL)
ereport(elevel,
{
void *data;
+ /* Avoid unportable behavior of realloc(NULL, 0) */
+ if (old == NULL && size == 0)
+ size = 1;
data = realloc(old, size);
if (data == NULL)
ereport(elevel,
/*
- * Build the sorted array. This is split out so that it could be
+ * Build the sorted array. This is split out so that it could be
* re-executed after startup (eg, we could allow loadable modules to
* add vars, and then we'd need to re-sort).
*/
/*
* The char* is allocated at the end of the struct since we have no
- * 'static' place to point to. Note that the current value, as well as
+ * 'static' place to point to. Note that the current value, as well as
* the boot and reset values, start out NULL.
*/
var->variable = (char **) (var + 1);
return *res;
/*
- * See if the name is an obsolete name for a variable. We assume that the
+ * See if the name is an obsolete name for a variable. We assume that the
* set of supported old names is short enough that a brute-force search is
* the best way.
*/
static int
guc_var_compare(const void *a, const void *b)
{
- struct config_generic *confa = *(struct config_generic **) a;
- struct config_generic *confb = *(struct config_generic **) b;
+ const struct config_generic *confa = *(struct config_generic * const *) a;
+ const struct config_generic *confb = *(struct config_generic * const *) b;
return guc_name_compare(confa->name, confb->name);
}
{
write_stderr("%s cannot access the server configuration file \"%s\": %s\n",
progname, ConfigFileName, strerror(errno));
+ free(configdir);
return false;
}
*/
SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);
+ /*
+ * Now read the config file a second time, allowing any settings in
+ * the PG_AUTOCONF_FILENAME file to take effect. (This is pretty ugly,
+ * but since we have to determine the DataDir before we can find the
+ * autoconf file, the alternatives seem worse.)
+ */
+ ProcessConfigFile(PGC_POSTMASTER);
+
/*
* If timezone_abbreviations wasn't set in the configuration file, install
- * the default value. We do it this way because we can't safely install
- * a "real" value until my_exec_path is set, which may not have happened
+ * the default value. We do it this way because we can't safely install a
+ * "real" value until my_exec_path is set, which may not have happened
* when InitializeGUCOptions runs, so the bootstrap default value cannot
* be the real desired default.
*/
/*
* Enter a new nesting level for GUC values. This is called at subtransaction
- * start and when entering a function that has proconfig settings. NOTE that
- * we must not risk error here, else subtransaction start will be unhappy.
+ * start, and when entering a function that has proconfig settings, and in
+ * some other places where we want to set GUC variables transiently.
+ * NOTE we must not risk error here, else subtransaction start will be unhappy.
*/
int
NewGUCNestLevel(void)
/*
* Do GUC processing at transaction or subtransaction commit or abort, or
- * when exiting a function that has proconfig settings. (The name is thus
- * a bit of a misnomer; perhaps it should be ExitGUCNestLevel or some such.)
+ * when exiting a function that has proconfig settings, or when undoing a
+ * transient assignment to some GUC variables. (The name is thus a bit of
+ * a misnomer; perhaps it should be ExitGUCNestLevel or some such.)
* During abort, we discard all GUC settings that were applied at nesting
* levels >= nestLevel. nestLevel == 1 corresponds to the main transaction.
*/
GucStack *stack;
/*
- * Process and pop each stack entry within the nest level. To
- * simplify fmgr_security_definer(), we allow failure exit from a
- * function-with-SET-options to be recovered at the surrounding
- * transaction or subtransaction abort; so there could be more than
- * one stack entry to pop.
+ * Process and pop each stack entry within the nest level. To simplify
+ * fmgr_security_definer() and other places that use GUC_ACTION_SAVE,
+ * we allow failure exit from code that uses a local nest level to be
+ * recovered at the surrounding transaction or subtransaction abort;
+ * so there could be more than one stack entry to pop.
*/
while ((stack = gconf->stack) != NULL &&
stack->nest_level >= nestLevel)
{
/* Set hint for use if no match or trailing garbage */
if (hintmsg)
- *hintmsg = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", and \"GB\".");
+ *hintmsg = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", \"GB\", and \"TB\".");
#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
#error BLCKSZ must be between 1KB and 1MB
break;
}
}
+ else if (strncmp(endptr, "TB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GUC_UNIT_MEMORY)
+ {
+ case GUC_UNIT_KB:
+ val *= KB_PER_TB;
+ break;
+ case GUC_UNIT_BLOCKS:
+ val *= KB_PER_TB / (BLCKSZ / 1024);
+ break;
+ case GUC_UNIT_XBLOCKS:
+ val *= KB_PER_TB / (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
}
else if (flags & GUC_UNIT_TIME)
{
return retstr.data;
}
-
/*
- * Sets option `name' to given value.
- *
- * The value should be a string, which will be parsed and converted to
- * the appropriate data type. The context and source parameters indicate
- * in which context this function is being called, so that it can apply the
- * access restrictions properly.
- *
- * If value is NULL, set the option to its default value (normally the
- * reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val).
- *
- * action indicates whether to set the value globally in the session, locally
- * to the current top transaction, or just for the duration of a function call.
- *
- * If changeVal is false then don't really set the option but do all
- * the checks to see if it would work.
- *
- * elevel should normally be passed as zero, allowing this function to make
- * its standard choice of ereport level. However some callers need to be
- * able to override that choice; they should pass the ereport level to use.
+ * Validates configuration parameter and value, by calling check hook functions
+ * depending on record's vartype. It validates if the parameter
+ * value given is in range of expected predefined value for that parameter.
*
+ * freemem - true indicates memory for newval and newextra will be
+ * freed in this function, false indicates it will be freed
+ * by caller.
* Return value:
- * +1: the value is valid and was successfully applied.
- * 0: the name or value is invalid (but see below).
- * -1: the value was not applied because of context, priority, or changeVal.
- *
- * If there is an error (non-existing option, invalid value) then an
- * ereport(ERROR) is thrown *unless* this is called for a source for which
- * we don't want an ERROR (currently, those are defaults, the config file,
+ * 1: the value is valid
+ * 0: the name or value is invalid
+ */
+static bool
+validate_conf_option(struct config_generic * record, const char *name,
+ const char *value, GucSource source, int elevel,
+ bool freemem, void *newval, void **newextra)
+{
+ /*
+ * Validate the value for the passed record, to ensure it is in expected
+ * range.
+ */
+ switch (record->vartype)
+ {
+
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+ bool tmpnewval;
+
+ if (newval == NULL)
+ newval = &tmpnewval;
+
+ if (value != NULL)
+ {
+ if (!parse_bool(value, newval))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a Boolean value",
+ name)));
+ return 0;
+ }
+
+ if (!call_bool_check_hook(conf, newval, newextra,
+ source, elevel))
+ return 0;
+
+ if (*newextra && freemem)
+ free(*newextra);
+ }
+ }
+ break;
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) record;
+ int tmpnewval;
+
+ if (newval == NULL)
+ newval = &tmpnewval;
+
+ if (value != NULL)
+ {
+ const char *hintmsg;
+
+ if (!parse_int(value, newval, conf->gen.flags, &hintmsg))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value),
+ hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ return 0;
+ }
+
+ if (*((int *) newval) < conf->min || *((int *) newval) > conf->max)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
+ *((int *) newval), name, conf->min, conf->max)));
+ return 0;
+ }
+
+ if (!call_int_check_hook(conf, newval, newextra,
+ source, elevel))
+ return 0;
+
+ if (*newextra && freemem)
+ free(*newextra);
+ }
+ }
+ break;
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) record;
+ double tmpnewval;
+
+ if (newval == NULL)
+ newval = &tmpnewval;
+
+ if (value != NULL)
+ {
+ if (!parse_real(value, newval))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a numeric value",
+ name)));
+ return 0;
+ }
+
+ if (*((double *) newval) < conf->min || *((double *) newval) > conf->max)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
+ *((double *) newval), name, conf->min, conf->max)));
+ return 0;
+ }
+
+ if (!call_real_check_hook(conf, newval, newextra,
+ source, elevel))
+ return 0;
+
+ if (*newextra && freemem)
+ free(*newextra);
+ }
+ }
+ break;
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+ char *tempPtr;
+ char **tmpnewval = newval;
+
+ if (newval == NULL)
+ tmpnewval = &tempPtr;
+
+ if (value != NULL)
+ {
+ /*
+ * The value passed by the caller could be transient, so
+ * we always strdup it.
+ */
+ *tmpnewval = guc_strdup(elevel, value);
+ if (*tmpnewval == NULL)
+ return 0;
+
+ /*
+ * The only built-in "parsing" check we have is to apply
+ * truncation if GUC_IS_NAME.
+ */
+ if (conf->gen.flags & GUC_IS_NAME)
+ truncate_identifier(*tmpnewval, strlen(*tmpnewval), true);
+
+ if (!call_string_check_hook(conf, tmpnewval, newextra,
+ source, elevel))
+ {
+ free(*tmpnewval);
+ return 0;
+ }
+
+ /* Free the malloc'd data if any */
+ if (freemem)
+ {
+ if (*tmpnewval != NULL)
+ free(*tmpnewval);
+ if (*newextra != NULL)
+ free(*newextra);
+ }
+ }
+ }
+ break;
+ case PGC_ENUM:
+ {
+ struct config_enum *conf = (struct config_enum *) record;
+ int tmpnewval;
+
+ if (newval == NULL)
+ newval = &tmpnewval;
+
+ if (value != NULL)
+ {
+ if (!config_enum_lookup_by_name(conf, value, newval))
+ {
+ char *hintmsg;
+
+ hintmsg = config_enum_get_options(conf,
+ "Available values: ",
+ ".", ", ");
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value),
+ hintmsg ? errhint("%s", _(hintmsg)) : 0));
+
+ if (hintmsg != NULL)
+ pfree(hintmsg);
+ return 0;
+ }
+ if (!call_enum_check_hook(conf, newval, newextra,
+ source, LOG))
+ return 0;
+
+ if (*newextra && freemem)
+ free(*newextra);
+ }
+ }
+ break;
+ }
+ return 1;
+}
+
+
+/*
+ * Sets option `name' to given value.
+ *
+ * The value should be a string, which will be parsed and converted to
+ * the appropriate data type. The context and source parameters indicate
+ * in which context this function is being called, so that it can apply the
+ * access restrictions properly.
+ *
+ * If value is NULL, set the option to its default value (normally the
+ * reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val).
+ *
+ * action indicates whether to set the value globally in the session, locally
+ * to the current top transaction, or just for the duration of a function call.
+ *
+ * If changeVal is false then don't really set the option but do all
+ * the checks to see if it would work.
+ *
+ * elevel should normally be passed as zero, allowing this function to make
+ * its standard choice of ereport level. However some callers need to be
+ * able to override that choice; they should pass the ereport level to use.
+ *
+ * Return value:
+ * +1: the value is valid and was successfully applied.
+ * 0: the name or value is invalid (but see below).
+ * -1: the value was not applied because of context, priority, or changeVal.
+ *
+ * If there is an error (non-existing option, invalid value) then an
+ * ereport(ERROR) is thrown *unless* this is called for a source for which
+ * we don't want an ERROR (currently, those are defaults, the config file,
* and per-database or per-user settings, as well as callers who specify
* a less-than-ERROR elevel). In those cases we write a suitable error
* message via ereport() and return 0.
*/
elevel = IsUnderPostmaster ? DEBUG3 : LOG;
}
- else if (source == PGC_S_DATABASE || source == PGC_S_USER ||
+ else if (source == PGC_S_GLOBAL || source == PGC_S_DATABASE || source == PGC_S_USER ||
source == PGC_S_DATABASE_USER)
elevel = WARNING;
else
* If a PGC_BACKEND parameter is changed in the config file,
* we want to accept the new value in the postmaster (whence
* it will propagate to subsequently-started backends), but
- * ignore it in existing backends. This is a tad klugy, but
+ * ignore it in existing backends. This is a tad klugy, but
* necessary because we don't re-read the config file during
* backend start.
+ *
+ * In EXEC_BACKEND builds, this works differently: we load all
+ * nondefault settings from the CONFIG_EXEC_PARAMS file during
+ * backend start. In that case we must accept PGC_SIGHUP
+ * settings, so as to have the same value as if we'd forked
+ * from the postmaster. We detect this situation by checking
+ * IsInitProcessingMode, which is a bit ugly, but it doesn't
+ * seem worth passing down an explicit flag saying we're doing
+ * read_nondefault_variables().
*/
+#ifdef EXEC_BACKEND
+ if (IsUnderPostmaster && !IsInitProcessingMode())
+ return -1;
+#else
if (IsUnderPostmaster)
return -1;
+#endif
}
else if (context != PGC_POSTMASTER && context != PGC_BACKEND &&
source != PGC_S_CLIENT)
* 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
+ * "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
if (value)
{
- if (!parse_bool(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a Boolean value",
- name)));
- return 0;
- }
- if (!call_bool_check_hook(conf, &newval, &newextra,
- source, elevel))
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
if (value)
{
- const char *hintmsg;
-
- if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for parameter \"%s\": \"%s\"",
- name, value),
- hintmsg ? errhint("%s", _(hintmsg)) : 0));
- return 0;
- }
- if (newval < conf->min || newval > conf->max)
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
- newval, name, conf->min, conf->max)));
- return 0;
- }
- if (!call_int_check_hook(conf, &newval, &newextra,
- source, elevel))
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
if (value)
{
- if (!parse_real(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a numeric value",
- name)));
- return 0;
- }
- if (newval < conf->min || newval > conf->max)
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
- newval, name, conf->min, conf->max)));
- return 0;
- }
- if (!call_real_check_hook(conf, &newval, &newextra,
- source, elevel))
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
if (value)
{
- /*
- * The value passed by the caller could be transient, so
- * we always strdup it.
- */
- newval = guc_strdup(elevel, value);
- if (newval == NULL)
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
return 0;
-
- /*
- * The only built-in "parsing" check we have is to apply
- * truncation if GUC_IS_NAME.
- */
- if (conf->gen.flags & GUC_IS_NAME)
- truncate_identifier(newval, strlen(newval), true);
-
- if (!call_string_check_hook(conf, &newval, &newextra,
- source, elevel))
- {
- free(newval);
- return 0;
- }
}
else if (source == PGC_S_DEFAULT)
{
if (value)
{
- if (!config_enum_lookup_by_name(conf, value, &newval))
- {
- 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));
-
- if (hintmsg)
- pfree(hintmsg);
- return 0;
- }
- if (!call_enum_check_hook(conf, &newval, &newextra,
- source, elevel))
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
A_Const *con;
if (l != list_head(args))
- appendStringInfo(&buf, ", ");
+ appendStringInfoString(&buf, ", ");
if (IsA(arg, TypeCast))
{
else
{
/*
- * Plain string literal or identifier. For quote mode,
+ * Plain string literal or identifier. For quote mode,
* quote it if it's not a vanilla identifier.
*/
if (flags & GUC_LIST_QUOTE)
return buf.data;
}
+/*
+ * Write updated configuration parameter values into a temporary file.
+ * This function traverses the list of parameters and quotes the string
+ * values before writing them.
+ */
+static void
+write_auto_conf_file(int fd, const char *filename, ConfigVariable **head_p)
+{
+ ConfigVariable *item;
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ appendStringInfoString(&buf, "# Do not edit this file manually!\n");
+ appendStringInfoString(&buf, "# It will be overwritten by ALTER SYSTEM command.\n");
+
+ /*
+ * write the file header message before contents, so that if there is no
+ * item it can contain message
+ */
+ if (write(fd, buf.data, buf.len) < 0)
+ ereport(ERROR,
+ (errmsg("failed to write to \"%s\" file", filename)));
+ resetStringInfo(&buf);
+
+ /*
+ * traverse the list of parameters, quote the string parameter and write
+ * it to file. Once all parameters are written fsync the file.
+ */
+
+ for (item = *head_p; item != NULL; item = item->next)
+ {
+ char *escaped;
+
+ appendStringInfoString(&buf, item->name);
+ appendStringInfoString(&buf, " = ");
+
+ appendStringInfoString(&buf, "\'");
+ escaped = escape_single_quotes_ascii(item->value);
+ appendStringInfoString(&buf, escaped);
+ free(escaped);
+ appendStringInfoString(&buf, "\'");
+
+ appendStringInfoString(&buf, "\n");
+
+ if (write(fd, buf.data, buf.len) < 0)
+ ereport(ERROR,
+ (errmsg("failed to write to \"%s\" file", filename)));
+ resetStringInfo(&buf);
+ }
+
+ if (pg_fsync(fd) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not fsync file \"%s\": %m", filename)));
+
+ pfree(buf.data);
+}
+
+
+/*
+ * This function takes list of all configuration parameters in
+ * PG_AUTOCONF_FILENAME and parameter to be updated as input arguments and
+ * replace the updated configuration parameter value in a list. If the
+ * parameter to be updated is new then it is appended to the list of
+ * parameters.
+ */
+static void
+replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
+ char *config_file,
+ char *name, char *value)
+{
+ ConfigVariable *item,
+ *prev = NULL;
+
+ if (*head_p != NULL)
+ {
+ for (item = *head_p; item != NULL; item = item->next)
+ {
+ if (strcmp(item->name, name) == 0)
+ {
+ pfree(item->value);
+ if (value != NULL)
+ /* update the parameter value */
+ item->value = pstrdup(value);
+ else
+ {
+ /* delete the configuration parameter from list */
+ if (*head_p == item)
+ *head_p = item->next;
+ else
+ prev->next = item->next;
+
+ if (*tail_p == item)
+ *tail_p = prev;
+
+ pfree(item->name);
+ pfree(item->filename);
+ pfree(item);
+ }
+ return;
+ }
+ prev = item;
+ }
+ }
+
+ if (value == NULL)
+ return;
+
+ item = palloc(sizeof *item);
+ item->name = pstrdup(name);
+ item->value = pstrdup(value);
+ item->filename = pstrdup(config_file);
+ item->next = NULL;
+
+ if (*head_p == NULL)
+ {
+ item->sourceline = 1;
+ *head_p = item;
+ }
+ else
+ {
+ item->sourceline = (*tail_p)->sourceline + 1;
+ (*tail_p)->next = item;
+ }
+
+ *tail_p = item;
+
+ return;
+}
+
+
+/*
+ * Persist the configuration parameter value.
+ *
+ * This function takes all previous configuration parameters
+ * set by ALTER SYSTEM command and the currently set ones
+ * and write them all to the automatic configuration file.
+ *
+ * The configuration parameters are written to a temporary
+ * file then renamed to the final name.
+ *
+ * An LWLock is used to serialize writing to the same file.
+ *
+ * In case of an error, we leave the original automatic
+ * configuration file (PG_AUTOCONF_FILENAME) intact.
+ */
+void
+AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
+{
+ char *name;
+ char *value;
+ int Tmpfd = -1;
+ FILE *infile;
+ struct config_generic *record;
+ ConfigVariable *head = NULL;
+ ConfigVariable *tail = NULL;
+ char AutoConfFileName[MAXPGPATH];
+ char AutoConfTmpFileName[MAXPGPATH];
+ struct stat st;
+ void *newextra = NULL;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser to execute ALTER SYSTEM command"))));
+
+ /*
+ * Validate the name and arguments [value1, value2 ... ].
+ */
+ name = altersysstmt->setstmt->name;
+
+ switch (altersysstmt->setstmt->kind)
+ {
+ case VAR_SET_VALUE:
+ value = ExtractSetVariableArgs(altersysstmt->setstmt);
+ break;
+
+ case VAR_SET_DEFAULT:
+ value = NULL;
+ break;
+ default:
+ elog(ERROR, "unrecognized alter system stmt type: %d",
+ altersysstmt->setstmt->kind);
+ break;
+ }
+
+ record = find_option(name, false, LOG);
+ if (record == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+
+ /*
+ * Don't allow the parameters which can't be set in configuration
+ * files to be set in PG_AUTOCONF_FILENAME file.
+ */
+ if ((record->context == PGC_INTERNAL) ||
+ (record->flags & GUC_DISALLOW_IN_FILE) ||
+ (record->flags & GUC_DISALLOW_IN_AUTO_FILE))
+ ereport(ERROR,
+ (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot be changed",
+ name)));
+
+ if (!validate_conf_option(record, name, value, PGC_S_FILE,
+ ERROR, true, NULL,
+ &newextra))
+ ereport(ERROR,
+ (errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
+
+
+ /*
+ * Use data directory as reference path for PG_AUTOCONF_FILENAME and its
+ * corresponding temporary file.
+ */
+ join_path_components(AutoConfFileName, data_directory, PG_AUTOCONF_FILENAME);
+ canonicalize_path(AutoConfFileName);
+ snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
+ AutoConfFileName,
+ "tmp");
+
+ /*
+ * One backend is allowed to operate on file PG_AUTOCONF_FILENAME, to
+ * ensure that we need to update the contents of the file with
+ * AutoFileLock. To ensure crash safety, first the contents are written to
+ * a temporary file which is then renameed to PG_AUTOCONF_FILENAME. In
+ * case there exists a temp file from previous crash, that can be reused.
+ */
+
+ LWLockAcquire(AutoFileLock, LW_EXCLUSIVE);
+
+ Tmpfd = open(AutoConfTmpFileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (Tmpfd < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("failed to open auto conf temp file \"%s\": %m ",
+ AutoConfTmpFileName)));
+
+ PG_TRY();
+ {
+ if (stat(AutoConfFileName, &st) == 0)
+ {
+ /* open file PG_AUTOCONF_FILENAME */
+ infile = AllocateFile(AutoConfFileName, "r");
+ if (infile == NULL)
+ ereport(ERROR,
+ (errmsg("failed to open auto conf file \"%s\": %m ",
+ AutoConfFileName)));
+
+ /* parse it */
+ ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail);
+
+ FreeFile(infile);
+ }
+
+ /*
+ * replace with new value if the configuration parameter already
+ * exists OR add it as a new cofiguration parameter in the file.
+ */
+ replace_auto_config_value(&head, &tail, AutoConfFileName, name, value);
+
+ /* Write and sync the new contents to the temporary file */
+ write_auto_conf_file(Tmpfd, AutoConfTmpFileName, &head);
+
+ close(Tmpfd);
+ Tmpfd = -1;
+
+ /*
+ * As the rename is atomic operation, if any problem occurs after this
+ * at max it can loose the parameters set by last ALTER SYSTEM
+ * command.
+ */
+ if (rename(AutoConfTmpFileName, AutoConfFileName) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not rename file \"%s\" to \"%s\" : %m",
+ AutoConfTmpFileName, AutoConfFileName)));
+ }
+ PG_CATCH();
+ {
+ if (Tmpfd >= 0)
+ close(Tmpfd);
+
+ unlink(AutoConfTmpFileName);
+ FreeConfigVariables(head);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ FreeConfigVariables(head);
+ LWLockRelease(AutoFileLock);
+ return;
+}
/*
* SET command
*/
void
-ExecSetVariableStmt(VariableSetStmt *stmt)
+ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
{
case VAR_SET_VALUE:
case VAR_SET_CURRENT:
+ if (stmt->is_local)
+ WarnNoTransactionChain(isTopLevel, "SET LOCAL");
(void) set_config_option(stmt->name,
ExtractSetVariableArgs(stmt),
(superuser() ? PGC_SUSET : PGC_USERSET),
case VAR_SET_MULTI:
/*
- * Special case for special SQL syntax that effectively sets more
- * than one variable per statement.
+ * Special-case SQL syntaxes. The TRANSACTION and SESSION
+ * CHARACTERISTICS cases effectively set more than one variable
+ * per statement. TRANSACTION SNAPSHOT only takes one argument,
+ * but we put it here anyway since it's a special case and not
+ * related to any GUC variable.
*/
if (strcmp(stmt->name, "TRANSACTION") == 0)
{
ListCell *head;
+ WarnNoTransactionChain(isTopLevel, "SET TRANSACTION");
+
foreach(head, stmt->args)
{
DefElem *item = (DefElem *) lfirst(head);
item->defname);
}
}
+ else if (strcmp(stmt->name, "TRANSACTION SNAPSHOT") == 0)
+ {
+ A_Const *con = (A_Const *) linitial(stmt->args);
+
+ if (stmt->is_local)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SET LOCAL TRANSACTION SNAPSHOT is not implemented")));
+
+ WarnNoTransactionChain(isTopLevel, "SET TRANSACTION");
+ Assert(IsA(con, A_Const));
+ Assert(nodeTag(&con->val) == T_String);
+ ImportSnapshot(strVal(&con->val));
+ }
else
elog(ERROR, "unexpected SET MULTI element: %s",
stmt->name);
break;
case VAR_SET_DEFAULT:
+ if (stmt->is_local)
+ WarnNoTransactionChain(isTopLevel, "SET LOCAL");
+ /* fall through */
case VAR_RESET:
+ if (strcmp(stmt->name, "transaction_isolation") == 0)
+ WarnNoTransactionChain(isTopLevel, "RESET TRANSACTION");
+
(void) set_config_option(stmt->name,
NULL,
(superuser() ? PGC_SUSET : PGC_USERSET),
*/
static void
reapply_stacked_values(struct config_generic * variable,
- struct config_string *pHolder,
+ struct config_string * pHolder,
GucStack *stack,
const char *curvalue,
GucContext curscontext, GucSource cursource)
case GUC_SET_LOCAL:
/* first, apply the masked value as SET */
(void) set_config_option(name, stack->masked.val.stringval,
- stack->masked_scontext, PGC_S_SESSION,
+ stack->masked_scontext, PGC_S_SESSION,
GUC_ACTION_SET, true, WARNING);
/* then apply the current value as LOCAL */
(void) set_config_option(name, curvalue,
break;
}
- if (result % KB_PER_GB == 0)
+ if (result % KB_PER_TB == 0)
+ {
+ result /= KB_PER_TB;
+ unit = "TB";
+ }
+ else if (result % KB_PER_GB == 0)
{
result /= KB_PER_GB;
unit = "GB";
GucSource varsource;
GucContext varscontext;
+ /*
+ * Assert that PGC_BACKEND case in set_config_option() will do the right
+ * thing.
+ */
+ Assert(IsInitProcessingMode());
+
/*
* Open file
*/
/*
* Handle options fetched from pg_db_role_setting.setconfig,
- * pg_proc.proconfig, etc. Caller must specify proper context/source/action.
+ * pg_proc.proconfig, etc. Caller must specify proper context/source/action.
*
* The array parameter must be an array of TEXT (it must not be NULL).
*/
name = record->name;
/* build new item for array */
- newval = palloc(strlen(name) + 1 + strlen(value) + 1);
- sprintf(newval, "%s=%s", name, value);
+ newval = psprintf("%s=%s", name, value);
datum = CStringGetTextDatum(newval);
if (array)
* Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
*
* name is the option name. value is the proposed value for the Add case,
- * or NULL for the Delete/Reset cases. If skipIfNoPermissions is true, it's
+ * or NULL for the Delete/Reset cases. If skipIfNoPermissions is true, it's
* not an error to have no permissions to set the option.
*
* Returns TRUE if OK, FALSE if skipIfNoPermissions is true and user does not
*
* name is not known, but exists or can be created as a placeholder (i.e.,
* it has a prefixed name). We allow this case if you're a superuser,
- * otherwise not. Superusers are assumed to know what they're doing.
- * We can't allow it for other users, because when the placeholder is
+ * otherwise not. Superusers are assumed to know what they're doing. We
+ * can't allow it for other users, because when the placeholder is
* resolved it might turn out to be a SUSET variable;
* define_custom_variable assumes we checked that.
*
* ERRCODE_INVALID_PARAMETER_VALUE SQLSTATE for check hook failures.
*
* Note that GUC_check_errmsg() etc are just macros that result in a direct
- * assignment to the associated variables. That is ugly, but forced by the
+ * assignment to the associated variables. That is ugly, but forced by the
* limitations of C's macro mechanisms.
*/
void
return true;
}
-static bool
-check_debug_assertions(bool *newval, void **extra, GucSource source)
-{
-#ifndef USE_ASSERT_CHECKING
- if (*newval)
- {
- GUC_check_errmsg("assertion checking is not supported by this build");
- return false;
- }
-#endif
- return true;
-}
-
static bool
check_bonjour(bool *newval, void **extra, GucSource source)
{
static bool
check_maxconnections(int *newval, void **extra, GucSource source)
{
- if (*newval + autovacuum_max_workers + 1 > MAX_BACKENDS)
+ if (*newval + autovacuum_max_workers + 1 +
+ max_worker_processes > MAX_BACKENDS)
return false;
return true;
}
-static void
-assign_maxconnections(int newval, void *extra)
+static bool
+check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
{
- MaxBackends = newval + autovacuum_max_workers + 1;
+ if (MaxConnections + *newval + 1 + max_worker_processes > MAX_BACKENDS)
+ return false;
+ return true;
}
static bool
-check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
+check_autovacuum_work_mem(int *newval, void **extra, GucSource source)
{
- if (MaxConnections + *newval + 1 > MAX_BACKENDS)
- return false;
+ /*
+ * -1 indicates fallback.
+ *
+ * If we haven't yet changed the boot_val default of -1, just let it be.
+ * Autovacuum will look to maintenance_work_mem instead.
+ */
+ if (*newval == -1)
+ return true;
+
+ /*
+ * We clamp manually-set values to at least 1MB. Since
+ * maintenance_work_mem is always set to at least this value, do the same
+ * here.
+ */
+ if (*newval < 1024)
+ *newval = 1024;
+
return true;
}
-static void
-assign_autovacuum_max_workers(int newval, void *extra)
+static bool
+check_max_worker_processes(int *newval, void **extra, GucSource source)
{
- MaxBackends = MaxConnections + newval + 1;
+ if (MaxConnections + autovacuum_max_workers + 1 + *newval > MAX_BACKENDS)
+ return false;
+ return true;
}
static bool
assign_pgstat_temp_directory(const char *newval, void *extra)
{
/* check_canonical_path already canonicalized newval for us */
+ char *dname;
char *tname;
char *fname;
- tname = guc_malloc(ERROR, strlen(newval) + 12); /* /pgstat.tmp */
- sprintf(tname, "%s/pgstat.tmp", newval);
- fname = guc_malloc(ERROR, strlen(newval) + 13); /* /pgstat.stat */
- sprintf(fname, "%s/pgstat.stat", newval);
+ /* directory */
+ dname = guc_malloc(ERROR, strlen(newval) + 1); /* runtime dir */
+ sprintf(dname, "%s", newval);
+
+ /* global stats */
+ tname = guc_malloc(ERROR, strlen(newval) + 12); /* /global.tmp */
+ sprintf(tname, "%s/global.tmp", newval);
+ fname = guc_malloc(ERROR, strlen(newval) + 13); /* /global.stat */
+ sprintf(fname, "%s/global.stat", newval);
+ if (pgstat_stat_directory)
+ free(pgstat_stat_directory);
+ pgstat_stat_directory = dname;
if (pgstat_stat_tmpname)
free(pgstat_stat_tmpname);
pgstat_stat_tmpname = tname;
pgstat_report_appname(newval);
}
+static bool
+check_cluster_name(char **newval, void **extra, GucSource source)
+{
+ /* Only allow clean ASCII chars in the cluster name */
+ char *p;
+
+ for (p = *newval; *p; p++)
+ {
+ if (*p < 32 || *p > 126)
+ *p = '?';
+ }
+
+ return true;
+}
+
static const char *
show_unix_socket_permissions(void)
{