* 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_custom_variable_classes(char **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 char *timezone_abbreviations_string;
static char *XactIsoLevel_string;
static char *session_authorization_string;
-static char *custom_variable_classes;
static int max_function_args;
static int max_index_keys;
static int max_identifier_length;
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
},
{
},
{
- {"custom_variable_classes", PGC_SIGHUP, CUSTOM_OPTIONS,
- gettext_noop("Sets the list of known custom variable classes."),
- NULL,
- GUC_LIST_INPUT | GUC_LIST_QUOTE
- },
- &custom_variable_classes,
- NULL,
- check_custom_variable_classes, 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 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 reapply_stacked_values(struct config_generic * variable,
+ struct config_string * pHolder,
+ GucStack *stack,
+ const char *curvalue,
+ GucContext curscontext, GucSource cursource);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest);
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).
*/
}
/*
- * Create and add a placeholder variable. It's presumed to belong
- * to a valid custom variable class at this point.
+ * Create and add a placeholder variable for a custom variable name.
*/
static struct config_generic *
add_placeholder_variable(const char *name, int elevel)
/*
* 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 gen;
}
-/*
- * Detect whether the portion of "name" before dotPos matches any custom
- * variable class name listed in custom_var_classes. The latter must be
- * formatted the way that assign_custom_variable_classes does it, ie,
- * no whitespace. NULL is valid for custom_var_classes.
- */
-static bool
-is_custom_class(const char *name, int dotPos, const char *custom_var_classes)
-{
- bool result = false;
- const char *ccs = custom_var_classes;
-
- if (ccs != NULL)
- {
- const char *start = ccs;
-
- for (;; ++ccs)
- {
- char c = *ccs;
-
- if (c == '\0' || c == ',')
- {
- if (dotPos == ccs - start && strncmp(start, name, dotPos) == 0)
- {
- result = true;
- break;
- }
- if (c == '\0')
- break;
- start = ccs + 1;
- }
- }
- }
- return result;
-}
-
/*
* Look up option NAME. If it exists, return a pointer to its record,
* else return NULL. If create_placeholders is TRUE, we'll create a
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.
*/
if (create_placeholders)
{
/*
- * Check if the name is qualified, and if so, check if the qualifier
- * matches any custom variable class. If so, add a placeholder.
+ * Check if the name is qualified, and if so, add a placeholder.
*/
- const char *dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
-
- if (dot != NULL &&
- is_custom_class(name, dot - name, custom_variable_classes))
+ if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL)
return add_placeholder_variable(name, elevel);
}
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);
}
InitializeOneGUCOption(struct config_generic * gconf)
{
gconf->status = 0;
- gconf->reset_source = PGC_S_DEFAULT;
gconf->source = PGC_S_DEFAULT;
+ gconf->reset_source = PGC_S_DEFAULT;
+ gconf->scontext = PGC_INTERNAL;
+ gconf->reset_scontext = PGC_INTERNAL;
gconf->stack = NULL;
gconf->extra = NULL;
gconf->sourcefile = NULL;
{
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.
*/
}
gconf->source = gconf->reset_source;
+ gconf->scontext = gconf->reset_scontext;
if (gconf->flags & GUC_REPORT)
ReportGUCOption(gconf);
if (stack->state == GUC_SET)
{
/* SET followed by SET LOCAL, remember SET's value */
+ stack->masked_scontext = gconf->scontext;
set_stack_value(gconf, &stack->masked);
stack->state = GUC_SET_LOCAL;
}
break;
}
stack->source = gconf->source;
+ stack->scontext = gconf->scontext;
set_stack_value(gconf, &stack->prior);
gconf->stack = stack;
/*
* 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)
if (prev->state == GUC_SET)
{
/* LOCAL migrates down */
+ prev->masked_scontext = stack->scontext;
prev->masked = stack->prior;
prev->state = GUC_SET_LOCAL;
}
/* prior state at this level no longer wanted */
discard_stack_value(gconf, &stack->prior);
/* copy down the masked state */
+ prev->masked_scontext = stack->masked_scontext;
if (prev->state == GUC_SET_LOCAL)
discard_stack_value(gconf, &prev->masked);
prev->masked = stack->masked;
/* Perform appropriate restoration of the stacked value */
config_var_value newvalue;
GucSource newsource;
+ GucContext newscontext;
if (restoreMasked)
{
newvalue = stack->masked;
newsource = PGC_S_SESSION;
+ newscontext = stack->masked_scontext;
}
else
{
newvalue = stack->prior;
newsource = stack->source;
+ newscontext = stack->scontext;
}
switch (gconf->vartype)
set_extra_field(gconf, &(stack->prior.extra), NULL);
set_extra_field(gconf, &(stack->masked.extra), NULL);
+ /* And restore source information */
gconf->source = newsource;
+ gconf->scontext = newscontext;
}
/* Finish popping the state stack */
{
/* 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 (flags & GUC_UNIT_TIME)
- {
- /* Set hint for use if no match or trailing garbage */
- if (hintmsg)
- *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\".");
-
- if (strncmp(endptr, "ms", 2) == 0)
+ else if (strncmp(endptr, "TB", 2) == 0)
{
endptr += 2;
- switch (flags & GUC_UNIT_TIME)
+ 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)
+ {
+ /* Set hint for use if no match or trailing garbage */
+ if (hintmsg)
+ *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\".");
+
+ if (strncmp(endptr, "ms", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GUC_UNIT_TIME)
{
case GUC_UNIT_S:
val /= MS_PER_S;
return retstr.data;
}
+/*
+ * 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
+ * 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 is going to be parsed and converted to the appropriate data
- * type. The context and source parameters indicate in which context this
- * function is being called so it can apply the access restrictions
- * properly.
+ * 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).
* 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 in a context where we
- * don't want to ereport (currently, startup or SIGHUP config file reread).
- * In that case we write a suitable error message via ereport(LOG) and
- * return false. This is working around the deficiencies in the ereport
- * mechanism, so don't blame me. In all other cases, the function
- * returns true, including cases where the input is valid but we chose
- * not to apply it because of context or source-priority considerations.
+ * 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.
*
* See also SetConfigOption for an external interface.
*/
-bool
+int
set_config_option(const char *name, const char *value,
GucContext context, GucSource source,
- GucAction action, bool changeVal)
+ GucAction action, bool changeVal, int elevel)
{
struct config_generic *record;
- int elevel;
bool prohibitValueChange = false;
bool makeDefault;
- if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
+ if (elevel == 0)
{
- /*
- * To avoid cluttering the log, only the postmaster bleats loudly
- * about problems with the config file.
- */
- elevel = IsUnderPostmaster ? DEBUG3 : LOG;
+ if (source == PGC_S_DEFAULT || source == PGC_S_FILE)
+ {
+ /*
+ * To avoid cluttering the log, only the postmaster bleats loudly
+ * about problems with the config file.
+ */
+ elevel = IsUnderPostmaster ? DEBUG3 : LOG;
+ }
+ else if (source == PGC_S_GLOBAL || source == PGC_S_DATABASE || source == PGC_S_USER ||
+ source == PGC_S_DATABASE_USER)
+ elevel = WARNING;
+ else
+ elevel = ERROR;
}
- else if (source == PGC_S_DATABASE || source == PGC_S_USER ||
- source == PGC_S_DATABASE_USER)
- elevel = WARNING;
- else
- elevel = ERROR;
record = find_option(name, true, elevel);
if (record == NULL)
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized configuration parameter \"%s\"", name)));
- return false;
+ return 0;
}
- /*
- * If source is postgresql.conf, mark the found record with
- * GUC_IS_IN_FILE. This is for the convenience of ProcessConfigFile. Note
- * that we do it even if changeVal is false, since ProcessConfigFile wants
- * the marking to occur during its testing pass.
- */
- if (source == PGC_S_FILE)
- record->status |= GUC_IS_IN_FILE;
-
/*
* Check if the option can be set at this time. See guc.h for the precise
* rules.
switch (record->context)
{
case PGC_INTERNAL:
- if (context == PGC_SIGHUP)
- {
- /*
- * Historically we've just silently ignored attempts to set
- * PGC_INTERNAL variables from the config file. Maybe it'd be
- * better to use the prohibitValueChange logic for this?
- */
- return true;
- }
- else if (context != PGC_INTERNAL)
+ if (context != PGC_INTERNAL)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed",
name)));
- return false;
+ return 0;
}
break;
case PGC_POSTMASTER:
* hooks, etc, we can't just compare the given string directly
* to what's stored. Set a flag to check below after we have
* the final storable value.
- *
- * During the "checking" pass we just do nothing, to avoid
- * printing the warning twice.
*/
- if (!changeVal)
- return true;
-
prohibitValueChange = true;
}
else if (context != PGC_POSTMASTER)
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
}
break;
case PGC_SIGHUP:
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed now",
name)));
- return false;
+ return 0;
}
/*
* 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 true;
+ return -1;
+#endif
}
else if (context != PGC_POSTMASTER && context != PGC_BACKEND &&
source != PGC_S_CLIENT)
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be set after connection start",
name)));
- return false;
+ return 0;
}
break;
case PGC_SUSET:
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
name)));
- return false;
+ return 0;
}
break;
case PGC_USERSET:
* 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
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot set parameter \"%s\" within security-definer function",
name)));
- return false;
+ return 0;
}
if (InSecurityRestrictedOperation())
{
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot set parameter \"%s\" within security-restricted operation",
name)));
- return false;
+ return 0;
}
}
{
elog(DEBUG3, "\"%s\": setting ignored because previous source is higher priority",
name);
- return true;
+ return -1;
}
changeVal = false;
}
if (value)
{
- if (!parse_bool(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a Boolean value",
- name)));
- return false;
- }
- if (!call_bool_check_hook(conf, &newval, &newextra,
- source, elevel))
- return false;
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
+ return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
if (!call_bool_check_hook(conf, &newval, &newextra,
source, elevel))
- return false;
+ return 0;
}
else
{
newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
+ context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
{
if (*conf->variable != newval)
+ {
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
+ }
+ return -1;
}
if (changeVal)
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
conf->gen.source = source;
+ conf->gen.scontext = context;
}
if (makeDefault)
{
set_extra_field(&conf->gen, &conf->reset_extra,
newextra);
conf->gen.reset_source = source;
+ conf->gen.reset_scontext = context;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
set_extra_field(&conf->gen, &stack->prior.extra,
newextra);
stack->source = source;
+ stack->scontext = context;
}
}
}
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 false;
- }
- 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 false;
- }
- if (!call_int_check_hook(conf, &newval, &newextra,
- source, elevel))
- return false;
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
+ return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
if (!call_int_check_hook(conf, &newval, &newextra,
source, elevel))
- return false;
+ return 0;
}
else
{
newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
+ context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
{
if (*conf->variable != newval)
+ {
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
+ }
+ return -1;
}
if (changeVal)
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
conf->gen.source = source;
+ conf->gen.scontext = context;
}
if (makeDefault)
{
set_extra_field(&conf->gen, &conf->reset_extra,
newextra);
conf->gen.reset_source = source;
+ conf->gen.reset_scontext = context;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
set_extra_field(&conf->gen, &stack->prior.extra,
newextra);
stack->source = source;
+ stack->scontext = context;
}
}
}
if (value)
{
- if (!parse_real(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a numeric value",
- name)));
- return false;
- }
- 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 false;
- }
- if (!call_real_check_hook(conf, &newval, &newextra,
- source, elevel))
- return false;
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
+ return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
if (!call_real_check_hook(conf, &newval, &newextra,
source, elevel))
- return false;
+ return 0;
}
else
{
newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
+ context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
{
if (*conf->variable != newval)
+ {
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
+ }
+ return -1;
}
if (changeVal)
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
conf->gen.source = source;
+ conf->gen.scontext = context;
}
if (makeDefault)
{
set_extra_field(&conf->gen, &conf->reset_extra,
newextra);
conf->gen.reset_source = source;
+ conf->gen.reset_scontext = context;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
set_extra_field(&conf->gen, &stack->prior.extra,
newextra);
stack->source = source;
+ stack->scontext = context;
}
}
}
if (value)
{
- /*
- * The value passed by the caller could be transient, so
- * we always strdup it.
- */
- newval = guc_strdup(elevel, value);
- if (newval == NULL)
- return false;
-
- /*
- * 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 false;
- }
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
+ return 0;
}
else if (source == PGC_S_DEFAULT)
{
{
newval = guc_strdup(elevel, conf->boot_val);
if (newval == NULL)
- return false;
+ return 0;
}
else
newval = NULL;
source, elevel))
{
free(newval);
- return false;
+ return 0;
}
}
else
newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
+ context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
/* newval shouldn't be NULL, so we're a bit sloppy here */
if (*conf->variable == NULL || newval == NULL ||
strcmp(*conf->variable, newval) != 0)
+ {
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
+ }
+ return -1;
}
if (changeVal)
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
conf->gen.source = source;
+ conf->gen.scontext = context;
}
if (makeDefault)
set_extra_field(&conf->gen, &conf->reset_extra,
newextra);
conf->gen.reset_source = source;
+ conf->gen.reset_scontext = context;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
set_extra_field(&conf->gen, &stack->prior.extra,
newextra);
stack->source = source;
+ stack->scontext = context;
}
}
}
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 false;
- }
- if (!call_enum_check_hook(conf, &newval, &newextra,
- source, elevel))
- return false;
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
+ return 0;
}
else if (source == PGC_S_DEFAULT)
{
newval = conf->boot_val;
if (!call_enum_check_hook(conf, &newval, &newextra,
source, elevel))
- return false;
+ return 0;
}
else
{
newval = conf->reset_val;
newextra = conf->reset_extra;
source = conf->gen.reset_source;
+ context = conf->gen.reset_scontext;
}
if (prohibitValueChange)
{
if (*conf->variable != newval)
+ {
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
name)));
- return false;
+ return 0;
+ }
+ return -1;
}
if (changeVal)
set_extra_field(&conf->gen, &conf->gen.extra,
newextra);
conf->gen.source = source;
+ conf->gen.scontext = context;
}
if (makeDefault)
{
set_extra_field(&conf->gen, &conf->reset_extra,
newextra);
conf->gen.reset_source = source;
+ conf->gen.reset_scontext = context;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
set_extra_field(&conf->gen, &stack->prior.extra,
newextra);
stack->source = source;
+ stack->scontext = context;
}
}
}
if (changeVal && (record->flags & GUC_REPORT))
ReportGUCOption(record);
- return true;
+ return changeVal ? 1 : -1;
}
}
/*
- * Set a config option to the given value. See also set_config_option,
- * this is just the wrapper to be called from outside GUC. NB: this
- * is used only for non-transactional operations.
+ * Set a config option to the given value.
+ *
+ * See also set_config_option; this is just the wrapper to be called from
+ * outside GUC. (This function should be used when possible, because its API
+ * is more stable than set_config_option's.)
*
* Note: there is no support here for setting source file/line, as it
* is currently not needed.
GucContext context, GucSource source)
{
(void) set_config_option(name, value, context, source,
- GUC_ACTION_SET, true);
+ GUC_ACTION_SET, true, 0);
}
StringInfoData buf;
ListCell *l;
- /* Fast path if just DEFAULT */
- if (args == NIL)
- return NULL;
+ /* Fast path if just DEFAULT */
+ if (args == NIL)
+ return NULL;
+
+ /*
+ * Get flags for the variable; if it's not known, use default flags.
+ * (Caller might throw error later, but not our business to do so here.)
+ */
+ record = find_option(name, false, WARNING);
+ if (record)
+ flags = record->flags;
+ else
+ flags = 0;
+
+ /* Complain if list input and non-list variable */
+ if ((flags & GUC_LIST_INPUT) == 0 &&
+ list_length(args) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("SET %s takes only one argument", name)));
+
+ initStringInfo(&buf);
+
+ /*
+ * 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).
+ */
+ foreach(l, args)
+ {
+ Node *arg = (Node *) lfirst(l);
+ char *val;
+ TypeName *typeName = NULL;
+ A_Const *con;
+
+ if (l != list_head(args))
+ appendStringInfoString(&buf, ", ");
+
+ if (IsA(arg, TypeCast))
+ {
+ TypeCast *tc = (TypeCast *) arg;
+
+ arg = tc->arg;
+ typeName = tc->typeName;
+ }
+
+ if (!IsA(arg, A_Const))
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
+ con = (A_Const *) arg;
+
+ switch (nodeTag(&con->val))
+ {
+ case T_Integer:
+ appendStringInfo(&buf, "%ld", intVal(&con->val));
+ break;
+ case T_Float:
+ /* represented as a string, so just copy it */
+ appendStringInfoString(&buf, strVal(&con->val));
+ break;
+ case T_String:
+ val = strVal(&con->val);
+ if (typeName != NULL)
+ {
+ /*
+ * Must be a ConstInterval argument for TIME ZONE. Coerce
+ * to interval and back to normalize the value and account
+ * for any typmod.
+ */
+ Oid typoid;
+ int32 typmod;
+ Datum interval;
+ char *intervalout;
+
+ typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
+ Assert(typoid == INTERVALOID);
+
+ interval =
+ DirectFunctionCall3(interval_in,
+ CStringGetDatum(val),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(typmod));
+
+ intervalout =
+ DatumGetCString(DirectFunctionCall1(interval_out,
+ interval));
+ appendStringInfo(&buf, "INTERVAL '%s'", intervalout);
+ }
+ else
+ {
+ /*
+ * Plain string literal or identifier. For quote mode,
+ * quote it if it's not a vanilla identifier.
+ */
+ if (flags & GUC_LIST_QUOTE)
+ appendStringInfoString(&buf, quote_identifier(val));
+ else
+ appendStringInfoString(&buf, val);
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(&con->val));
+ break;
+ }
+ }
+
+ 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)));
/*
- * Get flags for the variable; if it's not known, use default flags.
- * (Caller might throw error later, but not our business to do so here.)
+ * Don't allow the parameters which can't be set in configuration
+ * files to be set in PG_AUTOCONF_FILENAME file.
*/
- record = find_option(name, false, WARNING);
- if (record)
- flags = record->flags;
- else
- flags = 0;
-
- /* Complain if list input and non-list variable */
- if ((flags & GUC_LIST_INPUT) == 0 &&
- list_length(args) != 1)
+ 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,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("SET %s takes only one argument", name)));
+ (errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
- initStringInfo(&buf);
/*
- * 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).
+ * Use data directory as reference path for PG_AUTOCONF_FILENAME and its
+ * corresponding temporary file.
*/
- foreach(l, args)
- {
- Node *arg = (Node *) lfirst(l);
- char *val;
- TypeName *typeName = NULL;
- A_Const *con;
+ join_path_components(AutoConfFileName, data_directory, PG_AUTOCONF_FILENAME);
+ canonicalize_path(AutoConfFileName);
+ snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
+ AutoConfFileName,
+ "tmp");
- if (l != list_head(args))
- appendStringInfo(&buf, ", ");
+ /*
+ * 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.
+ */
- if (IsA(arg, TypeCast))
+ 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)
{
- TypeCast *tc = (TypeCast *) arg;
+ /* open file PG_AUTOCONF_FILENAME */
+ infile = AllocateFile(AutoConfFileName, "r");
+ if (infile == NULL)
+ ereport(ERROR,
+ (errmsg("failed to open auto conf file \"%s\": %m ",
+ AutoConfFileName)));
- arg = tc->arg;
- typeName = tc->typeName;
+ /* parse it */
+ ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail);
+
+ FreeFile(infile);
}
- if (!IsA(arg, A_Const))
- elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
- con = (A_Const *) arg;
+ /*
+ * 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);
- switch (nodeTag(&con->val))
- {
- case T_Integer:
- appendStringInfo(&buf, "%ld", intVal(&con->val));
- break;
- case T_Float:
- /* represented as a string, so just copy it */
- appendStringInfoString(&buf, strVal(&con->val));
- break;
- case T_String:
- val = strVal(&con->val);
- if (typeName != NULL)
- {
- /*
- * Must be a ConstInterval argument for TIME ZONE. Coerce
- * to interval and back to normalize the value and account
- * for any typmod.
- */
- Oid typoid;
- int32 typmod;
- Datum interval;
- char *intervalout;
+ /* Write and sync the new contents to the temporary file */
+ write_auto_conf_file(Tmpfd, AutoConfTmpFileName, &head);
- typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);
- Assert(typoid == INTERVALOID);
+ close(Tmpfd);
+ Tmpfd = -1;
- interval =
- DirectFunctionCall3(interval_in,
- CStringGetDatum(val),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(typmod));
+ /*
+ * 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);
- intervalout =
- DatumGetCString(DirectFunctionCall1(interval_out,
- interval));
- appendStringInfo(&buf, "INTERVAL '%s'", intervalout);
- }
- else
- {
- /*
- * Plain string literal or identifier. For quote mode,
- * quote it if it's not a vanilla identifier.
- */
- if (flags & GUC_LIST_QUOTE)
- appendStringInfoString(&buf, quote_identifier(val));
- else
- appendStringInfoString(&buf, val);
- }
- break;
- default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(&con->val));
- break;
- }
+ unlink(AutoConfTmpFileName);
+ FreeConfigVariables(head);
+ PG_RE_THROW();
}
+ PG_END_TRY();
- return buf.data;
+ 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:
- set_config_option(stmt->name,
- ExtractSetVariableArgs(stmt),
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION,
- action,
- true);
+ if (stmt->is_local)
+ WarnNoTransactionChain(isTopLevel, "SET LOCAL");
+ (void) set_config_option(stmt->name,
+ ExtractSetVariableArgs(stmt),
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ action,
+ true,
+ 0);
break;
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:
- set_config_option(stmt->name,
- NULL,
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION,
- action,
- true);
+ if (strcmp(stmt->name, "transaction_isolation") == 0)
+ WarnNoTransactionChain(isTopLevel, "RESET TRANSACTION");
+
+ (void) set_config_option(stmt->name,
+ NULL,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ action,
+ true,
+ 0);
break;
case VAR_RESET_ALL:
ResetAllOptions();
char *argstring = flatten_set_variable_args(name, args);
/* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
- set_config_option(name,
- argstring,
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION,
- is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true);
+ (void) set_config_option(name,
+ argstring,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
+ true,
+ 0);
}
/*
is_local = PG_GETARG_BOOL(2);
/* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
- set_config_option(name,
- value,
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION,
- is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true);
+ (void) set_config_option(name,
+ value,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
+ true,
+ 0);
/* get the new current value */
new_value = GetConfigOptionByName(name, NULL);
{
const char *name = variable->name;
const char **nameAddr = &name;
- const char *value;
struct config_string *pHolder;
- 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.
+ * Assign the string value(s) stored in the placeholder to the real
+ * variable. Essentially, we need to duplicate all the active and stacked
+ * values, but with appropriate validation and datatype adjustment.
+ *
+ * If an assignment fails, we report a WARNING and keep going. We don't
+ * want to throw ERROR for bad values, because it'd bollix the add-on
+ * module that's presumably halfway through getting loaded. In such cases
+ * the default or previous state will become active instead.
*/
- switch (pHolder->gen.source)
- {
- case PGC_S_DEFAULT:
- case PGC_S_DYNAMIC_DEFAULT:
- case PGC_S_ENV_VAR:
- case PGC_S_FILE:
- case PGC_S_ARGV:
- /*
- * If we got past the check in init_custom_variable, we can safely
- * assume that any existing value for a PGC_POSTMASTER variable
- * was set in postmaster context.
- */
- if (variable->context == PGC_POSTMASTER)
- phcontext = PGC_POSTMASTER;
- else
- phcontext = PGC_SIGHUP;
- break;
+ /* First, apply the reset value if any */
+ if (pHolder->reset_val)
+ (void) set_config_option(name, pHolder->reset_val,
+ pHolder->gen.reset_scontext,
+ pHolder->gen.reset_source,
+ GUC_ACTION_SET, true, WARNING);
+ /* That should not have resulted in stacking anything */
+ Assert(variable->stack == NULL);
+
+ /* Now, apply current and stacked values, in the order they were stacked */
+ reapply_stacked_values(variable, pHolder, pHolder->gen.stack,
+ *(pHolder->variable),
+ pHolder->gen.scontext, pHolder->gen.source);
+
+ /* Also copy over any saved source-location information */
+ if (pHolder->gen.sourcefile)
+ set_config_sourcefile(name, pHolder->gen.sourcefile,
+ pHolder->gen.sourceline);
- case PGC_S_DATABASE:
- case PGC_S_USER:
- case PGC_S_DATABASE_USER:
+ /*
+ * Free up as much as we conveniently can of the placeholder structure.
+ * (This neglects any stack items, so it's possible for some memory to be
+ * leaked. Since this can only happen once per session per variable, it
+ * doesn't seem worth spending much code on.)
+ */
+ set_string_field(pHolder, pHolder->variable, NULL);
+ set_string_field(pHolder, &pHolder->reset_val, NULL);
- /*
- * The existing value came from an ALTER ROLE/DATABASE SET
- * command. We can assume that at the time the command was issued,
- * we checked that the issuing user was superuser if the variable
- * requires superuser privileges to set. So it's safe to use
- * SUSET context here.
- */
- phcontext = PGC_SUSET;
- break;
+ free(pHolder);
+}
- case PGC_S_CLIENT:
- case PGC_S_SESSION:
- default:
+/*
+ * Recursive subroutine for define_custom_variable: reapply non-reset values
+ *
+ * We recurse so that the values are applied in the same order as originally.
+ * At each recursion level, apply the upper-level value (passed in) in the
+ * fashion implied by the stack entry.
+ */
+static void
+reapply_stacked_values(struct config_generic * variable,
+ struct config_string * pHolder,
+ GucStack *stack,
+ const char *curvalue,
+ GucContext curscontext, GucSource cursource)
+{
+ const char *name = variable->name;
+ GucStack *oldvarstack = variable->stack;
- /*
- * We must assume that the value came from an untrusted user, even
- * if the current_user is a superuser.
- */
- phcontext = PGC_USERSET;
- break;
- }
+ if (stack != NULL)
+ {
+ /* First, recurse, so that stack items are processed bottom to top */
+ reapply_stacked_values(variable, pHolder, stack->prev,
+ stack->prior.val.stringval,
+ stack->scontext, stack->source);
- /*
- * Assign the string value stored in the placeholder to the real variable.
- *
- * XXX this is not really good enough --- it should be a nontransactional
- * assignment, since we don't want it to roll back if the current xact
- * fails later. (Or do we?)
- */
- value = *pHolder->variable;
+ /* See how to apply the passed-in value */
+ switch (stack->state)
+ {
+ case GUC_SAVE:
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_SAVE, true, WARNING);
+ break;
+
+ case GUC_SET:
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_SET, true, WARNING);
+ break;
- if (value)
+ case GUC_LOCAL:
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_LOCAL, true, WARNING);
+ break;
+
+ 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,
+ GUC_ACTION_SET, true, WARNING);
+ /* then apply the current value as LOCAL */
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_LOCAL, true, WARNING);
+ break;
+ }
+
+ /* If we successfully made a stack entry, adjust its nest level */
+ if (variable->stack != oldvarstack)
+ variable->stack->nest_level = stack->nest_level;
+ }
+ else
{
- if (set_config_option(name, value,
- phcontext, pHolder->gen.source,
- GUC_ACTION_SET, true))
+ /*
+ * We are at the end of the stack. If the active/previous value is
+ * different from the reset value, it must represent a previously
+ * committed session value. Apply it, and then drop the stack entry
+ * that set_config_option will have created under the impression that
+ * this is to be just a transactional assignment. (We leak the stack
+ * entry.)
+ */
+ if (curvalue != pHolder->reset_val ||
+ curscontext != pHolder->gen.reset_scontext ||
+ cursource != pHolder->gen.reset_source)
{
- /* Also copy over any saved source-location information */
- if (pHolder->gen.sourcefile)
- set_config_sourcefile(name, pHolder->gen.sourcefile,
- pHolder->gen.sourceline);
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_SET, true, WARNING);
+ variable->stack = NULL;
}
}
-
- /*
- * Free up as much as we conveniently can of the placeholder structure
- * (this neglects any stack items...)
- */
- set_string_field(pHolder, pHolder->variable, NULL);
- set_string_field(pHolder, &pHolder->reset_val, NULL);
-
- free(pHolder);
}
void
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";
*
* variable name, string, null terminated
* variable value, string, null terminated
+ * variable sourcefile, string, null terminated (empty if none)
+ * variable sourceline, integer
* variable source, integer
+ * variable scontext, integer
*/
static void
write_one_nondefault_variable(FILE *fp, struct config_generic * gconf)
{
struct config_real *conf = (struct config_real *) gconf;
- /* Could lose precision here? */
- fprintf(fp, "%f", *conf->variable);
+ fprintf(fp, "%.17g", *conf->variable);
}
break;
fputc(0, fp);
- fwrite(&gconf->source, sizeof(gconf->source), 1, fp);
+ if (gconf->sourcefile)
+ fprintf(fp, "%s", gconf->sourcefile);
+ fputc(0, fp);
+
+ fwrite(&gconf->sourceline, 1, sizeof(gconf->sourceline), fp);
+ fwrite(&gconf->source, 1, sizeof(gconf->source), fp);
+ fwrite(&gconf->scontext, 1, sizeof(gconf->scontext), fp);
}
void
{
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 != cvc_conf)
- write_one_nondefault_variable(fp, gconf);
+ write_one_nondefault_variable(fp, guc_variables[i]);
}
if (FreeFile(fp))
{
FILE *fp;
char *varname,
- *varvalue;
- int varsource;
+ *varvalue,
+ *varsourcefile;
+ int varsourceline;
+ GucSource varsource;
+ GucContext varscontext;
+
+ /*
+ * Assert that PGC_BACKEND case in set_config_option() will do the right
+ * thing.
+ */
+ Assert(IsInitProcessingMode());
/*
* Open file
break;
if ((record = find_option(varname, true, FATAL)) == NULL)
- elog(FATAL, "failed to locate variable %s in exec config params file", varname);
+ elog(FATAL, "failed to locate variable \"%s\" in exec config params file", varname);
+
if ((varvalue = read_string_with_null(fp)) == NULL)
elog(FATAL, "invalid format of exec config params file");
- if (fread(&varsource, sizeof(varsource), 1, fp) == 0)
+ if ((varsourcefile = read_string_with_null(fp)) == NULL)
+ elog(FATAL, "invalid format of exec config params file");
+ if (fread(&varsourceline, 1, sizeof(varsourceline), fp) != sizeof(varsourceline))
+ elog(FATAL, "invalid format of exec config params file");
+ if (fread(&varsource, 1, sizeof(varsource), fp) != sizeof(varsource))
+ elog(FATAL, "invalid format of exec config params file");
+ if (fread(&varscontext, 1, sizeof(varscontext), fp) != sizeof(varscontext))
elog(FATAL, "invalid format of exec config params file");
- (void) set_config_option(varname, varvalue, record->context,
- varsource, GUC_ACTION_SET, true);
+ (void) set_config_option(varname, varvalue,
+ varscontext, varsource,
+ GUC_ACTION_SET, true, 0);
+ if (varsourcefile[0])
+ set_config_sourcefile(varname, varsourcefile, varsourceline);
+
free(varname);
free(varvalue);
+ free(varsourcefile);
}
FreeFile(fp);
/*
* 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).
*/
continue;
}
- (void) set_config_option(name, value, context, source, action, true);
+ (void) set_config_option(name, value,
+ context, source,
+ action, true, 0);
free(name);
if (value)
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
* permissions normally (ie, allow if variable is USERSET, or if it's
* SUSET and user is superuser).
*
- * name is not known, but exists or can be created as a placeholder
- * (implying it has a prefix listed in custom_variable_classes). 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 resolved it might turn out to be a SUSET
- * variable; define_custom_variable assumes we checked that.
+ * 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
+ * resolved it might turn out to be a SUSET variable;
+ * define_custom_variable assumes we checked that.
*
* name is not known and can't be created as a placeholder. Throw error,
- * unless skipIfNoPermissions is true, in which case return FALSE. (It's
- * tempting to allow this case to superusers, if the name is qualified but
- * not listed in custom_variable_classes. That would ease restoring of
- * dumps containing ALTER ROLE/DATABASE SET. However, it's not clear that
- * this usage justifies such a loss of error checking. You can always fix
- * custom_variable_classes before you restore.)
+ * unless skipIfNoPermissions is true, in which case return FALSE.
*/
gconf = find_option(name, true, WARNING);
if (!gconf)
return false;
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
+ errmsg("unrecognized configuration parameter \"%s\"",
+ name)));
}
if (gconf->flags & GUC_CUSTOM_PLACEHOLDER)
/* if a permissions error should be thrown, let set_config_option do it */
/* test for permissions and valid option value */
- set_config_option(name, value,
- superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_TEST, GUC_ACTION_SET, false);
+ (void) set_config_option(name, value,
+ superuser() ? PGC_SUSET : PGC_USERSET,
+ PGC_S_TEST, GUC_ACTION_SET, false, 0);
return true;
}
* 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_custom_variable_classes(char **newval, void **extra, GucSource source)
-{
- /*
- * Check syntax. newval must be a comma separated list of identifiers.
- * Whitespace is allowed but removed from the result.
- */
- bool hasSpaceAfterToken = false;
- const char *cp = *newval;
- int symLen = 0;
- char c;
- StringInfoData buf;
-
- /* Default NULL is OK */
- if (cp == NULL)
- return true;
-
- initStringInfo(&buf);
- while ((c = *cp++) != '\0')
- {
- if (isspace((unsigned char) c))
- {
- if (symLen > 0)
- hasSpaceAfterToken = true;
- continue;
- }
-
- if (c == ',')
- {
- if (symLen > 0) /* terminate identifier */
- {
- appendStringInfoChar(&buf, ',');
- symLen = 0;
- }
- hasSpaceAfterToken = false;
- continue;
- }
-
- if (hasSpaceAfterToken || !(isalnum((unsigned char) c) || c == '_'))
- {
- /*
- * Syntax error due to token following space after token or
- * non-identifier character
- */
- pfree(buf.data);
- return false;
- }
- appendStringInfoChar(&buf, c);
- symLen++;
- }
-
- /* Remove stray ',' at end */
- if (symLen == 0 && buf.len > 0)
- buf.data[--buf.len] = '\0';
-
- /* GUC wants the result malloc'd */
- free(*newval);
- *newval = guc_strdup(LOG, buf.data);
-
- pfree(buf.data);
- 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)
{