* See src/backend/utils/misc/README for more information.
*
*
- * Copyright (c) 2000-2006, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2007, PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.335 2006/08/11 20:15:16 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.426 2007/11/15 21:14:41 momjian Exp $
*
*--------------------------------------------------------------------
*/
#include <syslog.h>
#endif
-
#include "access/gin.h"
+#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
#include "catalog/namespace.h"
#include "commands/async.h"
+#include "commands/prepare.h"
#include "commands/vacuum.h"
#include "commands/variable.h"
+#include "commands/trigger.h"
#include "funcapi.h"
#include "libpq/auth.h"
#include "libpq/pqformat.h"
#include "parser/gramparse.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "parser/parse_type.h"
#include "parser/scansup.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
+#include "postmaster/walwriter.h"
#include "storage/fd.h"
#include "storage/freespace.h"
#include "tcop/tcopprot.h"
+#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
+#include "utils/plancache.h"
+#include "utils/portal.h"
#include "utils/ps_status.h"
#include "utils/tzparser.h"
+#include "utils/xml.h"
#ifndef PG_KRB_SRVTAB
#define PG_KRB_SRVTAB ""
#define KB_PER_GB (1024*1024)
#define MS_PER_S 1000
+#define S_PER_MIN 60
#define MS_PER_MIN (1000 * 60)
+#define MIN_PER_H 60
+#define S_PER_H (60 * 60)
#define MS_PER_H (1000 * 60 * 60)
+#define MIN_PER_D (60 * 24)
+#define S_PER_D (60 * 60 * 24)
#define MS_PER_D (1000 * 60 * 60 * 24)
/* XXX these should appear in other modules' header files */
extern bool Log_disconnections;
-extern bool check_function_bodies;
extern int CommitDelay;
extern int CommitSiblings;
extern char *default_tablespace;
+extern char *temp_tablespaces;
extern bool fullPageWrites;
#ifdef TRACE_SORT
extern bool trace_sort;
#endif
+#ifdef TRACE_SYNCSCAN
+extern bool trace_syncscan;
+#endif
+#ifdef DEBUG_BOUNDED_SORT
+extern bool optimize_bounded_sort;
+#endif
+
+#ifdef USE_SSL
+extern char *SSLCipherSuites;
+#endif
+
static const char *assign_log_destination(const char *value,
bool doit, GucSource source);
static const char *assign_defaultxactisolevel(const char *newval, bool doit,
GucSource source);
+static const char *assign_session_replication_role(const char *newval, bool doit,
+ GucSource source);
static const char *assign_log_min_messages(const char *newval, bool doit,
GucSource source);
static const char *assign_client_min_messages(const char *newval,
static const char *assign_canonical_path(const char *newval, bool doit, GucSource source);
static const char *assign_backslash_quote(const char *newval, bool doit, GucSource source);
static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source);
-
+static const char *assign_xmlbinary(const char *newval, bool doit, GucSource source);
+static const char *assign_xmloption(const char *newval, bool doit, GucSource source);
+static const char *show_archive_command(void);
static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source);
static const char *show_tcp_keepalives_idle(void);
static const char *show_tcp_keepalives_interval(void);
static const char *show_tcp_keepalives_count(void);
+static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source);
+static bool assign_maxconnections(int newval, bool doit, GucSource source);
/*
* GUC option variables that are exported from this module
* above together */
bool log_btree_build_stats = false;
+bool check_function_bodies = true;
+bool default_with_oids = false;
bool SQL_inheritance = true;
bool Password_encryption = true;
-bool default_with_oids = false;
-
-int log_min_error_statement = PANIC;
+int log_min_error_statement = ERROR;
int log_min_messages = NOTICE;
int client_min_messages = NOTICE;
int log_min_duration_statement = -1;
+int log_temp_files = -1;
int num_temp_buffers = 1000;
static char *log_statement_str;
static char *log_min_error_statement_str;
static char *log_destination_string;
+
#ifdef HAVE_SYSLOG
static char *syslog_facility_str;
static char *syslog_ident_str;
static char *client_encoding_string;
static char *datestyle_string;
static char *default_iso_level_string;
+static char *session_replication_role_string;
static char *locale_collate;
static char *locale_ctype;
static char *regex_flavor_string;
static char *server_encoding_string;
static char *server_version_string;
+static int server_version_num;
static char *timezone_string;
+static char *log_timezone_string;
static char *timezone_abbreviations_string;
static char *XactIsoLevel_string;
static char *data_directory;
static char *custom_variable_classes;
+static char *xmlbinary_string;
+static char *xmloption_string;
static int max_function_args;
static int max_index_keys;
static int max_identifier_length;
* 4. Add a record below.
*
* 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if
- * appropriate
- *
- * 6. Add it to src/bin/psql/tab-complete.c, if it's a USERSET option.
+ * appropriate.
*
- * 7. Don't forget to document the option.
+ * 6. Don't forget to document the option (at least in config.sgml).
*
- * 8. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure
+ * 7. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure
* it is not single quoted at dump time.
*/
&enableFsync,
true, NULL, NULL
},
+ {
+ {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ gettext_noop("Sets immediate fsync at commit."),
+ NULL
+ },
+ &XactSyncCommit,
+ true, NULL, NULL
+ },
{
{"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Continues processing past damaged page headers."),
&SilentMode,
false, NULL, NULL
},
+ {
+ {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
+ gettext_noop("Logs each checkpoint."),
+ NULL
+ },
+ &log_checkpoints,
+ false, NULL, NULL
+ },
{
{"log_connections", PGC_BACKEND, LOGGING_WHAT,
gettext_noop("Logs each successful connection."),
{
/* currently undocumented, so don't show in SHOW ALL */
{"exit_on_error", PGC_USERSET, UNGROUPED,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
},
#ifdef BTREE_BUILD_STATS
{
{"log_btree_build_stats", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
&Explain_pretty_print,
true, NULL, NULL
},
+
{
- {"stats_start_collector", PGC_POSTMASTER, STATS_COLLECTOR,
- gettext_noop("Starts the server statistics-collection subprocess."),
- NULL
+ {"track_activities", PGC_SUSET, STATS_COLLECTOR,
+ gettext_noop("Collects information about executing commands."),
+ gettext_noop("Enables the collection of information on the currently "
+ "executing command of each session, along with "
+ "the time at which that command began execution.")
},
- &pgstat_collect_startcollector,
+ &pgstat_track_activities,
true, NULL, NULL
},
{
- {"stats_reset_on_server_start", PGC_POSTMASTER, STATS_COLLECTOR,
- gettext_noop("Zeroes collected statistics on server restart."),
- NULL
- },
- &pgstat_collect_resetonpmstart,
- false, NULL, NULL
- },
- {
- {"stats_row_level", PGC_SUSET, STATS_COLLECTOR,
- gettext_noop("Collects row-level statistics on database activity."),
- NULL
- },
- &pgstat_collect_tuplelevel,
- false, NULL, NULL
- },
- {
- {"stats_block_level", PGC_SUSET, STATS_COLLECTOR,
- gettext_noop("Collects block-level statistics on database activity."),
+ {"track_counts", PGC_SUSET, STATS_COLLECTOR,
+ gettext_noop("Collects statistics on database activity."),
NULL
},
- &pgstat_collect_blocklevel,
- false, NULL, NULL
- },
-
- {
- {"stats_command_string", PGC_SUSET, STATS_COLLECTOR,
- gettext_noop("Collects information about executing commands."),
- gettext_noop("Enables the collection of information on the currently "
- "executing command of each session, along with the time "
- "at which that command began execution.")
- },
- &pgstat_collect_querystring,
+ &pgstat_track_counts,
true, NULL, NULL
},
NULL
},
&autovacuum_start_daemon,
- false, NULL, NULL
+ true, NULL, NULL
},
{
#ifdef LOCK_DEBUG
{
{"trace_locks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
#endif
+ {
+ {"log_lock_waits", PGC_SUSET, LOGGING_WHAT,
+ gettext_noop("Logs long lock waits."),
+ NULL
+ },
+ &log_lock_waits,
+ false, NULL, NULL
+ },
+
{
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
gettext_noop("Logs the host name in the connection logs."),
{"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
- "value means a NULL value; "
+ "value means a null value; "
"otherwise it is taken literally.")
},
&Array_nulls,
false, NULL, NULL
},
{
- {"redirect_stderr", PGC_POSTMASTER, LOGGING_WHERE,
- gettext_noop("Start a subprocess to capture stderr output into log files."),
+ {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
+ gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."),
NULL
},
- &Redirect_stderr,
+ &Logging_collector,
false, NULL, NULL
},
{
},
#endif
+#ifdef TRACE_SYNCSCAN
+ /* this is undocumented because not exposed in a standard build */
+ {
+ {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ gettext_noop("Generate debugging output for synchronized scanning."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &trace_syncscan,
+ false, NULL, NULL
+ },
+#endif
+
+#ifdef DEBUG_BOUNDED_SORT
+ /* this is undocumented because not exposed in a standard build */
+ {
+ {
+ "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ gettext_noop("Enable bounded sorting using heap sort."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &optimize_bounded_sort,
+ true, NULL, NULL
+ },
+#endif
+
#ifdef WAL_DEBUG
{
{"wal_debug", PGC_SUSET, DEVELOPER_OPTIONS,
{
{"krb_caseins_users", PGC_POSTMASTER, CONN_AUTH_SECURITY,
- gettext_noop("Sets whether Kerberos user names should be treated as case-insensitive."),
+ gettext_noop("Sets whether Kerberos and GSSAPI user names should be treated as case-insensitive."),
NULL
},
&pg_krb_caseins_users,
{
{"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
- gettext_noop("'...' strings treat backslashes literally."),
+ gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
},
false, NULL, NULL
},
+ {
+ {"archive_mode", PGC_POSTMASTER, WAL_SETTINGS,
+ gettext_noop("Allows archiving of WAL files using archive_command."),
+ NULL
+ },
+ &XLogArchiveMode,
+ false, NULL, NULL
+ },
+
{
{"allow_system_table_mods", PGC_POSTMASTER, DEVELOPER_OPTIONS,
- gettext_noop("Allows modifications of the structure of system tables."),
- NULL,
- GUC_NOT_IN_SAMPLE
+ gettext_noop("Allows modifications of the structure of system tables."),
+ NULL,
+ GUC_NOT_IN_SAMPLE
},
&allowSystemTableMods,
false, NULL, NULL
{
{"ignore_system_indexes", PGC_BACKEND, DEVELOPER_OPTIONS,
- gettext_noop("Disabled reading from system indexes."),
- gettext_noop("It does not prevent updating the indexes, so it is safe "
- "to use. The worst consequence is slowness."),
- GUC_NOT_IN_SAMPLE
+ gettext_noop("Disables reading from system indexes."),
+ gettext_noop("It does not prevent updating the indexes, so it is safe "
+ "to use. The worst consequence is slowness."),
+ GUC_NOT_IN_SAMPLE
},
&IgnoreSystemIndexes,
false, NULL, NULL
static struct config_int ConfigureNamesInt[] =
{
+ {
+ {"archive_timeout", PGC_SIGHUP, WAL_SETTINGS,
+ gettext_noop("Forces a switch to the next xlog file if a "
+ "new file has not been started within N seconds."),
+ NULL,
+ GUC_UNIT_S
+ },
+ &XLogArchiveTimeout,
+ 0, 0, INT_MAX, NULL, NULL
+ },
{
{"post_auth_delay", PGC_BACKEND, DEVELOPER_OPTIONS,
- gettext_noop("Waits N seconds on connection startup after authentication."),
- gettext_noop("This allows attaching a debugger to the process."),
- GUC_NOT_IN_SAMPLE | GUC_UNIT_S
+ gettext_noop("Waits N seconds on connection startup after authentication."),
+ gettext_noop("This allows attaching a debugger to the process."),
+ GUC_NOT_IN_SAMPLE | GUC_UNIT_S
},
&PostAuthDelay,
0, 0, INT_MAX, NULL, NULL
},
{
{"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
- gettext_noop("Sets the FROM-list size beyond which subqueries are not "
- "collapsed."),
+ gettext_noop("Sets the FROM-list size beyond which subqueries "
+ "are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
"queries if the resulting FROM list would have no more than "
"this many items.")
},
{
{"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
- gettext_noop("Sets the FROM-list size beyond which JOIN constructs are not "
- "flattened."),
+ gettext_noop("Sets the FROM-list size beyond which JOIN "
+ "constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
- "constructs into lists of FROM items whenever a list of no more "
- "than this many items would result.")
+ "constructs into lists of FROM items whenever a "
+ "list of no more than this many items would result.")
},
&join_collapse_limit,
8, 1, INT_MAX, NULL, NULL
{
{"deadlock_timeout", PGC_SIGHUP, LOCK_MANAGEMENT,
- gettext_noop("The time in milliseconds to wait on lock before checking for deadlock."),
+ gettext_noop("Sets the time to wait on a lock before checking for deadlock."),
NULL,
GUC_UNIT_MS
},
&DeadlockTimeout,
- 1000, 0, INT_MAX, NULL, NULL
+ 1000, 1, INT_MAX / 1000, NULL, NULL
},
/*
* number.
*
* MaxBackends is limited to INT_MAX/4 because some places compute
- * 4*MaxBackends without any overflow check. Likewise we have to limit
- * NBuffers to INT_MAX/2.
+ * 4*MaxBackends without any overflow check. This check is made on
+ * assign_maxconnections, since MaxBackends is computed as MaxConnections
+ * + autovacuum_max_workers.
+ *
+ * Likewise we have to limit NBuffers to INT_MAX/2.
*/
{
{"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
gettext_noop("Sets the maximum number of concurrent connections."),
NULL
},
- &MaxBackends,
- 100, 1, INT_MAX / 4, NULL, NULL
+ &MaxConnections,
+ 100, 1, INT_MAX / 4, assign_maxconnections, NULL
},
{
NULL
},
&ReservedBackends,
- 2, 0, INT_MAX / 4, NULL, NULL
+ 3, 0, INT_MAX / 4, NULL, NULL
},
{
GUC_UNIT_BLOCKS
},
&NBuffers,
- 1000, 16, INT_MAX / 2, NULL, NULL
+ 1024, 16, INT_MAX / 2, NULL, NULL
},
{
GUC_UNIT_BLOCKS
},
&num_temp_buffers,
- 1000, 100, INT_MAX / 2, NULL, show_num_temp_buffers
+ 1024, 100, INT_MAX / 2, NULL, show_num_temp_buffers
},
{
{
{"work_mem", PGC_USERSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
- gettext_noop("This much memory may be used by each internal "
+ gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
"temporary disk files."),
GUC_UNIT_KB
GUC_UNIT_KB
},
&max_stack_depth,
- 2048, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL
+ 100, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL
},
{
GUC_UNIT_MS
},
&autovacuum_vac_cost_delay,
- -1, -1, 1000, NULL, NULL
+ 20, -1, 1000, NULL, NULL
},
{
#ifdef LOCK_DEBUG
{
{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
},
{
{"trace_lock_table", PGC_SUSET, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
+ gettext_noop("No description available."),
NULL,
GUC_NOT_IN_SAMPLE
},
{
{"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Sets the maximum allowed duration (in milliseconds) of any statement."),
+ gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
},
0, 0, INT_MAX, NULL, NULL
},
+ {
+ {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Minimum age at which VACUUM should freeze a table row."),
+ NULL
+ },
+ &vacuum_freeze_min_age,
+ 100000000, 0, 1000000000, NULL, NULL
+ },
+
{
{"max_fsm_relations", PGC_POSTMASTER, RESOURCES_FSM,
gettext_noop("Sets the maximum number of tables and indexes for which free space is tracked."),
{
{"authentication_timeout", PGC_SIGHUP, CONN_AUTH_SECURITY,
- gettext_noop("Sets the maximum time in seconds to complete client authentication."),
+ gettext_noop("Sets the maximum allowed time to complete client authentication."),
NULL,
GUC_UNIT_S
},
{
/* Not for general use */
{"pre_auth_delay", PGC_SIGHUP, DEVELOPER_OPTIONS,
- gettext_noop("no description available"),
- NULL,
+ gettext_noop("Waits N seconds on connection startup before authentication."),
+ gettext_noop("This allows attaching a debugger to the process."),
GUC_NOT_IN_SAMPLE | GUC_UNIT_S
},
&PreAuthDelay,
{
{"checkpoint_timeout", PGC_SIGHUP, WAL_CHECKPOINTS,
- gettext_noop("Sets the maximum time in seconds between automatic WAL checkpoints."),
+ gettext_noop("Sets the maximum time between automatic WAL checkpoints."),
NULL,
GUC_UNIT_S
},
{
{"checkpoint_warning", PGC_SIGHUP, WAL_CHECKPOINTS,
- gettext_noop("Logs if filling of checkpoint segments happens more "
- "frequently than this (in seconds)."),
+ gettext_noop("Enables warnings if checkpoint segments are filled more "
+ "frequently than this."),
gettext_noop("Write a message to the server log if checkpoints "
"caused by the filling of checkpoint segment files happens more "
- "frequently than this number of seconds. Zero turns off the warning.")
+ "frequently than this number of seconds. Zero turns off the warning."),
+ GUC_UNIT_S
},
&CheckPointWarning,
30, 0, INT_MAX, NULL, NULL
{
{"wal_buffers", PGC_POSTMASTER, WAL_SETTINGS,
gettext_noop("Sets the number of disk-page buffers in shared memory for WAL."),
- NULL
+ NULL,
+ GUC_UNIT_XBLOCKS
},
&XLOGbuffers,
8, 4, INT_MAX, NULL, NULL
},
{
- {"commit_delay", PGC_USERSET, WAL_CHECKPOINTS,
+ {"wal_writer_delay", PGC_SIGHUP, WAL_SETTINGS,
+ gettext_noop("WAL writer sleep time between WAL flushes."),
+ NULL,
+ GUC_UNIT_MS
+ },
+ &WalWriterDelay,
+ 200, 1, 10000, NULL, NULL
+ },
+
+ {
+ {"commit_delay", PGC_USERSET, WAL_SETTINGS,
gettext_noop("Sets the delay in microseconds between transaction commit and "
"flushing WAL to disk."),
NULL
},
{
- {"commit_siblings", PGC_USERSET, WAL_CHECKPOINTS,
+ {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
gettext_noop("Sets the minimum concurrent open transactions before performing "
"commit_delay."),
NULL
{
{"log_min_duration_statement", PGC_SUSET, LOGGING_WHEN,
- gettext_noop("Sets the minimum execution time in milliseconds above which statements will "
- "be logged."),
- gettext_noop("Zero prints all queries. The default is -1 (turning this feature off)."),
+ gettext_noop("Sets the minimum execution time above which "
+ "statements will be logged."),
+ gettext_noop("Zero prints all queries. -1 turns this feature off."),
GUC_UNIT_MS
},
&log_min_duration_statement,
-1, -1, INT_MAX / 1000, NULL, NULL
},
+ {
+ {"log_autovacuum_min_duration", PGC_SIGHUP, LOGGING_WHAT,
+ gettext_noop("Sets the minimum execution time above which "
+ "autovacuum actions will be logged."),
+ gettext_noop("Zero prints all actions. -1 turns autovacuum logging off."),
+ GUC_UNIT_MS
+ },
+ &Log_autovacuum_min_duration,
+ -1, -1, INT_MAX / 1000, NULL, NULL
+ },
+
{
{"bgwriter_delay", PGC_SIGHUP, RESOURCES,
- gettext_noop("Background writer sleep time between rounds in milliseconds"),
+ gettext_noop("Background writer sleep time between rounds."),
NULL,
GUC_UNIT_MS
},
{
{"bgwriter_lru_maxpages", PGC_SIGHUP, RESOURCES,
- gettext_noop("Background writer maximum number of LRU pages to flush per round"),
+ gettext_noop("Background writer maximum number of LRU pages to flush per round."),
NULL
},
&bgwriter_lru_maxpages,
- 5, 0, 1000, NULL, NULL
- },
-
- {
- {"bgwriter_all_maxpages", PGC_SIGHUP, RESOURCES,
- gettext_noop("Background writer maximum number of all pages to flush per round"),
- NULL
- },
- &bgwriter_all_maxpages,
- 5, 0, 1000, NULL, NULL
+ 100, 0, 1000, NULL, NULL
},
{
{"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
- gettext_noop("Automatic log file rotation will occur after N minutes"),
+ gettext_noop("Automatic log file rotation will occur after N minutes."),
NULL,
GUC_UNIT_MIN
},
{
{"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
- gettext_noop("Automatic log file rotation will occur after N kilobytes"),
+ gettext_noop("Automatic log file rotation will occur after N kilobytes."),
NULL,
GUC_UNIT_KB
},
{
{"max_identifier_length", PGC_INTERNAL, PRESET_OPTIONS,
- gettext_noop("Shows the maximum identifier length"),
+ gettext_noop("Shows the maximum identifier length."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
{
{"block_size", PGC_INTERNAL, PRESET_OPTIONS,
- gettext_noop("Shows size of a disk block"),
+ gettext_noop("Shows the size of a disk block."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
},
{
{"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
- gettext_noop("Time to sleep between autovacuum runs, in seconds."),
+ gettext_noop("Time to sleep between autovacuum runs."),
NULL,
GUC_UNIT_S
},
&autovacuum_naptime,
- 60, 1, INT_MAX, NULL, NULL
+ 60, 1, INT_MAX / 1000, NULL, NULL
},
{
{"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM,
NULL
},
&autovacuum_vac_thresh,
- 1000, 0, INT_MAX, NULL, NULL
+ 50, 0, INT_MAX, NULL, NULL
},
{
{"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
NULL
},
&autovacuum_anl_thresh,
- 500, 0, INT_MAX, NULL, NULL
+ 50, 0, INT_MAX, NULL, NULL
+ },
+ {
+ /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
+ {"autovacuum_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Age at which to autovacuum a table to prevent transaction ID wraparound."),
+ NULL
+ },
+ &autovacuum_freeze_max_age,
+ 200000000, 100000000, 2000000000, NULL, NULL
+ },
+ {
+ /* see max_connections */
+ {"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Sets the maximum number of simultaneously running autovacuum worker processes."),
+ NULL
+ },
+ &autovacuum_max_workers,
+ 3, 1, INT_MAX / 4, assign_autovacuum_max_workers, NULL
},
{
{"tcp_keepalives_idle", PGC_USERSET, CLIENT_CONN_OTHER,
- gettext_noop("Seconds between issuing TCP keepalives."),
+ gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
},
{
{"tcp_keepalives_interval", PGC_USERSET, CLIENT_CONN_OTHER,
- gettext_noop("Seconds between TCP keepalive retransmits."),
+ gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
},
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, UNGROUPED,
- gettext_noop("Sets the maximum allowed result for exact search by gin."),
+ {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
},
{
{"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
- gettext_noop("Sets the planner's assumption about size of the disk cache."),
+ gettext_noop("Sets the planner's assumption about the size of the disk cache."),
gettext_noop("That is, the portion of the kernel's disk cache that "
"will be used for PostgreSQL data files. This is measured in disk "
"pages, which are normally 8 kB each."),
DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX, NULL, NULL
},
+ {
+ /* Can't be set in postgresql.conf */
+ {"server_version_num", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows the server version as an integer."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &server_version_num,
+ PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM, NULL, NULL
+ },
+
+ {
+ {"log_temp_files", PGC_USERSET, LOGGING_WHAT,
+ gettext_noop("Log the use of temporary files larger than this number of kilobytes."),
+ gettext_noop("Zero logs all files. The default is -1 (turning this feature off)."),
+ GUC_UNIT_KB
+ },
+ &log_temp_files,
+ -1, -1, INT_MAX, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
},
{
- {"bgwriter_lru_percent", PGC_SIGHUP, RESOURCES,
- gettext_noop("Background writer percentage of LRU buffers to flush per round"),
- NULL
- },
- &bgwriter_lru_percent,
- 1.0, 0.0, 100.0, NULL, NULL
- },
-
- {
- {"bgwriter_all_percent", PGC_SIGHUP, RESOURCES,
- gettext_noop("Background writer percentage of all buffers to flush per round"),
+ {"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES,
+ gettext_noop("Background writer multiplier on average buffers to scan per round."),
NULL
},
- &bgwriter_all_percent,
- 0.333, 0.0, 100.0, NULL, NULL
+ &bgwriter_lru_multiplier,
+ 2.0, 0.0, 10.0, NULL, NULL
},
{
NULL
},
&autovacuum_vac_scale,
- 0.4, 0.0, 100.0, NULL, NULL
+ 0.2, 0.0, 100.0, NULL, NULL
},
{
{"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM,
NULL
},
&autovacuum_anl_scale,
- 0.2, 0.0, 100.0, NULL, NULL
+ 0.1, 0.0, 100.0, NULL, NULL
+ },
+
+ {
+ {"checkpoint_completion_target", PGC_SIGHUP, WAL_CHECKPOINTS,
+ gettext_noop("Time spent flushing dirty buffers during checkpoint, as fraction of checkpoint interval."),
+ NULL
+ },
+ &CheckPointCompletionTarget,
+ 0.5, 0.0, 1.0, NULL, NULL
},
/* End-of-list marker */
{
{
{"archive_command", PGC_SIGHUP, WAL_SETTINGS,
- gettext_noop("WAL archiving command."),
- gettext_noop("The shell command that will be called to archive a WAL file.")
+ gettext_noop("Sets the shell command that will be called to archive a WAL file."),
+ NULL
},
&XLogArchiveCommand,
- "", NULL, NULL
+ "", NULL, show_archive_command
},
{
"specified level or a higher level are logged.")
},
&log_min_error_statement_str,
- "panic", assign_min_error_statement, NULL
+ "error", assign_min_error_statement, NULL
},
{
{"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT,
- gettext_noop("Controls information prefixed to each log line"),
- gettext_noop("if blank no prefix is used")
+ gettext_noop("Controls information prefixed to each log line."),
+ gettext_noop("If blank, no prefix is used.")
},
&Log_line_prefix,
"", NULL, NULL
},
+ {
+ {"log_timezone", PGC_SIGHUP, LOGGING_WHAT,
+ gettext_noop("Sets the time zone to use in log messages."),
+ NULL
+ },
+ &log_timezone_string,
+ "UNKNOWN", assign_log_timezone, show_log_timezone
+ },
{
{"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
{"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
- GUC_IS_NAME
+ GUC_IS_NAME
},
&default_tablespace,
"", assign_default_tablespace, NULL
},
+ {
+ {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
+ NULL,
+ GUC_LIST_INPUT | GUC_LIST_QUOTE
+ },
+ &temp_tablespaces,
+ "", assign_temp_tablespaces, NULL
+ },
+
{
{"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
"read committed", assign_defaultxactisolevel, NULL
},
+ {
+ {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the sessions behavior for triggers and rewrite rules."),
+ gettext_noop("Each session can be either"
+ " \"origin\", \"replica\" or \"local\".")
+ },
+ &session_replication_role_string,
+ "origin", assign_session_replication_role, NULL
+ },
+
{
{"dynamic_library_path", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the path for dynamically loadable modules."),
"$libdir", NULL, NULL
},
+ {
+ {"krb_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Sets realm to match Kerberos and GSSAPI users against."),
+ NULL,
+ GUC_SUPERUSER_ONLY
+ },
+ &pg_krb_realm,
+ NULL, NULL, NULL
+ },
+
{
{"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY,
gettext_noop("Sets the location of the Kerberos server key file."),
},
{
- {"preload_libraries", PGC_POSTMASTER, RESOURCES_KERNEL,
+ {"shared_preload_libraries", PGC_POSTMASTER, RESOURCES_KERNEL,
gettext_noop("Lists shared libraries to preload into server."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
},
- &preload_libraries_string,
+ &shared_preload_libraries_string,
+ "", NULL, NULL
+ },
+
+ {
+ {"local_preload_libraries", PGC_BACKEND, CLIENT_CONN_OTHER,
+ gettext_noop("Lists shared libraries to preload into each backend."),
+ NULL,
+ GUC_LIST_INPUT | GUC_LIST_QUOTE
+ },
+ &local_preload_libraries_string,
"", NULL, NULL
},
{
{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Sets the destination for server log output."),
- gettext_noop("Valid values are combinations of \"stderr\", \"syslog\", "
- "and \"eventlog\", depending on the platform."),
+ gettext_noop("Valid values are combinations of \"stderr\", "
+ "\"syslog\", \"csvlog\", and \"eventlog\", "
+ "depending on the platform."),
GUC_LIST_INPUT
},
&log_destination_string,
{
{"log_directory", PGC_SIGHUP, LOGGING_WHERE,
gettext_noop("Sets the destination directory for log files."),
- gettext_noop("May be specified as relative to the data directory "
+ gettext_noop("Can be specified as relative to the data directory "
"or as absolute path."),
GUC_SUPERUSER_ONLY
},
},
{
{"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
- gettext_noop("Selects a file of timezone abbreviations"),
+ gettext_noop("Selects a file of time zone abbreviations."),
NULL,
},
&timezone_abbreviations_string,
{
{"unix_socket_group", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
gettext_noop("Sets the owning group of the Unix-domain socket."),
- gettext_noop("(The owning user of the socket is always the user "
- "that starts the server.)")
+ gettext_noop("The owning user of the socket is always the user "
+ "that starts the server.")
},
&Unix_socket_group,
"", NULL, NULL
{
{"wal_sync_method", PGC_SIGHUP, WAL_SETTINGS,
- gettext_noop("Selects the method used for forcing WAL updates out to disk."),
+ gettext_noop("Selects the method used for forcing WAL updates to disk."),
NULL
},
&XLOG_sync_method,
{
{"hba_file", PGC_POSTMASTER, FILE_LOCATIONS,
- gettext_noop("Sets the server's \"hba\" configuration file"),
+ gettext_noop("Sets the server's \"hba\" configuration file."),
NULL,
GUC_SUPERUSER_ONLY
},
{
{"ident_file", PGC_POSTMASTER, FILE_LOCATIONS,
- gettext_noop("Sets the server's \"ident\" configuration file"),
+ gettext_noop("Sets the server's \"ident\" configuration file."),
NULL,
GUC_SUPERUSER_ONLY
},
NULL, assign_canonical_path, NULL
},
+ {
+ {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets how binary values are to be encoded in XML."),
+ gettext_noop("Valid values are BASE64 and HEX.")
+ },
+ &xmlbinary_string,
+ "base64", assign_xmlbinary, NULL
+ },
+
+ {
+ {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets whether XML data in implicit parsing and serialization "
+ "operations is to be considered as documents or content fragments."),
+ gettext_noop("Valid values are DOCUMENT and CONTENT.")
+ },
+ &xmloption_string,
+ "content", assign_xmloption, NULL
+ },
+
+ {
+ {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ gettext_noop("Sets default text search configuration."),
+ NULL
+ },
+ &TSCurrentConfig,
+ "pg_catalog.simple", assignTSCurrentConfig, NULL
+ },
+
+#ifdef USE_SSL
+ {
+ {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Sets the list of allowed SSL ciphers."),
+ NULL,
+ GUC_SUPERUSER_ONLY
+ },
+ &SSLCipherSuites,
+ "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", NULL, NULL
+ },
+#endif /* USE_SSL */
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
static bool reporting_enabled; /* TRUE to enable GUC_REPORT */
+static int GUCNestLevel = 0; /* 1 when in main transaction */
+
static int guc_var_compare(const void *a, const void *b);
static int guc_name_compare(const char *namea, const char *nameb);
-static void push_old_value(struct config_generic * gconf);
+static void push_old_value(struct config_generic * gconf, GucAction action);
static void ReportGUCOption(struct config_generic * record);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest);
static char *_ShowOption(struct config_generic * record, bool use_units);
-static bool is_newvalue_equal(struct config_generic *record, const char *newvalue);
+static bool is_newvalue_equal(struct config_generic * record, const char *newvalue);
/*
if (oldval == NULL ||
oldval == *(conf->variable) ||
oldval == conf->reset_val ||
- oldval == conf->tentative_val)
+ oldval == conf->boot_val)
return;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- if (oldval == stack->tentative_val.stringval ||
- oldval == stack->value.stringval)
+ if (oldval == stack->prior.stringval ||
+ oldval == stack->masked.stringval)
return;
}
if (strval == *(conf->variable) ||
strval == conf->reset_val ||
- strval == conf->tentative_val)
+ strval == conf->boot_val)
return true;
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
- if (strval == stack->tentative_val.stringval ||
- strval == stack->value.stringval)
+ if (strval == stack->prior.stringval ||
+ strval == stack->masked.stringval)
return true;
}
return false;
}
+/*
+ * Support for copying a variable's active value into a stack entry
+ */
+static void
+set_stack_value(struct config_generic * gconf, union config_var_value * val)
+{
+ switch (gconf->vartype)
+ {
+ case PGC_BOOL:
+ val->boolval =
+ *((struct config_bool *) gconf)->variable;
+ break;
+ case PGC_INT:
+ val->intval =
+ *((struct config_int *) gconf)->variable;
+ break;
+ case PGC_REAL:
+ val->realval =
+ *((struct config_real *) gconf)->variable;
+ break;
+ case PGC_STRING:
+ /* we assume stringval is NULL if not valid */
+ set_string_field((struct config_string *) gconf,
+ &(val->stringval),
+ *((struct config_string *) gconf)->variable);
+ break;
+ }
+}
+
+/*
+ * Support for discarding a no-longer-needed value in a stack entry
+ */
+static void
+discard_stack_value(struct config_generic * gconf, union config_var_value * val)
+{
+ switch (gconf->vartype)
+ {
+ case PGC_BOOL:
+ case PGC_INT:
+ case PGC_REAL:
+ /* no need to do anything */
+ break;
+ case PGC_STRING:
+ set_string_field((struct config_string *) gconf,
+ &(val->stringval),
+ NULL);
+ break;
+ }
+}
+
+/*
+ * Fetch the sorted array pointer (exported for help_config.c's use ONLY)
+ */
struct config_generic **
get_guc_variables(void)
{
sizeof(struct config_generic *), guc_var_compare);
}
-static bool
-is_custom_class(const char *name, int dotPos)
-{
- /*
- * assign_custom_variable_classes() has made sure no empty identifiers or
- * whitespace exists in the variable
- */
- bool result = false;
- const char *ccs = GetConfigOption("custom_variable_classes");
-
- if (ccs != NULL)
- {
- const char *start = ccs;
-
- for (;; ++ccs)
- {
- int 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;
-}
-
/*
* Add a new GUC variable to the list of known variables. The
* list is expanded if needed.
* Create and add a placeholder variable. It's presumed to belong
* to a valid custom variable class at this point.
*/
-static struct config_string *
+static struct config_generic *
add_placeholder_variable(const char *name, int elevel)
{
size_t sz = sizeof(struct config_string) + sizeof(char *);
var = (struct config_string *) guc_malloc(elevel, sz);
if (var == NULL)
return NULL;
-
- gen = &var->gen;
memset(var, 0, sz);
+ gen = &var->gen;
gen->name = guc_strdup(elevel, name);
if (gen->name == NULL)
/*
* The char* is allocated at the end of the struct since we have no
- * 'static' place to point to.
+ * '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 NULL;
}
- return var;
+ 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.
+ * 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
+ * placeholder record for a valid-looking custom variable name.
*/
static struct config_generic *
-find_option(const char *name, int elevel)
+find_option(const char *name, bool create_placeholders, int elevel)
{
- const char *dot;
const char **key = &name;
struct config_generic **res;
int i;
for (i = 0; map_old_guc_names[i] != NULL; i += 2)
{
if (guc_name_compare(name, map_old_guc_names[i]) == 0)
- return find_option(map_old_guc_names[i + 1], elevel);
+ return find_option(map_old_guc_names[i + 1], false, elevel);
}
- /*
- * Check if the name is qualified, and if so, check if the qualifier maps
- * to a custom variable class.
- */
- dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
- if (dot != NULL && is_custom_class(name, dot - name))
- /* Add a placeholder variable for this name */
- return (struct config_generic *) add_placeholder_variable(name, elevel);
+ 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.
+ */
+ const char *dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
+
+ if (dot != NULL &&
+ is_custom_class(name, dot - name, custom_variable_classes))
+ return add_placeholder_variable(name, elevel);
+ }
/* Unknown name */
return NULL;
return guc_name_compare(confa->name, confb->name);
}
-
+/*
+ * the bare comparison function for GUC names
+ */
static int
guc_name_compare(const char *namea, const char *nameb)
{
{
int i;
char *env;
+ long stack_rlimit;
+
+ /*
+ * Before log_line_prefix could possibly receive a nonempty setting, make
+ * sure that timezone processing is minimally alive (see elog.c).
+ */
+ pg_timezone_pre_initialize();
/*
* Build sorted array of all GUC variables.
gconf->status = 0;
gconf->reset_source = PGC_S_DEFAULT;
- gconf->tentative_source = PGC_S_DEFAULT;
gconf->source = PGC_S_DEFAULT;
gconf->stack = NULL;
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %d",
conf->gen.name, conf->boot_val);
- *conf->variable = conf->reset_val = conf->boot_val;
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_REAL:
PGC_S_DEFAULT))
elog(FATAL, "failed to initialize %s to %g",
conf->gen.name, conf->boot_val);
- *conf->variable = conf->reset_val = conf->boot_val;
+ *conf->variable = conf->reset_val = conf->boot_val;
break;
}
case PGC_STRING:
*conf->variable = NULL;
conf->reset_val = NULL;
- conf->tentative_val = NULL;
if (conf->boot_val == NULL)
{
- /* Cannot set value yet */
+ /* leave the value NULL, do not call assign hook */
break;
}
/*
* For historical reasons, some GUC parameters can receive defaults from
- * environment variables. Process those settings.
+ * environment variables. Process those settings. NB: if you add or
+ * remove anything here, see also ProcessConfigFile().
*/
env = getenv("PGPORT");
env = getenv("PGCLIENTENCODING");
if (env != NULL)
SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
+
+ /*
+ * rlimit isn't exactly an "environment variable", but it behaves about
+ * the same. If we can identify the platform stack depth rlimit, increase
+ * default stack depth setting up to whatever is safe (but at most 2MB).
+ */
+ stack_rlimit = get_stack_depth_rlimit();
+ if (stack_rlimit > 0)
+ {
+ int new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L;
+
+ if (new_limit > 100)
+ {
+ char limbuf[16];
+
+ new_limit = Min(new_limit, 2048);
+ sprintf(limbuf, "%d", new_limit);
+ SetConfigOption("max_stack_depth", limbuf,
+ PGC_POSTMASTER, PGC_S_ENV_VAR);
+ }
+ }
}
continue;
/* Save old value to support transaction abort */
- push_old_value(gconf);
+ push_old_value(gconf, GUC_ACTION_SET);
switch (gconf->vartype)
{
PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val;
- conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source;
- conf->gen.tentative_source = conf->gen.reset_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
break;
}
case PGC_INT:
PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val;
- conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source;
- conf->gen.tentative_source = conf->gen.reset_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
break;
}
case PGC_REAL:
PGC_S_SESSION))
elog(ERROR, "failed to reset %s", conf->gen.name);
*conf->variable = conf->reset_val;
- conf->tentative_val = conf->reset_val;
conf->gen.source = conf->gen.reset_source;
- conf->gen.tentative_source = conf->gen.reset_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
break;
}
case PGC_STRING:
struct config_string *conf = (struct config_string *) gconf;
char *str;
- if (conf->reset_val == NULL)
- {
- /* Nothing to reset to, as yet; so do nothing */
- break;
- }
-
/* We need not strdup here */
str = conf->reset_val;
- if (conf->assign_hook)
+ if (conf->assign_hook && str)
{
const char *newstr;
}
set_string_field(conf, conf->variable, str);
- set_string_field(conf, &conf->tentative_val, str);
conf->gen.source = conf->gen.reset_source;
- conf->gen.tentative_source = conf->gen.reset_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
break;
}
}
/*
* push_old_value
- * Push previous state during first assignment to a GUC variable
- * within a particular transaction.
- *
- * We have to be willing to "back-fill" the state stack if the first
- * assignment occurs within a subtransaction nested several levels deep.
- * This ensures that if an intermediate transaction aborts, it will have
- * the proper value available to restore the setting to.
+ * Push previous state during transactional assignment to a GUC variable.
*/
static void
-push_old_value(struct config_generic * gconf)
+push_old_value(struct config_generic * gconf, GucAction action)
{
- int my_level = GetCurrentTransactionNestLevel();
GucStack *stack;
- /* If we're not inside a transaction, do nothing */
- if (my_level == 0)
+ /* If we're not inside a nest level, do nothing */
+ if (GUCNestLevel == 0)
return;
- for (;;)
+ /* Do we already have a stack entry of the current nest level? */
+ stack = gconf->stack;
+ if (stack && stack->nest_level >= GUCNestLevel)
{
- /* Done if we already pushed it at this nesting depth */
- if (gconf->stack && gconf->stack->nest_level >= my_level)
- return;
-
- /*
- * We keep all the stack entries in TopTransactionContext so as to
- * avoid allocation problems when a subtransaction back-fills stack
- * entries for upper transaction levels.
- */
- stack = (GucStack *) MemoryContextAlloc(TopTransactionContext,
- sizeof(GucStack));
-
- stack->prev = gconf->stack;
- stack->nest_level = stack->prev ? stack->prev->nest_level + 1 : 1;
- stack->status = gconf->status;
- stack->tentative_source = gconf->tentative_source;
- stack->source = gconf->source;
-
- switch (gconf->vartype)
+ /* Yes, so adjust its state if necessary */
+ Assert(stack->nest_level == GUCNestLevel);
+ switch (action)
{
- case PGC_BOOL:
- stack->tentative_val.boolval =
- ((struct config_bool *) gconf)->tentative_val;
- stack->value.boolval =
- *((struct config_bool *) gconf)->variable;
- break;
-
- case PGC_INT:
- stack->tentative_val.intval =
- ((struct config_int *) gconf)->tentative_val;
- stack->value.intval =
- *((struct config_int *) gconf)->variable;
+ case GUC_ACTION_SET:
+ /* SET overrides any prior action at same nest level */
+ if (stack->state == GUC_SET_LOCAL)
+ {
+ /* must discard old masked value */
+ discard_stack_value(gconf, &stack->masked);
+ }
+ stack->state = GUC_SET;
break;
-
- case PGC_REAL:
- stack->tentative_val.realval =
- ((struct config_real *) gconf)->tentative_val;
- stack->value.realval =
- *((struct config_real *) gconf)->variable;
+ case GUC_ACTION_LOCAL:
+ if (stack->state == GUC_SET)
+ {
+ /* SET followed by SET LOCAL, remember SET's value */
+ set_stack_value(gconf, &stack->masked);
+ stack->state = GUC_SET_LOCAL;
+ }
+ /* in all other cases, no change to stack entry */
break;
-
- case PGC_STRING:
- stack->tentative_val.stringval =
- ((struct config_string *) gconf)->tentative_val;
- stack->value.stringval =
- *((struct config_string *) gconf)->variable;
+ case GUC_ACTION_SAVE:
+ /* Could only have a prior SAVE of same variable */
+ Assert(stack->state == GUC_SAVE);
break;
}
+ Assert(guc_dirty); /* must be set already */
+ return;
+ }
- gconf->stack = stack;
-
- /* Set state to indicate nothing happened yet within this level */
- gconf->status = GUC_HAVE_STACK;
+ /*
+ * Push a new stack entry
+ *
+ * We keep all the stack entries in TopTransactionContext for simplicity.
+ */
+ stack = (GucStack *) MemoryContextAllocZero(TopTransactionContext,
+ sizeof(GucStack));
- /* Ensure we remember to pop at end of xact */
- guc_dirty = true;
+ stack->prev = gconf->stack;
+ stack->nest_level = GUCNestLevel;
+ switch (action)
+ {
+ case GUC_ACTION_SET:
+ stack->state = GUC_SET;
+ break;
+ case GUC_ACTION_LOCAL:
+ stack->state = GUC_LOCAL;
+ break;
+ case GUC_ACTION_SAVE:
+ stack->state = GUC_SAVE;
+ break;
}
+ stack->source = gconf->source;
+ set_stack_value(gconf, &stack->prior);
+
+ gconf->stack = stack;
+
+ /* Ensure we remember to pop at end of xact */
+ guc_dirty = true;
+}
+
+
+/*
+ * Do GUC processing at main transaction start.
+ */
+void
+AtStart_GUC(void)
+{
+ /*
+ * The nest level should be 0 between transactions; if it isn't, somebody
+ * didn't call AtEOXact_GUC, or called it with the wrong nestLevel. We
+ * throw a warning but make no other effort to clean up.
+ */
+ if (GUCNestLevel != 0)
+ elog(WARNING, "GUC nest level = %d at transaction start",
+ GUCNestLevel);
+ GUCNestLevel = 1;
+}
+
+/*
+ * 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.
+ */
+int
+NewGUCNestLevel(void)
+{
+ return ++GUCNestLevel;
}
/*
- * Do GUC processing at transaction or subtransaction commit or abort.
+ * 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.)
+ * During abort, we discard all GUC settings that were applied at nesting
+ * levels >= nestLevel. nestLevel == 1 corresponds to the main transaction.
*/
void
-AtEOXact_GUC(bool isCommit, bool isSubXact)
+AtEOXact_GUC(bool isCommit, int nestLevel)
{
- int my_level;
+ bool still_dirty;
int i;
+ Assert(nestLevel > 0 && nestLevel <= GUCNestLevel);
+
/* Quick exit if nothing's changed in this transaction */
if (!guc_dirty)
+ {
+ GUCNestLevel = nestLevel - 1;
return;
+ }
- my_level = GetCurrentTransactionNestLevel();
- Assert(isSubXact ? (my_level > 1) : (my_level == 1));
-
+ still_dirty = false;
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
- int my_status = gconf->status & (~GUC_IN_CONFFILE);
- GucStack *stack = gconf->stack;
- bool useTentative;
- bool changed;
+ GucStack *stack;
/*
- * Skip if nothing's happened to this var in this transaction
+ * 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.
*/
- if (my_status == 0)
+ while ((stack = gconf->stack) != NULL &&
+ stack->nest_level >= nestLevel)
{
- Assert(stack == NULL);
- continue;
- }
- /* Assert that we stacked old value before changing it */
- Assert(stack != NULL && (my_status & GUC_HAVE_STACK));
- /* However, the last change may have been at an outer xact level */
- if (stack->nest_level < my_level)
- continue;
- Assert(stack->nest_level == my_level);
-
- /*
- * We will pop the stack entry. Start by restoring outer xact status
- * (since we may want to modify it below). Be careful to use
- * my_status to reference the inner xact status below this point...
- */
- gconf->status = stack->status;
-
- /*
- * We have two cases:
- *
- * If commit and HAVE_TENTATIVE, set actual value to tentative (this
- * is to override a SET LOCAL if one occurred later than SET). We keep
- * the tentative value and propagate HAVE_TENTATIVE to the parent
- * status, allowing the SET's effect to percolate up. (But if we're
- * exiting the outermost transaction, we'll drop the HAVE_TENTATIVE
- * bit below.)
- *
- * Otherwise, we have a transaction that aborted or executed only SET
- * LOCAL (or no SET at all). In either case it should have no further
- * effect, so restore both tentative and actual values from the stack
- * entry.
- */
-
- useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0;
- changed = false;
+ GucStack *prev = stack->prev;
+ bool restorePrior = false;
+ bool restoreMasked = false;
+ bool changed;
- switch (gconf->vartype)
- {
- case PGC_BOOL:
+ /*
+ * In this next bit, if we don't set either restorePrior or
+ * restoreMasked, we must "discard" any unwanted fields of the
+ * stack entries to avoid leaking memory. If we do set one of
+ * those flags, unused fields will be cleaned up after restoring.
+ */
+ if (!isCommit) /* if abort, always restore prior value */
+ restorePrior = true;
+ else if (stack->state == GUC_SAVE)
+ restorePrior = true;
+ else if (stack->nest_level == 1)
+ {
+ /* transaction commit */
+ if (stack->state == GUC_SET_LOCAL)
+ restoreMasked = true;
+ else if (stack->state == GUC_SET)
{
- struct config_bool *conf = (struct config_bool *) gconf;
- bool newval;
- GucSource newsource;
-
- if (useTentative)
- {
- newval = conf->tentative_val;
- newsource = conf->gen.tentative_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- }
- else
- {
- newval = stack->value.boolval;
- newsource = stack->source;
- conf->tentative_val = stack->tentative_val.boolval;
- conf->gen.tentative_source = stack->tentative_source;
- }
-
- if (*conf->variable != newval)
- {
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval,
- true, PGC_S_OVERRIDE))
- elog(LOG, "failed to commit %s",
- conf->gen.name);
- *conf->variable = newval;
- changed = true;
- }
- conf->gen.source = newsource;
- break;
+ /* we keep the current active value */
+ discard_stack_value(gconf, &stack->prior);
}
- case PGC_INT:
+ else /* must be GUC_LOCAL */
+ restorePrior = true;
+ }
+ else if (prev == NULL ||
+ prev->nest_level < stack->nest_level - 1)
+ {
+ /* decrement entry's level and do not pop it */
+ stack->nest_level--;
+ continue;
+ }
+ else
+ {
+ /*
+ * We have to merge this stack entry into prev. See README for
+ * discussion of this bit.
+ */
+ switch (stack->state)
{
- struct config_int *conf = (struct config_int *) gconf;
- int newval;
- GucSource newsource;
+ case GUC_SAVE:
+ Assert(false); /* can't get here */
+
+ case GUC_SET:
+ /* next level always becomes SET */
+ discard_stack_value(gconf, &stack->prior);
+ if (prev->state == GUC_SET_LOCAL)
+ discard_stack_value(gconf, &prev->masked);
+ prev->state = GUC_SET;
+ break;
- if (useTentative)
- {
- newval = conf->tentative_val;
- newsource = conf->gen.tentative_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- }
- else
- {
- newval = stack->value.intval;
- newsource = stack->source;
- conf->tentative_val = stack->tentative_val.intval;
- conf->gen.tentative_source = stack->tentative_source;
- }
+ case GUC_LOCAL:
+ if (prev->state == GUC_SET)
+ {
+ /* LOCAL migrates down */
+ prev->masked = stack->prior;
+ prev->state = GUC_SET_LOCAL;
+ }
+ else
+ {
+ /* else just forget this stack level */
+ discard_stack_value(gconf, &stack->prior);
+ }
+ break;
- if (*conf->variable != newval)
- {
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval,
- true, PGC_S_OVERRIDE))
- elog(LOG, "failed to commit %s",
- conf->gen.name);
- *conf->variable = newval;
- changed = true;
- }
- conf->gen.source = newsource;
- break;
+ case GUC_SET_LOCAL:
+ /* prior state at this level no longer wanted */
+ discard_stack_value(gconf, &stack->prior);
+ /* copy down the masked state */
+ if (prev->state == GUC_SET_LOCAL)
+ discard_stack_value(gconf, &prev->masked);
+ prev->masked = stack->masked;
+ prev->state = GUC_SET_LOCAL;
+ break;
}
- case PGC_REAL:
- {
- struct config_real *conf = (struct config_real *) gconf;
- double newval;
- GucSource newsource;
+ }
- if (useTentative)
- {
- newval = conf->tentative_val;
- newsource = conf->gen.tentative_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- }
- else
- {
- newval = stack->value.realval;
- newsource = stack->source;
- conf->tentative_val = stack->tentative_val.realval;
- conf->gen.tentative_source = stack->tentative_source;
- }
+ changed = false;
- if (*conf->variable != newval)
- {
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval,
- true, PGC_S_OVERRIDE))
- elog(LOG, "failed to commit %s",
- conf->gen.name);
- *conf->variable = newval;
- changed = true;
- }
- conf->gen.source = newsource;
- break;
+ if (restorePrior || restoreMasked)
+ {
+ /* Perform appropriate restoration of the stacked value */
+ union config_var_value newvalue;
+ GucSource newsource;
+
+ if (restoreMasked)
+ {
+ newvalue = stack->masked;
+ newsource = PGC_S_SESSION;
}
- case PGC_STRING:
+ else
{
- struct config_string *conf = (struct config_string *) gconf;
- char *newval;
- GucSource newsource;
+ newvalue = stack->prior;
+ newsource = stack->source;
+ }
- if (useTentative)
- {
- newval = conf->tentative_val;
- newsource = conf->gen.tentative_source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- }
- else
- {
- newval = stack->value.stringval;
- newsource = stack->source;
- set_string_field(conf, &conf->tentative_val,
- stack->tentative_val.stringval);
- conf->gen.tentative_source = stack->tentative_source;
- }
+ switch (gconf->vartype)
+ {
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
+ bool newval = newvalue.boolval;
- if (*conf->variable != newval)
- {
- if (conf->assign_hook)
+ if (*conf->variable != newval)
+ {
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval,
+ true, PGC_S_OVERRIDE))
+ elog(LOG, "failed to commit %s",
+ conf->gen.name);
+ *conf->variable = newval;
+ changed = true;
+ }
+ break;
+ }
+ case PGC_INT:
{
- const char *newstr;
-
- newstr = (*conf->assign_hook) (newval, true,
- PGC_S_OVERRIDE);
- if (newstr == NULL)
- elog(LOG, "failed to commit %s",
- conf->gen.name);
- else if (newstr != newval)
+ struct config_int *conf = (struct config_int *) gconf;
+ int newval = newvalue.intval;
+
+ if (*conf->variable != newval)
{
- /*
- * If newval should now be freed, it'll be
- * taken care of below.
- *
- * See notes in set_config_option about
- * casting
- */
- newval = (char *) newstr;
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval,
+ true, PGC_S_OVERRIDE))
+ elog(LOG, "failed to commit %s",
+ conf->gen.name);
+ *conf->variable = newval;
+ changed = true;
}
+ break;
}
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
+ double newval = newvalue.realval;
- set_string_field(conf, conf->variable, newval);
- changed = true;
- }
- conf->gen.source = newsource;
- /* Release stacked values if not used anymore */
- set_string_field(conf, &stack->value.stringval,
- NULL);
- set_string_field(conf, &stack->tentative_val.stringval,
- NULL);
- /* Don't store tentative value separately after commit */
- if (!isSubXact)
- set_string_field(conf, &conf->tentative_val, NULL);
- break;
+ if (*conf->variable != newval)
+ {
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval,
+ true, PGC_S_OVERRIDE))
+ elog(LOG, "failed to commit %s",
+ conf->gen.name);
+ *conf->variable = newval;
+ changed = true;
+ }
+ break;
+ }
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+ char *newval = newvalue.stringval;
+
+ if (*conf->variable != newval)
+ {
+ if (conf->assign_hook && newval)
+ {
+ const char *newstr;
+
+ newstr = (*conf->assign_hook) (newval, true,
+ PGC_S_OVERRIDE);
+ if (newstr == NULL)
+ elog(LOG, "failed to commit %s",
+ conf->gen.name);
+ else if (newstr != newval)
+ {
+ /*
+ * If newval should now be freed,
+ * it'll be taken care of below.
+ *
+ * See notes in set_config_option
+ * about casting
+ */
+ newval = (char *) newstr;
+ }
+ }
+
+ set_string_field(conf, conf->variable, newval);
+ changed = true;
+ }
+
+ /*
+ * Release stacked values if not used anymore. We
+ * could use discard_stack_value() here, but since
+ * we have type-specific code anyway, might as
+ * well inline it.
+ */
+ set_string_field(conf, &stack->prior.stringval, NULL);
+ set_string_field(conf, &stack->masked.stringval, NULL);
+ break;
+ }
}
- }
- /* Finish popping the state stack */
- gconf->stack = stack->prev;
- pfree(stack);
+ gconf->source = newsource;
+ }
+
+ /* Finish popping the state stack */
+ gconf->stack = prev;
+ pfree(stack);
- /*
- * If we're now out of all xact levels, forget TENTATIVE status bit;
- * there's nothing tentative about the value anymore.
- */
- if (!isSubXact)
- {
- Assert(gconf->stack == NULL);
- gconf->status = 0;
- }
+ /* Report new value if we changed it */
+ if (changed && (gconf->flags & GUC_REPORT))
+ ReportGUCOption(gconf);
+ } /* end of stack-popping loop */
- /* Report new value if we changed it */
- if (changed && (gconf->flags & GUC_REPORT))
- ReportGUCOption(gconf);
+ if (stack != NULL)
+ still_dirty = true;
}
- /*
- * If we're now out of all xact levels, we can clear guc_dirty. (Note: we
- * cannot reset guc_dirty when exiting a subtransaction, because we know
- * that all outer transaction levels will have stacked values to deal
- * with.)
- */
- if (!isSubXact)
- guc_dirty = false;
+ /* If there are no remaining stack entries, we can reset guc_dirty */
+ guc_dirty = still_dirty;
+
+ /* Update nesting level */
+ GUCNestLevel = nestLevel - 1;
}
/*
* Try to interpret value as boolean value. Valid values are: true,
- * false, yes, no, on, off, 1, 0. If the string parses okay, return
- * true, else false. If result is not NULL, return the parsing result
- * there.
+ * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
*/
static bool
parse_bool(const char *value, bool *result)
*result = false;
}
- else if (pg_strcasecmp(value, "on") == 0)
+ /* 'o' is not unique enough */
+ else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
{
if (result)
*result = true;
}
- else if (pg_strcasecmp(value, "off") == 0)
+ else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
{
if (result)
*result = false;
/*
* Try to parse value as an integer. The accepted formats are the
- * usual decimal, octal, or hexadecimal formats. If the string parses
- * okay, return true, else false. If result is not NULL, return the
- * value there.
+ * usual decimal, octal, or hexadecimal formats, optionally followed by
+ * a unit name if "flags" indicates a unit is allowed.
+ *
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
+ * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable
+ * HINT message, or NULL if no hint provided.
*/
static bool
-parse_int(const char *value, int *result, int flags)
+parse_int(const char *value, int *result, int flags, const char **hintmsg)
{
- long val;
+ int64 val;
char *endptr;
+ /* To suppress compiler warnings, always set output params */
+ if (result)
+ *result = 0;
+ if (hintmsg)
+ *hintmsg = NULL;
+
+ /* We assume here that int64 is at least as wide as long */
errno = 0;
val = strtol(value, &endptr, 0);
- if ((flags & GUC_UNIT_MEMORY) && endptr != value)
- {
- bool used = false;
-
- while (*endptr == ' ')
- endptr++;
-
- if (strcmp(endptr, "kB") == 0)
- {
- used = true;
- endptr += 2;
- }
- else if (strcmp(endptr, "MB") == 0)
- {
- val *= KB_PER_MB;
- used = true;
- endptr += 2;
- }
- else if (strcmp(endptr, "GB") == 0)
- {
- val *= KB_PER_MB;
- used = true;
- endptr += 2;
- }
+ if (endptr == value)
+ return false; /* no HINT for integer syntax error */
- if (used && (flags & GUC_UNIT_BLOCKS))
- val /= (BLCKSZ/1024);
+ if (errno == ERANGE || val != (int64) ((int32) val))
+ {
+ if (hintmsg)
+ *hintmsg = gettext_noop("Value exceeds integer range.");
+ return false;
}
- if ((flags & GUC_UNIT_TIME) && endptr != value)
+ /* allow whitespace between integer and unit */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+
+ /* Handle possible unit */
+ if (*endptr != '\0')
{
- bool used = false;
+ /*
+ * Note: the multiple-switch coding technique here is a bit tedious,
+ * but seems necessary to avoid intermediate-value overflows.
+ *
+ * If INT64_IS_BUSTED (ie, it's really int32) we will fail to detect
+ * overflow due to units conversion, but there are few enough such
+ * machines that it does not seem worth trying to be smarter.
+ */
+ if (flags & GUC_UNIT_MEMORY)
+ {
+ /* 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\".");
- while (*endptr == ' ')
- endptr++;
+#if BLCKSZ < 1024 || BLCKSZ > (1024*1024)
+#error BLCKSZ must be between 1KB and 1MB
+#endif
+#if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024)
+#error XLOG_BLCKSZ must be between 1KB and 1MB
+#endif
- if (strcmp(endptr, "ms") == 0)
- {
- used = true;
- endptr += 2;
- }
- else if (strcmp(endptr, "s") == 0)
- {
- val *= MS_PER_S;
- used = true;
- endptr += 1;
- }
- else if (strcmp(endptr, "min") == 0)
- {
- val *= MS_PER_MIN;
- used = true;
- endptr += 3;
+ if (strncmp(endptr, "kB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GUC_UNIT_MEMORY)
+ {
+ case GUC_UNIT_BLOCKS:
+ val /= (BLCKSZ / 1024);
+ break;
+ case GUC_UNIT_XBLOCKS:
+ val /= (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
+ else if (strncmp(endptr, "MB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GUC_UNIT_MEMORY)
+ {
+ case GUC_UNIT_KB:
+ val *= KB_PER_MB;
+ break;
+ case GUC_UNIT_BLOCKS:
+ val *= KB_PER_MB / (BLCKSZ / 1024);
+ break;
+ case GUC_UNIT_XBLOCKS:
+ val *= KB_PER_MB / (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
+ else if (strncmp(endptr, "GB", 2) == 0)
+ {
+ endptr += 2;
+ switch (flags & GUC_UNIT_MEMORY)
+ {
+ case GUC_UNIT_KB:
+ val *= KB_PER_GB;
+ break;
+ case GUC_UNIT_BLOCKS:
+ val *= KB_PER_GB / (BLCKSZ / 1024);
+ break;
+ case GUC_UNIT_XBLOCKS:
+ val *= KB_PER_GB / (XLOG_BLCKSZ / 1024);
+ break;
+ }
+ }
}
- else if (strcmp(endptr, "h") == 0)
+ else if (flags & GUC_UNIT_TIME)
{
- val *= MS_PER_H;
- used = true;
- endptr += 1;
+ /* 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;
+ break;
+ case GUC_UNIT_MIN:
+ val /= MS_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "s", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GUC_UNIT_TIME)
+ {
+ case GUC_UNIT_MS:
+ val *= MS_PER_S;
+ break;
+ case GUC_UNIT_MIN:
+ val /= S_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "min", 3) == 0)
+ {
+ endptr += 3;
+ switch (flags & GUC_UNIT_TIME)
+ {
+ case GUC_UNIT_MS:
+ val *= MS_PER_MIN;
+ break;
+ case GUC_UNIT_S:
+ val *= S_PER_MIN;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "h", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GUC_UNIT_TIME)
+ {
+ case GUC_UNIT_MS:
+ val *= MS_PER_H;
+ break;
+ case GUC_UNIT_S:
+ val *= S_PER_H;
+ break;
+ case GUC_UNIT_MIN:
+ val *= MIN_PER_H;
+ break;
+ }
+ }
+ else if (strncmp(endptr, "d", 1) == 0)
+ {
+ endptr += 1;
+ switch (flags & GUC_UNIT_TIME)
+ {
+ case GUC_UNIT_MS:
+ val *= MS_PER_D;
+ break;
+ case GUC_UNIT_S:
+ val *= S_PER_D;
+ break;
+ case GUC_UNIT_MIN:
+ val *= MIN_PER_D;
+ break;
+ }
+ }
}
- else if (strcmp(endptr, "d") == 0)
+
+ /* allow whitespace after unit */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+
+ if (*endptr != '\0')
+ return false; /* appropriate hint, if any, already set */
+
+ /* Check for overflow due to units conversion */
+ if (val != (int64) ((int32) val))
{
- val *= MS_PER_D;
- used = true;
- endptr += 1;
+ if (hintmsg)
+ *hintmsg = gettext_noop("Value exceeds integer range.");
+ return false;
}
-
- if (used && (flags & GUC_UNIT_S))
- val /= MS_PER_S;
- else if (used && (flags & GUC_UNIT_MIN))
- val /= MS_PER_MIN;
}
- if (endptr == value || *endptr != '\0' || errno == ERANGE
-#ifdef HAVE_LONG_INT_64
- /* if long > 32 bits, check for overflow of int4 */
- || val != (long) ((int32) val)
-#endif
- )
- {
- if (result)
- *result = 0; /* suppress compiler warning */
- return false;
- }
if (result)
*result = (int) val;
return true;
/*
- * Try to parse value as a floating point constant in the usual
- * format. If the value parsed okay return true, else false. If
- * result is not NULL, return the semantic value there.
+ * Try to parse value as a floating point number in the usual format.
+ * If the string parses okay, return true, else false.
+ * If okay and result is not NULL, return the value in *result.
*/
static bool
parse_real(const char *value, double *result)
double val;
char *endptr;
+ if (result)
+ *result = 0; /* suppress compiler warning */
+
errno = 0;
val = strtod(value, &endptr);
- if (endptr == value || *endptr != '\0' || errno == ERANGE)
- {
- if (result)
- *result = 0; /* suppress compiler warning */
+ if (endptr == value || errno == ERANGE)
return false;
- }
+
+ /* allow whitespace after number */
+ while (isspace((unsigned char) *endptr))
+ endptr++;
+ if (*endptr != '\0')
+ return false;
+
if (result)
*result = val;
return true;
return result;
}
+
/*
- * Try to parse value. Determine what is type and call related
- * parsing function or if newval is equal to NULL, reset value
- * to default or bootval. If the value parsed okay return true,
- * else false.
+ * 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.
+ *
+ * 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.
+ *
+ * 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(DEBUG) 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.
+ *
+ * See also SetConfigOption for an external interface.
*/
-static bool
-parse_value(int elevel, const struct config_generic *record,
- const char *value, GucSource *source, bool changeVal,
- union config_var_value *retval)
+bool
+set_config_option(const char *name, const char *value,
+ GucContext context, GucSource source,
+ GucAction action, bool changeVal)
{
+ struct config_generic *record;
+ int elevel;
+ bool makeDefault;
- Assert( !(changeVal && retval==NULL) );
- /*
- * Evaluate value and set variable.
- */
- switch (record->vartype)
+ if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
{
- case PGC_BOOL:
- {
- struct config_bool *conf = (struct config_bool *) record;
- bool newval;
-
- if (value)
- {
- if (!parse_bool(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a Boolean value",
- record->name)));
- return false;
- }
- }
- else
- {
- /* Revert value to default if source is configuration file. It is used when
- * configuration parameter is removed/commented out in the config file. Else
- * RESET or SET TO DEFAULT command is called and reset_val is used.
- */
- if( *source == PGC_S_FILE )
- {
- newval = conf->boot_val;
- }
- else
- {
- newval = conf->reset_val;
- *source = conf->gen.reset_source;
- }
- }
-
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval, changeVal, *source))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for parameter \"%s\": %d",
- record->name, (int) newval)));
- return false;
- }
- if( retval != NULL )
- retval->boolval = newval;
- break;
- }
-
- case PGC_INT:
- {
- struct config_int *conf = (struct config_int *) record;
- int newval;
-
- if (value)
- {
- if (!parse_int(value, &newval, conf->gen.flags))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires an integer value",
- record->name)));
- 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, record->name, conf->min, conf->max)));
- return false;
- }
- }
- else
- {
- /* Revert value to default if source is configuration file. It is used when
- * configuration parameter is removed/commented out in the config file. Else
- * RESET or SET TO DEFAULT command is called and reset_val is used.
- */
- if( *source == PGC_S_FILE )
- {
- newval = conf->boot_val;
- }
- else
- {
- newval = conf->reset_val;
- *source = conf->gen.reset_source;
- }
- }
-
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval, changeVal, *source))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for parameter \"%s\": %d",
- record->name, newval)));
- return false;
- }
- if( retval != NULL )
- retval->intval = newval;
- break;
- }
-
- case PGC_REAL:
- {
- struct config_real *conf = (struct config_real *) record;
- double newval;
-
- if (value)
- {
- if (!parse_real(value, &newval))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" requires a numeric value",
- record->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, record->name, conf->min, conf->max)));
- return false;
- }
- }
- else
- {
- /* Revert value to default if source is configuration file. It is used when
- * configuration parameter is removed/commented out in the config file. Else
- * RESET or SET TO DEFAULT command is called and reset_val is used.
- */
- if( *source == PGC_S_FILE )
- {
- newval = conf->boot_val;
- }
- else
- {
- newval = conf->reset_val;
- *source = conf->gen.reset_source;
- }
- }
-
- if (conf->assign_hook)
- if (!(*conf->assign_hook) (newval, changeVal, *source))
- {
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for parameter \"%s\": %g",
- record->name, newval)));
- return false;
- }
- if( retval != NULL )
- retval->realval = newval;
- break;
- }
-
- case PGC_STRING:
- {
- struct config_string *conf = (struct config_string *) record;
- char *newval;
-
- if (value)
- {
- newval = guc_strdup(elevel, value);
- if (newval == NULL)
- return false;
- /*
- * The only sort of "parsing" check we need to do is
- * apply truncation if GUC_IS_NAME.
- */
- if (conf->gen.flags & GUC_IS_NAME)
- truncate_identifier(newval, strlen(newval), true);
- }
- else if (*source == PGC_S_FILE)
- {
- /* Revert value to default when item is removed from config file. */
- if ( conf->boot_val != NULL )
- {
- newval = guc_strdup(elevel, conf->boot_val);
- if (newval == NULL)
- return false;
- }
- else
- {
- return false;
- }
- }
- else if (conf->reset_val)
- {
- /*
- * We could possibly avoid strdup here, but easier to make
- * this case work the same as the normal assignment case.
- */
- newval = guc_strdup(elevel, conf->reset_val);
- if (newval == NULL)
- return false;
- *source = conf->gen.reset_source;
- }
- else
- {
- /* Nothing to reset to, as yet; so do nothing */
- break;
- }
-
- if (conf->assign_hook)
- {
- const char *hookresult;
-
- /*
- * If the hook ereports, we have to make sure we free
- * newval, else it will be a permanent memory leak.
- */
- hookresult = call_string_assign_hook(conf->assign_hook,
- newval,
- changeVal,
- *source);
- if (hookresult == NULL)
- {
- free(newval);
- ereport(elevel,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for parameter \"%s\": \"%s\"",
- record->name, value ? value : "")));
- return false;
- }
- else if (hookresult != newval)
- {
- free(newval);
-
- /*
- * Having to cast away const here is annoying, but the
- * alternative is to declare assign_hooks as returning
- * char*, which would mean they'd have to cast away
- * const, or as both taking and returning char*, which
- * doesn't seem attractive either --- we don't want
- * them to scribble on the passed str.
- */
- newval = (char *) hookresult;
- }
- }
+ /*
+ * To avoid cluttering the log, only the postmaster bleats loudly
+ * about problems with the config file.
+ */
+ elevel = IsUnderPostmaster ? DEBUG2 : LOG;
+ }
+ else if (source == PGC_S_DATABASE || source == PGC_S_USER)
+ elevel = INFO;
+ else
+ elevel = ERROR;
- if ( !changeVal )
- free(newval);
- if( retval != NULL )
- retval->stringval= newval;
- break;
- }
+ record = find_option(name, true, elevel);
+ if (record == NULL)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"", name)));
+ return false;
}
- return true;
-}
-/*
- * Check if the option can be set at this time. See guc.h for the precise
- * rules.
- */
-static bool
-checkContext(int elevel, struct config_generic *record, GucContext context)
-{
+ /*
+ * 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. Note that we don't want to throw errors if we're in the SIGHUP
+ * context. In that case we just ignore the attempt and return true.
+ */
switch (record->context)
{
case PGC_INTERNAL:
+ if (context == PGC_SIGHUP)
+ return true;
if (context != PGC_INTERNAL)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed",
- record->name)));
+ name)));
return false;
}
break;
case PGC_POSTMASTER:
if (context == PGC_SIGHUP)
- return false;
-
+ {
+ /*
+ * We are reading a PGC_POSTMASTER var from postgresql.conf.
+ * We can't change the setting, so give a warning if the DBA
+ * tries to change it. (Throwing an error would be more
+ * consistent, but seems overly rigid.)
+ */
+ if (changeVal && !is_newvalue_equal(record, value))
+ ereport(elevel,
+ (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored",
+ name)));
+ return true;
+ }
if (context != PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed after server start",
- record->name)));
+ name)));
return false;
}
break;
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed now",
- record->name)));
+ name)));
return false;
}
* backend start.
*/
if (IsUnderPostmaster)
- {
- return false;
- }
+ return true;
}
else if (context != PGC_BACKEND && context != PGC_POSTMASTER)
{
ereport(elevel,
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be set after connection start",
- record->name)));
+ name)));
return false;
}
break;
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
- record->name)));
+ name)));
return false;
}
break;
/* always okay */
break;
}
- return true;
-}
-
-/*
- * Get error level for different sources and context.
- */
-static int
-get_elevel(GucContext context, GucSource source)
-{
- int elevel;
- if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
- {
- /*
- * To avoid cluttering the log, only the postmaster bleats loudly
- * about problems with the config file.
- */
- elevel = IsUnderPostmaster ? DEBUG2 : LOG;
- }
- else if (source == PGC_S_DATABASE || source == PGC_S_USER)
- elevel = INFO;
- else
- elevel = ERROR;
-
- return elevel;
-}
-
-/*
- * Verify if option exists and value is valid.
- * It is primary used for validation of items in configuration file.
- */
-bool
-verify_config_option(const char *name, const char *value,
- GucContext context, GucSource source,
- bool *isNewEqual, bool *isContextOK)
-{
- union config_var_value newval;
- int elevel;
- struct config_generic *record;
-
- elevel = get_elevel(context, source);
-
- record = find_option(name, elevel);
- if (record == NULL)
- {
- ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
- return false;
- }
-
- if( parse_value(elevel, record, value, &source, false, &newval) )
- {
- /* Mark record like presented in the config file. Be carefull if
- * you use this function for another purpose than config file
- * verification. It causes confusion configfile parser. */
- record->status |= GUC_IN_CONFFILE;
-
- if( isNewEqual != NULL)
- *isNewEqual = is_newvalue_equal(record, value);
- if( isContextOK != NULL)
- *isContextOK = checkContext(elevel, record, context);
- }
- else
- return false;
-
- return true;
-}
-
-
-/*
- * 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.
- *
- * If value is NULL, set the option to its default value. If the
- * parameter changeVal is false then don't really set the option but do all
- * the checks to see if it would work.
- *
- * 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(DEBUG) 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.
- *
- * See also SetConfigOption for an external interface.
- */
-bool
-set_config_option(const char *name, const char *value,
- GucContext context, GucSource source,
- bool isLocal, bool changeVal)
-{
- struct config_generic *record;
- int elevel;
- bool makeDefault;
-
- elevel = get_elevel(context, source);
-
- record = find_option(name, elevel);
- if (record == NULL)
- {
- ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\"", name)));
- return false;
- }
-
- /* Check if change is allowed in the running context. */
- if( !checkContext(elevel, record, context) )
- return false;
/*
* Should we set reset/stacked values? (If so, the behavior is not
- * transactional.)
+ * transactional.) This is done either when we get a default value from
+ * the database's/user's/client's default settings or when we reset a
+ * value to its default.
*/
- makeDefault = changeVal && (source <= PGC_S_OVERRIDE) && (value != NULL || source == PGC_S_FILE);
+ makeDefault = changeVal && (source <= PGC_S_OVERRIDE) &&
+ ((value != NULL) || source == PGC_S_DEFAULT);
/*
* Ignore attempted set if overridden by previously processed setting.
{
struct config_bool *conf = (struct config_bool *) record;
bool newval;
-
- if( !parse_value(elevel, record, value, &source, changeVal, (union config_var_value*) &newval) )
- return 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;
+ }
+ }
+ else if (source == PGC_S_DEFAULT)
+ newval = conf->boot_val;
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, changeVal, source))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": %d",
+ name, (int) newval)));
+ return false;
+ }
if (changeVal || makeDefault)
{
/* Save old value to support transaction abort */
if (!makeDefault)
- push_old_value(&conf->gen);
+ push_old_value(&conf->gen, action);
if (changeVal)
{
*conf->variable = newval;
{
if (stack->source <= source)
{
- stack->value.boolval = newval;
+ stack->prior.boolval = newval;
stack->source = source;
}
}
}
- else if (isLocal)
- {
- conf->gen.status |= GUC_HAVE_LOCAL;
- guc_dirty = true;
- }
- else
- {
- conf->tentative_val = newval;
- conf->gen.tentative_source = source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
- }
}
break;
}
struct config_int *conf = (struct config_int *) record;
int newval;
- if( !parse_value(elevel, record, value, &source, changeVal, (union config_var_value*) &newval) )
- return false;
+ 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(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;
+ }
+ }
+ else if (source == PGC_S_DEFAULT)
+ newval = conf->boot_val;
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, changeVal, source))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": %d",
+ name, newval)));
+ return false;
+ }
if (changeVal || makeDefault)
{
/* Save old value to support transaction abort */
if (!makeDefault)
- push_old_value(&conf->gen);
+ push_old_value(&conf->gen, action);
if (changeVal)
{
*conf->variable = newval;
{
if (stack->source <= source)
{
- stack->value.intval = newval;
+ stack->prior.intval = newval;
stack->source = source;
}
}
}
- else if (isLocal)
- {
- conf->gen.status |= GUC_HAVE_LOCAL;
- guc_dirty = true;
- }
- else
- {
- conf->tentative_val = newval;
- conf->gen.tentative_source = source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
- }
}
break;
}
struct config_real *conf = (struct config_real *) record;
double newval;
- if( !parse_value(elevel, record, value, &source, changeVal, (union config_var_value*) &newval) )
- return false;
+ 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;
+ }
+ }
+ else if (source == PGC_S_DEFAULT)
+ newval = conf->boot_val;
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, changeVal, source))
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": %g",
+ name, newval)));
+ return false;
+ }
if (changeVal || makeDefault)
{
/* Save old value to support transaction abort */
if (!makeDefault)
- push_old_value(&conf->gen);
+ push_old_value(&conf->gen, action);
if (changeVal)
{
*conf->variable = newval;
{
if (stack->source <= source)
{
- stack->value.realval = newval;
+ stack->prior.realval = newval;
stack->source = source;
}
}
}
- else if (isLocal)
+ }
+ break;
+ }
+
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+ char *newval;
+
+ if (value)
+ {
+ newval = guc_strdup(elevel, value);
+ if (newval == NULL)
+ return false;
+
+ /*
+ * The only sort of "parsing" check we need to do is apply
+ * truncation if GUC_IS_NAME.
+ */
+ if (conf->gen.flags & GUC_IS_NAME)
+ truncate_identifier(newval, strlen(newval), true);
+ }
+ else if (source == PGC_S_DEFAULT)
+ {
+ if (conf->boot_val == NULL)
+ newval = NULL;
+ else
+ {
+ newval = guc_strdup(elevel, conf->boot_val);
+ if (newval == NULL)
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * We could possibly avoid strdup here, but easier to make
+ * this case work the same as the normal assignment case;
+ * note the possible free of newval below.
+ */
+ if (conf->reset_val == NULL)
+ newval = NULL;
+ else
+ {
+ newval = guc_strdup(elevel, conf->reset_val);
+ if (newval == NULL)
+ return false;
+ }
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook && newval)
+ {
+ const char *hookresult;
+
+ /*
+ * If the hook ereports, we have to make sure we free
+ * newval, else it will be a permanent memory leak.
+ */
+ hookresult = call_string_assign_hook(conf->assign_hook,
+ newval,
+ changeVal,
+ source);
+ if (hookresult == NULL)
{
- conf->gen.status |= GUC_HAVE_LOCAL;
- guc_dirty = true;
+ free(newval);
+ ereport(elevel,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"",
+ name, value ? value : "")));
+ return false;
}
- else
+ else if (hookresult != newval)
{
- conf->tentative_val = newval;
- conf->gen.tentative_source = source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
+ free(newval);
+
+ /*
+ * Having to cast away const here is annoying, but the
+ * alternative is to declare assign_hooks as returning
+ * char*, which would mean they'd have to cast away
+ * const, or as both taking and returning char*, which
+ * doesn't seem attractive either --- we don't want
+ * them to scribble on the passed str.
+ */
+ newval = (char *) hookresult;
}
}
- break;
- }
-
- case PGC_STRING:
- {
- struct config_string *conf = (struct config_string *) record;
- char *newval;
-
- if( !parse_value(elevel, record, value, &source, changeVal, (union config_var_value*) &newval) )
- return false;
if (changeVal || makeDefault)
{
/* Save old value to support transaction abort */
if (!makeDefault)
- push_old_value(&conf->gen);
+ push_old_value(&conf->gen, action);
if (changeVal)
{
set_string_field(conf, conf->variable, newval);
{
if (stack->source <= source)
{
- set_string_field(conf, &stack->value.stringval,
+ set_string_field(conf, &stack->prior.stringval,
newval);
stack->source = source;
}
}
/* Perhaps we didn't install newval anywhere */
- if (!string_field_used(conf, newval))
+ if (newval && !string_field_used(conf, newval))
free(newval);
}
- else if (isLocal)
- {
- conf->gen.status |= GUC_HAVE_LOCAL;
- guc_dirty = true;
- }
- else
- {
- set_string_field(conf, &conf->tentative_val, newval);
- conf->gen.tentative_source = source;
- conf->gen.status |= GUC_HAVE_TENTATIVE;
- guc_dirty = true;
- }
}
- else
- if( newval != NULL )
- free(newval);
+ else if (newval)
+ free(newval);
break;
}
}
SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source)
{
- (void) set_config_option(name, value, context, source, false, true);
+ (void) set_config_option(name, value, context, source,
+ GUC_ACTION_SET, true);
}
struct config_generic *record;
static char buffer[256];
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
/*
* Get the RESET value associated with the given option.
+ *
+ * Note: this is not re-entrant, due to use of static result buffer;
+ * not to mention that a string variable could have its reset_val changed.
+ * Beware of assuming the result value is good for very long.
*/
const char *
GetConfigOptionResetString(const char *name)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
{
struct config_generic *record;
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
/* On an unrecognized name, don't error, just return false. */
if (record == NULL)
return false;
* We need to be told the name of the variable the args are for, because
* the flattening rules vary (ugh).
*
- * The result is NULL if input is NIL (ie, SET ... TO DEFAULT), otherwise
+ * The result is NULL if args is NIL (ie, SET ... TO DEFAULT), otherwise
* a palloc'd string.
*/
-char *
+static char *
flatten_set_variable_args(const char *name, List *args)
{
struct config_generic *record;
StringInfoData buf;
ListCell *l;
- /*
- * Fast path if just DEFAULT. We do not check the variable name in this
- * case --- necessary for RESET ALL to work correctly.
- */
+ /* Fast path if just DEFAULT */
if (args == NIL)
return NULL;
/* Else get flags for the variable */
- record = find_option(name, ERROR);
+ record = find_option(name, true, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
* to interval and back to normalize the value and account
* for any typmod.
*/
+ Oid typoid;
+ int32 typmod;
Datum interval;
char *intervalout;
+ typoid = typenameTypeId(NULL, arg->typename, &typmod);
+ Assert(typoid == INTERVALOID);
+
interval =
DirectFunctionCall3(interval_in,
CStringGetDatum(val),
ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(arg->typename->typmod));
+ Int32GetDatum(typmod));
intervalout =
DatumGetCString(DirectFunctionCall1(interval_out,
* SET command
*/
void
+ExecSetVariableStmt(VariableSetStmt *stmt)
+{
+ GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+
+ switch (stmt->kind)
+ {
+ 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);
+ break;
+ case VAR_SET_MULTI:
+
+ /*
+ * Special case for special SQL syntax that effectively sets more
+ * than one variable per statement.
+ */
+ if (strcmp(stmt->name, "TRANSACTION") == 0)
+ {
+ ListCell *head;
+
+ foreach(head, stmt->args)
+ {
+ DefElem *item = (DefElem *) lfirst(head);
+
+ if (strcmp(item->defname, "transaction_isolation") == 0)
+ SetPGVariable("transaction_isolation",
+ list_make1(item->arg), stmt->is_local);
+ else if (strcmp(item->defname, "transaction_read_only") == 0)
+ SetPGVariable("transaction_read_only",
+ list_make1(item->arg), stmt->is_local);
+ else
+ elog(ERROR, "unexpected SET TRANSACTION element: %s",
+ item->defname);
+ }
+ }
+ else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0)
+ {
+ ListCell *head;
+
+ foreach(head, stmt->args)
+ {
+ DefElem *item = (DefElem *) lfirst(head);
+
+ if (strcmp(item->defname, "transaction_isolation") == 0)
+ SetPGVariable("default_transaction_isolation",
+ list_make1(item->arg), stmt->is_local);
+ else if (strcmp(item->defname, "transaction_read_only") == 0)
+ SetPGVariable("default_transaction_read_only",
+ list_make1(item->arg), stmt->is_local);
+ else
+ elog(ERROR, "unexpected SET SESSION element: %s",
+ item->defname);
+ }
+ }
+ else
+ elog(ERROR, "unexpected SET MULTI element: %s",
+ stmt->name);
+ break;
+ case VAR_SET_DEFAULT:
+ case VAR_RESET:
+ set_config_option(stmt->name,
+ NULL,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ action,
+ true);
+ break;
+ case VAR_RESET_ALL:
+ ResetAllOptions();
+ break;
+ }
+}
+
+/*
+ * Get the value to assign for a VariableSetStmt, or NULL if it's RESET.
+ * The result is palloc'd.
+ *
+ * This is exported for use by actions such as ALTER ROLE SET.
+ */
+char *
+ExtractSetVariableArgs(VariableSetStmt *stmt)
+{
+ switch (stmt->kind)
+ {
+ case VAR_SET_VALUE:
+ return flatten_set_variable_args(stmt->name, stmt->args);
+ case VAR_SET_CURRENT:
+ return GetConfigOptionByName(stmt->name, NULL);
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * SetPGVariable - SET command exported as an easily-C-callable function.
+ *
+ * This provides access to SET TO value, as well as SET TO DEFAULT (expressed
+ * by passing args == NIL), but not SET FROM CURRENT functionality.
+ */
+void
SetPGVariable(const char *name, List *args, bool is_local)
{
char *argstring = flatten_set_variable_args(name, args);
argstring,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
- is_local,
+ is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
true);
}
value,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
- is_local,
+ is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
true);
/* get the new current value */
PG_RETURN_TEXT_P(result_text);
}
+
+/*
+ * Common code for DefineCustomXXXVariable subroutines: allocate the
+ * new variable's config struct and fill in generic fields.
+ */
+static struct config_generic *
+init_custom_variable(const char *name,
+ const char *short_desc,
+ const char *long_desc,
+ GucContext context,
+ enum config_type type,
+ size_t sz)
+{
+ struct config_generic *gen;
+
+ gen = (struct config_generic *) guc_malloc(ERROR, sz);
+ memset(gen, 0, sz);
+
+ gen->name = guc_strdup(ERROR, name);
+ gen->context = context;
+ gen->group = CUSTOM_OPTIONS;
+ gen->short_desc = short_desc;
+ gen->long_desc = long_desc;
+ gen->vartype = type;
+
+ return gen;
+}
+
+/*
+ * Common code for DefineCustomXXXVariable subroutines: insert the new
+ * variable into the GUC variable array, replacing any placeholder.
+ */
static void
define_custom_variable(struct config_generic * variable)
{
const char **nameAddr = &name;
const char *value;
struct config_string *pHolder;
- struct config_generic **res = (struct config_generic **) bsearch(
- (void *) &nameAddr,
- (void *) guc_variables,
- num_guc_variables,
- sizeof(struct config_generic *),
- guc_var_compare);
+ struct config_generic **res;
+ res = (struct config_generic **) bsearch((void *) &nameAddr,
+ (void *) guc_variables,
+ num_guc_variables,
+ sizeof(struct config_generic *),
+ guc_var_compare);
if (res == NULL)
{
+ /* No placeholder to replace, so just add it */
add_guc_variable(variable, ERROR);
return;
}
errmsg("attempt to redefine parameter \"%s\"", name)));
Assert((*res)->vartype == PGC_STRING);
- pHolder = (struct config_string *) * res;
+ pHolder = (struct config_string *) (*res);
- /* We have the same name, no sorting is necessary */
+ /*
+ * Replace the placeholder. We aren't changing the name, so no re-sorting
+ * is necessary
+ */
*res = variable;
- value = *pHolder->variable;
-
/*
* Assign the string value stored in the placeholder to the real variable.
*
* assignment, since we don't want it to roll back if the current xact
* fails later.
*/
- set_config_option(name, value,
- pHolder->gen.context, pHolder->gen.source,
- false, true);
+ value = *pHolder->variable;
+
+ if (value)
+ set_config_option(name, value,
+ pHolder->gen.context, pHolder->gen.source,
+ GUC_ACTION_SET, true);
/*
* Free up as much as we conveniently can of the placeholder structure
*/
set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL);
- set_string_field(pHolder, &pHolder->tentative_val, NULL);
free(pHolder);
}
-static void
-init_custom_variable(struct config_generic * gen,
- const char *name,
- const char *short_desc,
- const char *long_desc,
- GucContext context,
- enum config_type type)
-{
- gen->name = guc_strdup(ERROR, name);
- gen->context = context;
- gen->group = CUSTOM_OPTIONS;
- gen->short_desc = short_desc;
- gen->long_desc = long_desc;
- gen->vartype = type;
-}
-
void
DefineCustomBoolVariable(const char *name,
const char *short_desc,
GucBoolAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_bool);
- struct config_bool *var = (struct config_bool *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_BOOL);
+ struct config_bool *var;
+ var = (struct config_bool *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_BOOL, sizeof(struct config_bool));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->assign_hook = assign_hook;
var->show_hook = show_hook;
GucIntAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_int);
- struct config_int *var = (struct config_int *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_INT);
+ struct config_int *var;
+ var = (struct config_int *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_INT, sizeof(struct config_int));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->min = minValue;
var->max = maxValue;
GucRealAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_real);
- struct config_real *var = (struct config_real *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_REAL);
+ struct config_real *var;
+ var = (struct config_real *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_REAL, sizeof(struct config_real));
var->variable = valueAddr;
+ var->boot_val = *valueAddr;
var->reset_val = *valueAddr;
var->min = minValue;
var->max = maxValue;
GucStringAssignHook assign_hook,
GucShowHook show_hook)
{
- size_t sz = sizeof(struct config_string);
- struct config_string *var = (struct config_string *) guc_malloc(ERROR, sz);
-
- memset(var, 0, sz);
- init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_STRING);
+ struct config_string *var;
+ var = (struct config_string *)
+ init_custom_variable(name, short_desc, long_desc, context,
+ PGC_STRING, sizeof(struct config_string));
var->variable = valueAddr;
- var->reset_val = *valueAddr;
+ var->boot_val = *valueAddr;
+ /* we could probably do without strdup, but keep it like normal case */
+ if (var->boot_val)
+ var->reset_val = guc_strdup(ERROR, var->boot_val);
var->assign_hook = assign_hook;
var->show_hook = show_hook;
define_custom_variable(&var->gen);
void
GetPGVariable(const char *name, DestReceiver *dest)
{
- if (pg_strcasecmp(name, "all") == 0)
+ if (guc_name_compare(name, "all") == 0)
ShowAllGUCConfig(dest);
else
ShowGUCConfigOption(name, dest);
{
TupleDesc tupdesc;
- if (pg_strcasecmp(name, "all") == 0)
+ if (guc_name_compare(name, "all") == 0)
{
/* need a tuple descriptor representing three TEXT columns */
tupdesc = CreateTemplateTupleDesc(3, false);
return tupdesc;
}
-/*
- * RESET command
- */
-void
-ResetPGVariable(const char *name)
-{
- if (pg_strcasecmp(name, "all") == 0)
- ResetAllOptions();
- else
- set_config_option(name,
- NULL,
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION,
- false,
- true);
-}
-
/*
* SHOW command
{
struct config_generic *record;
- record = find_option(name, ERROR);
+ record = find_option(name, false, ERROR);
if (record == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
/* unit */
if (conf->vartype == PGC_INT)
{
- if (conf->flags & GUC_UNIT_KB)
- values[2] = "kB";
- else if (conf->flags & GUC_UNIT_BLOCKS)
- {
- static char buf[8];
+ static char buf[8];
- snprintf(buf, sizeof(buf), "%dkB", BLCKSZ/1024);
- values[2] = buf;
+ switch (conf->flags & (GUC_UNIT_MEMORY | GUC_UNIT_TIME))
+ {
+ case GUC_UNIT_KB:
+ values[2] = "kB";
+ break;
+ case GUC_UNIT_BLOCKS:
+ snprintf(buf, sizeof(buf), "%dkB", BLCKSZ / 1024);
+ values[2] = buf;
+ break;
+ case GUC_UNIT_XBLOCKS:
+ snprintf(buf, sizeof(buf), "%dkB", XLOG_BLCKSZ / 1024);
+ values[2] = buf;
+ break;
+ case GUC_UNIT_MS:
+ values[2] = "ms";
+ break;
+ case GUC_UNIT_S:
+ values[2] = "s";
+ break;
+ case GUC_UNIT_MIN:
+ values[2] = "min";
+ break;
+ default:
+ values[2] = "";
+ break;
}
- else if (conf->flags & GUC_UNIT_MS)
- values[2] = "ms";
- else if (conf->flags & GUC_UNIT_S)
- values[2] = "s";
- else if (conf->flags & GUC_UNIT_MIN)
- values[2] = "min";
- else
- values[2] = "";
}
else
values[2] = NULL;
val = (*conf->show_hook) ();
else
{
- char unit[4];
- int result = *conf->variable;
+ char unit[4];
+ int result = *conf->variable;
if (use_units && result > 0 && (record->flags & GUC_UNIT_MEMORY))
{
- if (record->flags & GUC_UNIT_BLOCKS)
- result *= BLCKSZ/1024;
+ switch (record->flags & GUC_UNIT_MEMORY)
+ {
+ case GUC_UNIT_BLOCKS:
+ result *= BLCKSZ / 1024;
+ break;
+ case GUC_UNIT_XBLOCKS:
+ result *= XLOG_BLCKSZ / 1024;
+ break;
+ }
if (result % KB_PER_GB == 0)
{
}
else if (use_units && result > 0 && (record->flags & GUC_UNIT_TIME))
{
- if (record->flags & GUC_UNIT_S)
- result = result * MS_PER_S;
- else if (record->flags & GUC_UNIT_MIN)
- result = result * MS_PER_MIN;
+ switch (record->flags & GUC_UNIT_TIME)
+ {
+ case GUC_UNIT_S:
+ result *= MS_PER_S;
+ break;
+ case GUC_UNIT_MIN:
+ result *= MS_PER_MIN;
+ break;
+ }
if (result % MS_PER_D == 0)
{
strcpy(unit, "");
snprintf(buffer, sizeof(buffer), "%d%s",
- (int)result, unit);
+ (int) result, unit);
val = buffer;
}
}
}
+/*
+ * Attempt (badly) to detect if a proposed new GUC setting is the same
+ * as the current value.
+ *
+ * XXX this does not really work because it doesn't account for the
+ * effects of canonicalization of string values by assign_hooks.
+ */
static bool
-is_newvalue_equal(struct config_generic *record, const char *newvalue)
+is_newvalue_equal(struct config_generic * record, const char *newvalue)
{
- if( !newvalue )
- return false;
-
+ /* newvalue == NULL isn't supported */
+ Assert(newvalue != NULL);
+
switch (record->vartype)
{
case PGC_BOOL:
- {
- struct config_bool *conf = (struct config_bool *) record;
- bool newval;
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+ bool newval;
- return parse_bool(newvalue, &newval) && *conf->variable == newval;
- }
+ return parse_bool(newvalue, &newval)
+ && *conf->variable == newval;
+ }
case PGC_INT:
- {
- struct config_int *conf = (struct config_int *) record;
- int newval;
+ {
+ struct config_int *conf = (struct config_int *) record;
+ int newval;
- return parse_int(newvalue, &newval, record->flags) && *conf->variable == newval;
- }
+ return parse_int(newvalue, &newval, record->flags, NULL)
+ && *conf->variable == newval;
+ }
case PGC_REAL:
- {
- struct config_real *conf = (struct config_real *) record;
- double newval;
+ {
+ struct config_real *conf = (struct config_real *) record;
+ double newval;
- return parse_real(newvalue, &newval) && *conf->variable == newval;
- }
+ return parse_real(newvalue, &newval)
+ && *conf->variable == newval;
+ }
case PGC_STRING:
- {
- struct config_string *conf = (struct config_string *) record;
+ {
+ struct config_string *conf = (struct config_string *) record;
- return strcmp(*conf->variable, newvalue) == 0;
- }
+ return *conf->variable != NULL &&
+ strcmp(*conf->variable, newvalue) == 0;
+ }
}
return false;
if ((varname = read_string_with_null(fp)) == NULL)
break;
- if ((record = find_option(varname, FATAL)) == NULL)
+ if ((record = find_option(varname, true, FATAL)) == NULL)
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");
elog(FATAL, "invalid format of exec config params file");
(void) set_config_option(varname, varvalue, record->context,
- varsource, false, true);
+ varsource, GUC_ACTION_SET, true);
free(varname);
free(varvalue);
}
if (string[equal_pos] == '=')
{
*name = guc_malloc(FATAL, equal_pos + 1);
- strncpy(*name, string, equal_pos);
- (*name)[equal_pos] = '\0';
+ strlcpy(*name, string, equal_pos + 1);
*value = guc_strdup(FATAL, &string[equal_pos + 1]);
}
/*
- * Handle options fetched from pg_database.datconfig or pg_authid.rolconfig.
+ * Handle options fetched from pg_database.datconfig, pg_authid.rolconfig,
+ * 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).
*/
void
-ProcessGUCArray(ArrayType *array, GucSource source)
+ProcessGUCArray(ArrayType *array,
+ GucContext context, GucSource source, GucAction action)
{
int i;
Assert(ARR_ELEMTYPE(array) == TEXTOID);
Assert(ARR_NDIM(array) == 1);
Assert(ARR_LBOUND(array)[0] == 1);
- Assert(source == PGC_S_DATABASE || source == PGC_S_USER);
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
{
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("could not parse setting for parameter \"%s\"", name)));
+ errmsg("could not parse setting for parameter \"%s\"",
+ name)));
free(name);
continue;
}
- /*
- * We process all these options at SUSET level. We assume that the
- * right to insert an option into pg_database or pg_authid was checked
- * when it was inserted.
- */
- SetConfigOption(name, value, PGC_SUSET, source);
+ (void) set_config_option(name, value, context, source, action, true);
free(name);
if (value)
/* test if the option is valid */
set_config_option(name, value,
superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_TEST, false, false);
+ PGC_S_TEST, GUC_ACTION_SET, false);
/* convert name to canonical spelling, so we can use plain strcmp */
(void) GetConfigOptionByName(name, &varname);
/* test if the option is valid */
set_config_option(name, NULL,
superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_TEST, false, false);
+ PGC_S_TEST, GUC_ACTION_SET, false);
/* convert name to canonical spelling, so we can use plain strcmp */
(void) GetConfigOptionByName(name, &varname);
/*
- * assign_hook subroutines
+ * assign_hook and show_hook subroutines
*/
static const char *
if (pg_strcasecmp(tok, "stderr") == 0)
newlogdest |= LOG_DESTINATION_STDERR;
+ else if (pg_strcasecmp(tok, "csvlog") == 0)
+ newlogdest |= LOG_DESTINATION_CSVLOG;
#ifdef HAVE_SYSLOG
else if (pg_strcasecmp(tok, "syslog") == 0)
newlogdest |= LOG_DESTINATION_SYSLOG;
}
static const char *
-assign_log_min_messages(const char *newval,
- bool doit, GucSource source)
+assign_session_replication_role(const char *newval, bool doit, GucSource source)
+{
+ int newrole;
+
+ if (pg_strcasecmp(newval, "origin") == 0)
+ newrole = SESSION_REPLICATION_ROLE_ORIGIN;
+ else if (pg_strcasecmp(newval, "replica") == 0)
+ newrole = SESSION_REPLICATION_ROLE_REPLICA;
+ else if (pg_strcasecmp(newval, "local") == 0)
+ newrole = SESSION_REPLICATION_ROLE_LOCAL;
+ else
+ return NULL;
+
+ /*
+ * Must flush the plan cache when changing replication role; but don't
+ * flush unnecessarily.
+ */
+ if (doit && SessionReplicationRole != newrole)
+ {
+ ResetPlanCache();
+ SessionReplicationRole = newrole;
+ }
+
+ return newval;
+}
+
+static const char *
+assign_log_min_messages(const char *newval, bool doit, GucSource source)
{
return (assign_msglvl(&log_min_messages, newval, doit, source));
}
{
/*
* Check syntax. newval must be a comma separated list of identifiers.
- * Whitespace is allowed but skipped.
+ * Whitespace is allowed but removed from the result.
*/
bool hasSpaceAfterToken = false;
const char *cp = newval;
int symLen = 0;
- int c;
+ char c;
StringInfoData buf;
initStringInfo(&buf);
- while ((c = *cp++) != 0)
+ while ((c = *cp++) != '\0')
{
- if (isspace(c))
+ if (isspace((unsigned char) c))
{
if (symLen > 0)
hasSpaceAfterToken = true;
if (c == ',')
{
- hasSpaceAfterToken = false;
- if (symLen > 0)
+ if (symLen > 0) /* terminate identifier */
{
- symLen = 0;
appendStringInfoChar(&buf, ',');
+ symLen = 0;
}
+ hasSpaceAfterToken = false;
continue;
}
- if (hasSpaceAfterToken || !isalnum(c))
+ if (hasSpaceAfterToken || !isalnum((unsigned char) c))
{
/*
* Syntax error due to token following space after token or non
* alpha numeric character
*/
- ereport(LOG,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid syntax for \"custom_variable_classes\": \"%s\"", newval)));
pfree(buf.data);
return NULL;
}
+ appendStringInfoChar(&buf, c);
symLen++;
- appendStringInfoChar(&buf, (char) c);
}
/* Remove stray ',' at end */
if (symLen == 0 && buf.len > 0)
buf.data[--buf.len] = '\0';
- if (buf.len == 0)
- newval = NULL;
- else if (doit)
- newval = strdup(buf.data);
+ /* GUC wants the result malloc'd */
+ newval = guc_strdup(LOG, buf.data);
pfree(buf.data);
return newval;
if (newval)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("assertion checking is not supported by this build")));
+ errmsg("assertion checking is not supported by this build")));
#endif
return true;
}
assign_backslash_quote(const char *newval, bool doit, GucSource source)
{
BackslashQuoteType bq;
- bool bqbool;
+ bool bqbool;
/*
- * Although only "on", "off", and "safe_encoding" are documented,
- * we use parse_bool so we can accept all the likely variants of
- * "on" and "off".
+ * Although only "on", "off", and "safe_encoding" are documented, we use
+ * parse_bool so we can accept all the likely variants of "on" and "off".
*/
if (pg_strcasecmp(newval, "safe_encoding") == 0)
bq = BACKSLASH_QUOTE_SAFE_ENCODING;
* The powerup value shown above for timezone_abbreviations is "UNKNOWN".
* When we see this we just do nothing. If this value isn't overridden
* from the config file then pg_timezone_abbrev_initialize() will
- * eventually replace it with "Default". This hack has two purposes:
- * to avoid wasting cycles loading values that might soon be overridden
- * from the config file, and to avoid trying to read the timezone abbrev
- * files during InitializeGUCOptions(). The latter doesn't work in an
- * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet and
- * so we can't locate PGSHAREDIR. (Essentially the same hack is used
- * to delay initializing TimeZone ... if we have any more, we should
- * try to clean up and centralize this mechanism ...)
+ * eventually replace it with "Default". This hack has two purposes: to
+ * avoid wasting cycles loading values that might soon be overridden from
+ * the config file, and to avoid trying to read the timezone abbrev files
+ * during InitializeGUCOptions(). The latter doesn't work in an
+ * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet and so
+ * we can't locate PGSHAREDIR. (Essentially the same hack is used to
+ * delay initializing TimeZone ... if we have any more, we should try to
+ * clean up and centralize this mechanism ...)
*/
if (strcmp(newval, "UNKNOWN") == 0)
{
if (timezone_abbreviations_string == NULL ||
strcmp(timezone_abbreviations_string, newval) != 0)
{
- int elevel;
+ int elevel;
/*
* If reading config file, only the postmaster should bleat loudly
- * about problems. Otherwise, it's just this one process doing it,
+ * about problems. Otherwise, it's just this one process doing it,
* and we use WARNING message level.
*/
if (source == PGC_S_FILE)
}
}
+static const char *
+assign_xmlbinary(const char *newval, bool doit, GucSource source)
+{
+ XmlBinaryType xb;
+
+ if (pg_strcasecmp(newval, "base64") == 0)
+ xb = XMLBINARY_BASE64;
+ else if (pg_strcasecmp(newval, "hex") == 0)
+ xb = XMLBINARY_HEX;
+ else
+ return NULL; /* reject */
+
+ if (doit)
+ xmlbinary = xb;
+
+ return newval;
+}
+
+static const char *
+assign_xmloption(const char *newval, bool doit, GucSource source)
+{
+ XmlOptionType xo;
+
+ if (pg_strcasecmp(newval, "document") == 0)
+ xo = XMLOPTION_DOCUMENT;
+ else if (pg_strcasecmp(newval, "content") == 0)
+ xo = XMLOPTION_CONTENT;
+ else
+ return NULL; /* reject */
+
+ if (doit)
+ xmloption = xo;
+
+ return newval;
+}
+
+static const char *
+show_archive_command(void)
+{
+ if (XLogArchiveMode)
+ return XLogArchiveCommand;
+ else
+ return "(disabled)";
+}
+
static bool
assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
{
return nbuf;
}
+static bool
+assign_maxconnections(int newval, bool doit, GucSource source)
+{
+ if (newval + autovacuum_max_workers > INT_MAX / 4)
+ return false;
+
+ if (doit)
+ MaxBackends = newval + autovacuum_max_workers;
+
+ return true;
+}
+
+static bool
+assign_autovacuum_max_workers(int newval, bool doit, GucSource source)
+{
+ if (newval + MaxConnections > INT_MAX / 4)
+ return false;
+
+ if (doit)
+ MaxBackends = newval + MaxConnections;
+
+ return true;
+}
#include "guc-file.c"