* See src/backend/utils/misc/README for more information.
*
*
- * Copyright (c) 2000-2013, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2014, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
#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"
#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 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 char *temp_tablespaces;
extern bool ignore_checksum_failure;
extern bool synchronize_seqscans;
-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 bool check_max_worker_processes(int *newval, void **extra, GucSource source);
static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
+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.
{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;
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;
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 */
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."),
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 "
+ "(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
},
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
},
#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
{
{"wal_receiver_timeout", PGC_SIGHUP, REPLICATION_STANDBY,
- gettext_noop("Sets the maximum wait time to receive data from master."),
+ gettext_noop("Sets the maximum wait time to receive data from the primary."),
NULL,
GUC_UNIT_MS
},
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
},
#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
},
+ {
+ {"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,
NULL, NULL, NULL
},
+ {
+ /* 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."),
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."),
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,
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
+ },
+
{
{"tcp_keepalives_idle", PGC_USERSET, CLIENT_CONN_OTHER,
gettext_noop("Time between issuing TCP keepalives."),
{"track_activity_query_size", PGC_POSTMASTER, RESOURCES_MEM,
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,
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
},
&event_source,
- "PostgreSQL",
+ DEFAULT_EVENT_SOURCE,
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,
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
- "DEFAULT:!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
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 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);
/*
/*
- * 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.
*/
*/
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
/*
* Do GUC processing at transaction or subtransaction commit or abort, or
* 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
+ * 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.
{
/* 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,
- * 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.
+ * 1: the value is valid
+ * 0: the name or value is invalid
*/
-int
-set_config_option(const char *name, const char *value,
- GucContext context, GucSource source,
- GucAction action, bool changeVal, int elevel)
+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)
{
- struct config_generic *record;
- bool prohibitValueChange = false;
- bool makeDefault;
-
- if (elevel == 0)
- {
- 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;
- }
-
- record = find_option(name, true, elevel);
- if (record == NULL)
- {
- ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
- return 0;
- }
-
/*
- * Check if the option can be set at this time. See guc.h for the precise
- * rules.
+ * Validate the value for the passed record, to ensure it is in expected
+ * range.
*/
- switch (record->context)
+ switch (record->vartype)
{
- case PGC_INTERNAL:
- if (context != PGC_INTERNAL)
+
+ case PGC_BOOL:
{
- ereport(elevel,
- (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
- errmsg("parameter \"%s\" cannot be changed",
- name)));
- return 0;
+ 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_POSTMASTER:
- if (context == PGC_SIGHUP)
+ case PGC_INT:
{
- /*
- * We are re-reading a PGC_POSTMASTER variable from
- * postgresql.conf. We can't change the setting, so we should
- * give a warning if the DBA tries to change it. However,
+ 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.
+ *
+ * See also SetConfigOption for an external interface.
+ */
+int
+set_config_option(const char *name, const char *value,
+ GucContext context, GucSource source,
+ GucAction action, bool changeVal, int elevel)
+{
+ struct config_generic *record;
+ bool prohibitValueChange = false;
+ bool makeDefault;
+
+ if (elevel == 0)
+ {
+ 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;
+ }
+
+ record = find_option(name, true, elevel);
+ if (record == NULL)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ return 0;
+ }
+
+ /*
+ * 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_INTERNAL)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot be changed",
+ name)));
+ return 0;
+ }
+ break;
+ case PGC_POSTMASTER:
+ if (context == PGC_SIGHUP)
+ {
+ /*
+ * We are re-reading a PGC_POSTMASTER variable from
+ * postgresql.conf. We can't change the setting, so we should
+ * give a warning if the DBA tries to change it. However,
* because of variant formats, canonicalization by check
* hooks, etc, we can't just compare the given string directly
* to what's stored. Set a flag to check below after we have
* 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)
- 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);
+ if (!validate_conf_option(record, name, value, source,
+ elevel, false, &newval,
+ &newextra))
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),
{
ListCell *head;
+ WarnNoTransactionChain(isTopLevel, "SET TRANSACTION");
+
foreach(head, stmt->args)
{
DefElem *item = (DefElem *) lfirst(head);
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));
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),
* 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
+ * 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.
/*
* 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
+ * 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);
else
{
/*
- * We are at the end of the stack. If the active/previous value is
+ * 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
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
* 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 + GetNumShmemAttachedBgworkers() + autovacuum_max_workers + 1 >
- MAX_BACKENDS)
+ if (*newval + autovacuum_max_workers + 1 +
+ max_worker_processes > MAX_BACKENDS)
return false;
return true;
}
static bool
check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
{
- if (MaxConnections + *newval + 1 + GetNumShmemAttachedBgworkers() >
- MAX_BACKENDS)
+ if (MaxConnections + *newval + 1 + max_worker_processes > MAX_BACKENDS)
+ return false;
+ return true;
+}
+
+static bool
+check_autovacuum_work_mem(int *newval, void **extra, GucSource source)
+{
+ /*
+ * -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 bool
+check_max_worker_processes(int *newval, void **extra, GucSource source)
+{
+ if (MaxConnections + autovacuum_max_workers + 1 + *newval > MAX_BACKENDS)
return false;
return true;
}
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)
{