]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/misc/guc.c
Document and enforce that the usable range of setseed() arguments is
[postgresql] / src / backend / utils / misc / guc.c
index f801c060d7847add0e9f93774846342e8e16344d..49958cb4e7f3ab74c62a82ecf89b6fb80294fe11 100644 (file)
@@ -6,11 +6,11 @@
  * See src/backend/utils/misc/README for more information.
  *
  *
- * Copyright (c) 2000-2007, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.398 2007/06/18 10:02:57 mha Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.436 2008/03/10 12:39:23 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 #include <syslog.h>
 #endif
 
-
 #include "access/gin.h"
 #include "access/transam.h"
 #include "access/twophase.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"
 #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 */
@@ -104,6 +110,7 @@ extern int  CommitDelay;
 extern int     CommitSiblings;
 extern char *default_tablespace;
 extern char *temp_tablespaces;
+extern bool synchronize_seqscans;
 extern bool fullPageWrites;
 
 #ifdef TRACE_SORT
@@ -136,7 +143,7 @@ static const char *assign_syslog_ident(const char *ident,
 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);
+                                                               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,
@@ -163,7 +170,7 @@ static const char *assign_backslash_quote(const char *newval, bool doit, GucSour
 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);
@@ -202,7 +209,7 @@ bool                SQL_inheritance = true;
 bool           Password_encryption = true;
 
 int                    log_min_error_statement = ERROR;
-int                    log_min_messages = NOTICE;
+int                    log_min_messages = WARNING;
 int                    client_min_messages = NOTICE;
 int                    log_min_duration_statement = -1;
 int                    log_temp_files = -1;
@@ -249,6 +256,7 @@ 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;
@@ -415,9 +423,9 @@ const char *const config_type_names[] =
  * 4. Add a record below.
  *
  * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if
- *       appropriate
+ *       appropriate.
  *
- * 6. Don't forget to document the option.
+ * 6. Don't forget to document the option (at least in config.sgml).
  *
  * 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.
@@ -547,6 +555,14 @@ static struct config_bool ConfigureNamesBool[] =
                &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."),
@@ -581,6 +597,14 @@ static struct config_bool ConfigureNamesBool[] =
                &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."),
@@ -713,47 +737,23 @@ static struct config_bool ConfigureNamesBool[] =
                &Explain_pretty_print,
                true, NULL, NULL
        },
+
        {
-               {"stats_start_collector", PGC_POSTMASTER, STATS_COLLECTOR,
-                       gettext_noop("Starts the server statistics-collection subprocess."),
-                       NULL
-               },
-               &pgstat_collect_startcollector,
-               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
+               {"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_tuplelevel,
+               &pgstat_track_activities,
                true, 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
        },
 
@@ -825,13 +825,14 @@ static struct config_bool ConfigureNamesBool[] =
 #endif
 
        {
-               {"log_lock_waits", PGC_SIGHUP, LOGGING_WHAT,
-                       gettext_noop("Logs long lock wait events."),
+               {"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."),
@@ -943,11 +944,11 @@ static struct config_bool ConfigureNamesBool[] =
                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
        },
        {
@@ -1026,7 +1027,7 @@ static struct config_bool ConfigureNamesBool[] =
 
        {
                {"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,
@@ -1052,6 +1053,24 @@ static struct config_bool ConfigureNamesBool[] =
                false, NULL, NULL
        },
 
+       {
+               {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+                       gettext_noop("Enable synchronized sequential scans."),
+                       NULL
+               },
+               &synchronize_seqscans,
+               true, 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."),
@@ -1172,20 +1191,14 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &DeadlockTimeout,
-               1000, 0, INT_MAX, NULL, NULL
+               1000, 1, INT_MAX / 1000, NULL, NULL
        },
 
        /*
-        * Note: There is some postprocessing done in PostmasterMain() to make
-        * sure the buffers are at least twice the number of backends, so the
-        * constraints here are partially unused. Similarly, the superuser
-        * reserved number is checked to ensure it is less than the max backends
-        * number.
-        *
-        * MaxBackends is limited to INT_MAX/4 because some places compute
-        * 4*MaxBackends without any overflow check.  This check is made on
-        * assign_maxconnections, since MaxBackends is computed as MaxConnections +
-        * autovacuum_max_workers.
+        * Note: MaxBackends is limited to INT_MAX/4 because some places compute
+        * 4*MaxBackends without any overflow check.  This check is made in
+        * assign_maxconnections, since MaxBackends is computed as MaxConnections
+        * plus autovacuum_max_workers.
         *
         * Likewise we have to limit NBuffers to INT_MAX/2.
         */
@@ -1334,7 +1347,7 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &autovacuum_vac_cost_delay,
-               -1, -1, 1000, NULL, NULL
+               20, -1, 1000, NULL, NULL
        },
 
        {
@@ -1496,7 +1509,17 @@ static struct config_int ConfigureNamesInt[] =
        },
 
        {
-               {"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
@@ -1506,7 +1529,7 @@ static struct config_int ConfigureNamesInt[] =
        },
 
        {
-               {"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
@@ -1528,9 +1551,9 @@ static struct config_int ConfigureNamesInt[] =
 
        {
                {"log_min_duration_statement", PGC_SUSET, LOGGING_WHEN,
-                       gettext_noop("Sets the minimum execution time 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,
@@ -1538,13 +1561,13 @@ static struct config_int ConfigureNamesInt[] =
        },
 
        {
-               {"log_autovacuum", PGC_BACKEND, LOGGING_WHAT,
-                       gettext_noop("Sets the minimum execution time above which autovacuum actions "
-                                                "will be logged."),
-                       gettext_noop("Zero prints all actions.  The default is -1 (turning this feature off)."),
+               {"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,
+               &Log_autovacuum_min_duration,
                -1, -1, INT_MAX / 1000, NULL, NULL
        },
 
@@ -1564,16 +1587,7 @@ static struct config_int ConfigureNamesInt[] =
                        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
        },
 
        {
@@ -1651,7 +1665,7 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_vac_thresh,
-               500, 0, INT_MAX, NULL, NULL
+               50, 0, INT_MAX, NULL, NULL
        },
        {
                {"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
@@ -1659,7 +1673,7 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_anl_thresh,
-               250, 0, INT_MAX, NULL, NULL
+               50, 0, INT_MAX, NULL, NULL
        },
        {
                /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
@@ -1820,21 +1834,12 @@ static struct config_real ConfigureNamesReal[] =
        },
 
        {
-               {"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("Multiple of the average buffer usage to free 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
        },
 
        {
@@ -1844,7 +1849,7 @@ static struct config_real ConfigureNamesReal[] =
                        GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &phony_random_seed,
-               0.5, 0.0, 1.0, assign_random_seed, show_random_seed
+               0.0, -1.0, 1.0, assign_random_seed, show_random_seed
        },
 
        {
@@ -1864,6 +1869,15 @@ static struct config_real ConfigureNamesReal[] =
                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 */
        {
                {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL
@@ -1879,7 +1893,7 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &XLogArchiveCommand,
-               "", NULL, NULL
+               "", NULL, show_archive_command
        },
 
        {
@@ -1921,7 +1935,7 @@ static struct config_string ConfigureNamesString[] =
                                                 "includes all the levels that follow it.")
                },
                &log_min_messages_str,
-               "notice", assign_log_min_messages, NULL
+               "warning", assign_log_min_messages, NULL
        },
 
        {
@@ -1960,6 +1974,14 @@ static struct config_string ConfigureNamesString[] =
                "", 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,
@@ -2004,9 +2026,9 @@ static struct config_string ConfigureNamesString[] =
 
        {
                {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
-                       gettext_noop("Sets the sessions behavior for triggers and rewrite rules."),
+                       gettext_noop("Sets the session's behavior for triggers and rewrite rules."),
                        gettext_noop("Each session can be either"
-                                                " \"origin\", \"replica\" or \"local\".")
+                                                " \"origin\", \"replica\", or \"local\".")
                },
                &session_replication_role_string,
                "origin", assign_session_replication_role, NULL
@@ -2025,6 +2047,16 @@ static struct config_string ConfigureNamesString[] =
                "$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."),
@@ -2206,8 +2238,9 @@ static struct config_string ConfigureNamesString[] =
        {
                {"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,
@@ -2400,6 +2433,15 @@ static struct config_string ConfigureNamesString[] =
                "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,
@@ -2410,7 +2452,7 @@ static struct config_string ConfigureNamesString[] =
                &SSLCipherSuites,
                "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", NULL, NULL
        },
-#endif /* USE_SSL */
+#endif   /* USE_SSL */
 
        /* End-of-list marker */
        {
@@ -2451,10 +2493,12 @@ static bool guc_dirty;                  /* TRUE if need to do commit/abort work */
 
 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);
@@ -2523,13 +2567,12 @@ set_string_field(struct config_string * conf, char **field, char *newval)
        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;
        }
 
@@ -2547,19 +2590,71 @@ string_field_used(struct config_string * conf, char *strval)
 
        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)
 {
@@ -2644,40 +2739,6 @@ build_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.
@@ -2721,7 +2782,7 @@ add_guc_variable(struct config_generic * var, int elevel)
  * 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 *);
@@ -2731,9 +2792,8 @@ add_placeholder_variable(const char *name, int elevel)
        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)
@@ -2750,7 +2810,8 @@ add_placeholder_variable(const char *name, int elevel)
 
        /*
         * The char* is allocated at the end of the struct since we have no
-        * 'static' place to point to.
+        * '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);
 
@@ -2761,17 +2822,53 @@ add_placeholder_variable(const char *name, int elevel)
                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;
@@ -2798,17 +2895,21 @@ find_option(const char *name, int elevel)
        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;
@@ -2827,7 +2928,9 @@ guc_var_compare(const void *a, const void *b)
        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)
 {
@@ -2856,30 +2959,6 @@ guc_name_compare(const char *namea, const char *nameb)
 }
 
 
-static int
-guc_get_index(const char *name)
-{
-       const char **key = &name;
-       struct config_generic **res;
-
-       Assert(name);
-
-       /*
-        * By equating const char ** with struct config_generic *, we are assuming
-        * the name field is first in config_generic.
-        */
-       res = (struct config_generic **) bsearch((void *) &key,
-                                                                                        (void *) guc_variables,
-                                                                                        num_guc_variables,
-                                                                                        sizeof(struct config_generic *),
-                                                                                        guc_var_compare);
-       if (!res)
-               return -1;
-
-       return res - guc_variables;
-}
-
-
 /*
  * Initialize GUC options during program startup.
  *
@@ -2893,6 +2972,12 @@ InitializeGUCOptions(void)
        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.
         */
@@ -2908,7 +2993,6 @@ InitializeGUCOptions(void)
 
                gconf->status = 0;
                gconf->reset_source = PGC_S_DEFAULT;
-               gconf->tentative_source = PGC_S_DEFAULT;
                gconf->source = PGC_S_DEFAULT;
                gconf->stack = NULL;
 
@@ -2961,11 +3045,10 @@ InitializeGUCOptions(void)
 
                                        *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;
                                        }
 
@@ -3015,7 +3098,8 @@ InitializeGUCOptions(void)
 
        /*
         * 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");
@@ -3038,11 +3122,11 @@ InitializeGUCOptions(void)
        stack_rlimit = get_stack_depth_rlimit();
        if (stack_rlimit > 0)
        {
-               int             new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L;
+               int                     new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L;
 
                if (new_limit > 100)
                {
-                       char    limbuf[16];
+                       char            limbuf[16];
 
                        new_limit = Min(new_limit, 2048);
                        sprintf(limbuf, "%d", new_limit);
@@ -3226,7 +3310,7 @@ ResetAllOptions(void)
                        continue;
 
                /* Save old value to support transaction abort */
-               push_old_value(gconf);
+               push_old_value(gconf, GUC_ACTION_SET);
 
                switch (gconf->vartype)
                {
@@ -3239,11 +3323,7 @@ ResetAllOptions(void)
                                                                                                   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:
@@ -3255,11 +3335,7 @@ ResetAllOptions(void)
                                                                                                   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:
@@ -3271,11 +3347,7 @@ ResetAllOptions(void)
                                                                                                   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:
@@ -3283,16 +3355,10 @@ ResetAllOptions(void)
                                        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;
 
@@ -3310,11 +3376,7 @@ ResetAllOptions(void)
                                        }
 
                                        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;
                                }
                }
@@ -3327,339 +3389,368 @@ ResetAllOptions(void)
 
 /*
  * 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 transaction or subtransaction commit or abort.
+ * Do GUC processing at main transaction start.
  */
 void
-AtEOXact_GUC(bool isCommit, bool isSubXact)
+AtStart_GUC(void)
 {
-       int                     my_level;
-       int                     i;
-
-       /* Quick exit if nothing's changed in this transaction */
+       /*
+        * 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, 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, int nestLevel)
+{
+       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;
-               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.
-                */
+                       GucStack   *prev = stack->prev;
+                       bool            restorePrior = false;
+                       bool            restoreMasked = false;
+                       bool            changed;
 
-               useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0;
-               changed = false;
-
-               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;
+                       }
 
-               /*
-                * 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;
-               }
+                       /* Finish popping the state stack */
+                       gconf->stack = prev;
+                       pfree(stack);
 
-               /* Report new value if we changed it */
-               if (changed && (gconf->flags & GUC_REPORT))
-                       ReportGUCOption(gconf);
+                       /* Report new value if we changed it */
+                       if (changed && (gconf->flags & GUC_REPORT))
+                               ReportGUCOption(gconf);
+               }                                               /* end of stack-popping loop */
+
+               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;
 }
 
 
@@ -3715,9 +3806,9 @@ ReportGUCOption(struct config_generic * record)
 
 /*
  * 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)
@@ -3782,124 +3873,209 @@ parse_bool(const char *value, bool *result)
 
 /*
  * 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)
+       if (endptr == value)
+               return false;                   /* no HINT for integer syntax error */
+
+       if (errno == ERANGE || val != (int64) ((int32) val))
        {
-               bool            used = false;
+               if (hintmsg)
+                       *hintmsg = gettext_noop("Value exceeds integer range.");
+               return false;
+       }
 
-               while (*endptr == ' ')
-                       endptr++;
+       /* allow whitespace between integer and unit */
+       while (isspace((unsigned char) *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)
+       /* Handle possible unit */
+       if (*endptr != '\0')
+       {
+               /*
+                * 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)
                {
-                       val *= KB_PER_GB;
-                       used = true;
-                       endptr += 2;
-               }
+                       /* 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\".");
 
-#if BLCKSZ < 1024
-#error BLCKSZ must be >= 1024
+#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 (used)
-               {
-                       switch (flags & GUC_UNIT_MEMORY)
+                       if (strncmp(endptr, "kB", 2) == 0)
                        {
-                               case GUC_UNIT_BLOCKS:
-                                       val /= (BLCKSZ / 1024);
-                                       break;
-                               case GUC_UNIT_XBLOCKS:
-                                       val /= (XLOG_BLCKSZ / 1024);
-                                       break;
+                               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 (flags & GUC_UNIT_TIME)
+               {
+                       /* Set hint for use if no match or trailing garbage */
+                       if (hintmsg)
+                               *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\".");
 
-       if ((flags & GUC_UNIT_TIME) && endptr != value)
-       {
-               bool            used = false;
+                       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;
+                               }
+                       }
+               }
 
-               while (*endptr == ' ')
+               /* allow whitespace after unit */
+               while (isspace((unsigned char) *endptr))
                        endptr++;
 
-               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;
-               }
-               else if (strcmp(endptr, "h") == 0)
-               {
-                       val *= MS_PER_H;
-                       used = true;
-                       endptr += 1;
-               }
-               else if (strcmp(endptr, "d") == 0)
-               {
-                       val *= MS_PER_D;
-                       used = true;
-                       endptr += 1;
-               }
+               if (*endptr != '\0')
+                       return false;           /* appropriate hint, if any, already set */
 
-               if (used)
+               /* Check for overflow due to units conversion */
+               if (val != (int64) ((int32) val))
                {
-                       switch (flags & GUC_UNIT_TIME)
-                       {
-                               case GUC_UNIT_S:
-                                       val /= MS_PER_S;
-                                       break;
-                               case GUC_UNIT_MIN:
-                                       val /= MS_PER_MIN;
-                                       break;
-                       }
+                       if (hintmsg)
+                               *hintmsg = gettext_noop("Value exceeds integer range.");
+                       return false;
                }
        }
 
-       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;
@@ -3908,9 +4084,9 @@ parse_int(const char *value, int *result, int flags)
 
 
 /*
- * 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)
@@ -3918,14 +4094,20 @@ 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;
@@ -3967,14 +4149,19 @@ call_string_assign_hook(GucStringAssignHook assign_hook,
  * 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
+ * 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
+ * In that case we write a suitable error message via ereport(LOG) and
  * return false. This is working around the deficiencies in the ereport
  * mechanism, so don't blame me.  In all other cases, the function
  * returns true, including cases where the input is valid but we chose
@@ -3985,7 +4172,7 @@ call_string_assign_hook(GucStringAssignHook assign_hook,
 bool
 set_config_option(const char *name, const char *value,
                                  GucContext context, GucSource source,
-                                 bool isLocal, bool changeVal)
+                                 GucAction action, bool changeVal)
 {
        struct config_generic *record;
        int                     elevel;
@@ -3997,14 +4184,14 @@ set_config_option(const char *name, const char *value,
                 * To avoid cluttering the log, only the postmaster bleats loudly
                 * about problems with the config file.
                 */
-               elevel = IsUnderPostmaster ? DEBUG2 : LOG;
+               elevel = IsUnderPostmaster ? DEBUG3 : LOG;
        }
        else if (source == PGC_S_DATABASE || source == PGC_S_USER)
                elevel = INFO;
        else
                elevel = ERROR;
 
-       record = find_option(name, elevel);
+       record = find_option(name, true, elevel);
        if (record == NULL)
        {
                ereport(elevel,
@@ -4014,11 +4201,13 @@ set_config_option(const char *name, const char *value,
        }
 
        /*
-        * Do not replace a value that has been set on the command line by a SIGHUP
-        * reload
+        * 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 (context == PGC_SIGHUP && record->source == PGC_S_ARGV)
-               return true;
+       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
@@ -4042,12 +4231,17 @@ set_config_option(const char *name, const char *value,
                case PGC_POSTMASTER:
                        if (context == PGC_SIGHUP)
                        {
+                               /*
+                                * 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)
@@ -4115,27 +4309,22 @@ set_config_option(const char *name, const char *value,
        }
 
        /*
-        * Should we set reset/stacked values?  (If so, the behavior is not
-        * 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.
+        * Should we set reset/stacked values?  (If so, the behavior is not
+        * 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_DEFAULT);
+       makeDefault = changeVal && (source <= PGC_S_OVERRIDE) &&
+               ((value != NULL) || source == PGC_S_DEFAULT);
 
        /*
-        * Ignore attempted set if overridden by previously processed
-        * setting.  However, if changeVal is false then plow ahead anyway
-        * since we are trying to find out if the value is potentially
-        * good, not actually use it.  Also keep going if makeDefault is
-        * true, since we may want to set the reset/stacked values even if
-        * we can't set the variable itself.  There's one exception to
-        * this rule: if we want to apply the default value to variables
-        * that were removed from the configuration file.  This is
-        * indicated by source == PGC_S_DEFAULT and context == PGC_SIGHUP.
+        * Ignore attempted set if overridden by previously processed setting.
+        * However, if changeVal is false then plow ahead anyway since we are
+        * trying to find out if the value is potentially good, not actually use
+        * it. Also keep going if makeDefault is true, since we may want to set
+        * the reset/stacked values even if we can't set the variable itself.
         */
-       if (record->source > source
-               && !(source == PGC_S_DEFAULT && context == PGC_SIGHUP))
+       if (record->source > source)
        {
                if (changeVal && !makeDefault)
                {
@@ -4167,14 +4356,8 @@ set_config_option(const char *name, const char *value,
                                                return false;
                                        }
                                }
-                               /*
-                                * If value == NULL and source == PGC_S_DEFAULT then
-                                * we reset some value to its default (removed from
-                                * configuration file).
-                                */
                                else if (source == PGC_S_DEFAULT)
                                        newval = conf->boot_val;
-                               /* else we handle a "RESET varname" command */
                                else
                                {
                                        newval = conf->reset_val;
@@ -4195,7 +4378,7 @@ set_config_option(const char *name, const char *value,
                                {
                                        /* 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;
@@ -4214,23 +4397,11 @@ set_config_option(const char *name, const char *value,
                                                {
                                                        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;
                        }
@@ -4242,12 +4413,15 @@ set_config_option(const char *name, const char *value,
 
                                if (value)
                                {
-                                       if (!parse_int(value, &newval, conf->gen.flags))
+                                       const char *hintmsg;
+
+                                       if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
                                        {
                                                ereport(elevel,
                                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("parameter \"%s\" requires an integer value",
-                                                               name)));
+                                                errmsg("invalid value for parameter \"%s\": \"%s\"",
+                                                               name, value),
+                                                                hintmsg ? errhint(hintmsg) : 0));
                                                return false;
                                        }
                                        if (newval < conf->min || newval > conf->max)
@@ -4259,14 +4433,8 @@ set_config_option(const char *name, const char *value,
                                                return false;
                                        }
                                }
-                               /*
-                                * If value == NULL and source == PGC_S_DEFAULT then
-                                * we reset some value to its default (removed from
-                                * configuration file).
-                                */
                                else if (source == PGC_S_DEFAULT)
                                        newval = conf->boot_val;
-                               /* else we handle a "RESET varname" command */
                                else
                                {
                                        newval = conf->reset_val;
@@ -4287,7 +4455,7 @@ set_config_option(const char *name, const char *value,
                                {
                                        /* 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;
@@ -4306,23 +4474,11 @@ set_config_option(const char *name, const char *value,
                                                {
                                                        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;
                        }
@@ -4351,14 +4507,8 @@ set_config_option(const char *name, const char *value,
                                                return false;
                                        }
                                }
-                               /*
-                                * If value == NULL and source == PGC_S_DEFAULT then
-                                * we reset some value to its default (removed from
-                                * configuration file).
-                                */
                                else if (source == PGC_S_DEFAULT)
                                        newval = conf->boot_val;
-                               /* else we handle a "RESET varname" command */
                                else
                                {
                                        newval = conf->reset_val;
@@ -4379,7 +4529,7 @@ set_config_option(const char *name, const char *value,
                                {
                                        /* 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;
@@ -4398,23 +4548,11 @@ set_config_option(const char *name, const char *value,
                                                {
                                                        if (stack->source <= source)
                                                        {
-                                                               stack->value.realval = newval;
+                                                               stack->prior.realval = 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;
                        }
@@ -4437,11 +4575,6 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.flags & GUC_IS_NAME)
                                                truncate_identifier(newval, strlen(newval), true);
                                }
-                               /*
-                                * If value == NULL and source == PGC_S_DEFAULT then
-                                * we reset some value to its default (removed from
-                                * configuration file).
-                                */
                                else if (source == PGC_S_DEFAULT)
                                {
                                        if (conf->boot_val == NULL)
@@ -4453,25 +4586,25 @@ set_config_option(const char *name, const char *value,
                                                        return false;
                                        }
                                }
-                               /* else we handle a "RESET varname" command */
-                               else if (conf->reset_val)
+                               else
                                {
                                        /*
                                         * We could possibly avoid strdup here, but easier to make
-                                        * this case work the same as the normal assignment case.
+                                        * this case work the same as the normal assignment case;
+                                        * note the possible free of newval below.
                                         */
-                                       newval = guc_strdup(elevel, conf->reset_val);
-                                       if (newval == NULL)
-                                               return false;
+                                       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;
                                }
-                               else
-                               {
-                                       /* Nothing to reset to, as yet; so do nothing */
-                                       break;
-                               }
 
-                               if (conf->assign_hook)
+                               if (conf->assign_hook && newval)
                                {
                                        const char *hookresult;
 
@@ -4512,7 +4645,7 @@ set_config_option(const char *name, const char *value,
                                {
                                        /* 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);
@@ -4531,29 +4664,17 @@ set_config_option(const char *name, const char *value,
                                                {
                                                        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
+                               else if (newval)
                                        free(newval);
                                break;
                        }
@@ -4575,7 +4696,8 @@ void
 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);
 }
 
 
@@ -4593,7 +4715,7 @@ GetConfigOption(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),
@@ -4626,6 +4748,10 @@ GetConfigOption(const char *name)
 
 /*
  * 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)
@@ -4633,7 +4759,7 @@ 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),
@@ -4673,7 +4799,7 @@ IsSuperuserConfigOption(const char *name)
 {
        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;
@@ -4681,6 +4807,51 @@ IsSuperuserConfigOption(const char *name)
 }
 
 
+/*
+ * GUC_complaint_elevel
+ *             Get the ereport error level to use in an assign_hook's error report.
+ *
+ * This should be used by assign hooks that want to emit a custom error
+ * report (in addition to the generic "invalid value for option FOO" that
+ * guc.c will provide).  Note that the result might be ERROR or a lower
+ * level, so the caller must be prepared for control to return from ereport,
+ * or not.  If control does return, return false/NULL from the hook function.
+ *
+ * At some point it'd be nice to replace this with a mechanism that allows
+ * the custom message to become the DETAIL line of guc.c's generic message.
+ */
+int
+GUC_complaint_elevel(GucSource source)
+{
+       int                     elevel;
+
+       if (source == PGC_S_FILE)
+       {
+               /*
+                * To avoid cluttering the log, only the postmaster bleats loudly
+                * about problems with the config file.
+                */
+               elevel = IsUnderPostmaster ? DEBUG3 : LOG;
+       }
+       else if (source == PGC_S_OVERRIDE)
+       {
+               /*
+                * If we're a postmaster child, this is probably "undo" during
+                * transaction abort, so we don't want to clutter the log.  There's
+                * a small chance of a real problem with an OVERRIDE setting,
+                * though, so suppressing the message entirely wouldn't be desirable.
+                */
+               elevel = IsUnderPostmaster ? DEBUG5 : LOG;
+       }
+       else if (source < PGC_S_INTERACTIVE)
+               elevel = LOG;
+       else
+               elevel = ERROR;
+
+       return elevel;
+}
+
+
 /*
  * flatten_set_variable_args
  *             Given a parsenode List as emitted by the grammar for SET,
@@ -4689,10 +4860,10 @@ IsSuperuserConfigOption(const char *name)
  * 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;
@@ -4700,15 +4871,12 @@ flatten_set_variable_args(const char *name, List *args)
        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),
@@ -4754,11 +4922,13 @@ flatten_set_variable_args(const char *name, List *args)
                                         * to interval and back to normalize the value and account
                                         * for any typmod.
                                         */
+                                       Oid                     typoid;
                                        int32           typmod;
                                        Datum           interval;
                                        char       *intervalout;
 
-                                       typmod = typenameTypeMod(NULL, arg->typename, INTERVALOID);
+                                       typoid = typenameTypeId(NULL, arg->typename, &typmod);
+                                       Assert(typoid == INTERVALOID);
 
                                        interval =
                                                DirectFunctionCall3(interval_in,
@@ -4798,6 +4968,111 @@ flatten_set_variable_args(const char *name, List *args)
  * 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);
@@ -4807,7 +5082,7 @@ SetPGVariable(const char *name, List *args, bool is_local)
                                          argstring,
                                          (superuser() ? PGC_SUSET : PGC_USERSET),
                                          PGC_S_SESSION,
-                                         is_local,
+                                         is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
                                          true);
 }
 
@@ -4851,7 +5126,7 @@ set_config_by_name(PG_FUNCTION_ARGS)
                                          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 */
@@ -4864,6 +5139,38 @@ set_config_by_name(PG_FUNCTION_ARGS)
        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)
 {
@@ -4871,15 +5178,16 @@ 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;
        }
@@ -4893,13 +5201,14 @@ define_custom_variable(struct config_generic * variable)
                                 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.
         *
@@ -4907,9 +5216,12 @@ define_custom_variable(struct config_generic * 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
@@ -4917,27 +5229,10 @@ define_custom_variable(struct config_generic * variable)
         */
        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,
@@ -4947,13 +5242,13 @@ DefineCustomBoolVariable(const char *name,
                                                 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;
@@ -4971,13 +5266,13 @@ DefineCustomIntVariable(const char *name,
                                                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;
@@ -4997,13 +5292,13 @@ DefineCustomRealVariable(const char *name,
                                                 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;
@@ -5021,14 +5316,16 @@ DefineCustomStringVariable(const char *name,
                                                   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);
@@ -5064,7 +5361,7 @@ EmitWarningsOnPlaceholders(const char *className)
 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);
@@ -5075,7 +5372,7 @@ GetPGVariableResultDesc(const char *name)
 {
        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);
@@ -5102,23 +5399,6 @@ GetPGVariableResultDesc(const char *name)
        return tupdesc;
 }
 
-/*
- * RESET command
- */
-void
-ResetPGVariable(const char *name, bool isTopLevel)
-{
-       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
@@ -5206,7 +5486,7 @@ GetConfigOptionByName(const char *name, const char **varname)
 {
        struct config_generic *record;
 
-       record = find_option(name, ERROR);
+       record = find_option(name, false, ERROR);
        if (record == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -5663,9 +5943,19 @@ _ShowOption(struct config_generic * record, bool use_units)
 }
 
 
+/*
+ * 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)
 {
+       /* newvalue == NULL isn't supported */
+       Assert(newvalue != NULL);
+
        switch (record->vartype)
        {
                case PGC_BOOL:
@@ -5673,27 +5963,31 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue)
                                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;
 
-                               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;
 
-                               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;
 
-                               return strcmp(*conf->variable, newvalue) == 0;
+                               return *conf->variable != NULL &&
+                                       strcmp(*conf->variable, newvalue) == 0;
                        }
        }
 
@@ -5873,7 +6167,7 @@ read_nondefault_variables(void)
                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");
@@ -5881,7 +6175,7 @@ read_nondefault_variables(void)
                        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);
        }
@@ -5931,11 +6225,14 @@ ParseLongOption(const char *string, char **name, char **value)
 
 
 /*
- * 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;
 
@@ -5943,7 +6240,6 @@ ProcessGUCArray(ArrayType *array, GucSource source)
        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++)
        {
@@ -5970,17 +6266,13 @@ ProcessGUCArray(ArrayType *array, GucSource source)
                {
                        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)
@@ -6007,7 +6299,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *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);
@@ -6085,7 +6377,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
        /* 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);
@@ -6143,7 +6435,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
 
 
 /*
- * assign_hook subroutines
+ * assign_hook and show_hook subroutines
  */
 
 static const char *
@@ -6163,9 +6455,8 @@ assign_log_destination(const char *value, bool doit, GucSource source)
                /* syntax error in list */
                pfree(rawstring);
                list_free(elemlist);
-               if (source >= PGC_S_INTERACTIVE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+               ereport(GUC_complaint_elevel(source),
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                        errmsg("invalid list syntax for parameter \"log_destination\"")));
                return NULL;
        }
@@ -6176,6 +6467,8 @@ assign_log_destination(const char *value, bool doit, GucSource source)
 
                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;
@@ -6186,9 +6479,8 @@ assign_log_destination(const char *value, bool doit, GucSource source)
 #endif
                else
                {
-                       if (source >= PGC_S_INTERACTIVE)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                       ereport(GUC_complaint_elevel(source),
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                  errmsg("unrecognized \"log_destination\" key word: \"%s\"",
                                                 tok)));
                        pfree(rawstring);
@@ -6284,7 +6576,7 @@ assign_defaultxactisolevel(const char *newval, bool doit, GucSource source)
 static const char *
 assign_session_replication_role(const char *newval, bool doit, GucSource source)
 {
-       int             newrole;
+       int                     newrole;
 
        if (pg_strcasecmp(newval, "origin") == 0)
                newrole = SESSION_REPLICATION_ROLE_ORIGIN;
@@ -6309,8 +6601,7 @@ 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)
+assign_log_min_messages(const char *newval, bool doit, GucSource source)
 {
        return (assign_msglvl(&log_min_messages, newval, doit, source));
 }
@@ -6475,10 +6766,9 @@ assign_phony_autocommit(bool newval, bool doit, GucSource source)
 {
        if (!newval)
        {
-               if (doit && source >= PGC_S_INTERACTIVE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("SET AUTOCOMMIT TO OFF is no longer supported")));
+               ereport(GUC_complaint_elevel(source),
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("SET AUTOCOMMIT TO OFF is no longer supported")));
                return false;
        }
        return true;
@@ -6489,23 +6779,16 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource 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;
 
-       /*
-        * Resetting custom_variable_classes by removing it from the
-        * configuration file will lead to newval = NULL
-        */
-       if (newval == NULL)
-               return guc_strdup(ERROR, "");
-
        initStringInfo(&buf);
-       while ((c = *cp++) != 0)
+       while ((c = *cp++) != '\0')
        {
                if (isspace((unsigned char) c))
                {
@@ -6516,12 +6799,12 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
 
                if (c == ',')
                {
-                       hasSpaceAfterToken = false;
-                       if (symLen > 0)
+                       if (symLen > 0)         /* terminate identifier */
                        {
-                               symLen = 0;
                                appendStringInfoChar(&buf, ',');
+                               symLen = 0;
                        }
+                       hasSpaceAfterToken = false;
                        continue;
                }
 
@@ -6531,24 +6814,19 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
                         * 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 = guc_strdup(ERROR, buf.data);
+       /* GUC wants the result malloc'd */
+       newval = guc_strdup(LOG, buf.data);
 
        pfree(buf.data);
        return newval;
@@ -6559,9 +6837,12 @@ assign_debug_assertions(bool newval, bool doit, GucSource source)
 {
 #ifndef USE_ASSERT_CHECKING
        if (newval)
-               ereport(ERROR,
+       {
+               ereport(GUC_complaint_elevel(source),
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                           errmsg("assertion checking is not supported by this build")));
+               return false;
+       }
 #endif
        return true;
 }
@@ -6571,9 +6852,12 @@ assign_ssl(bool newval, bool doit, GucSource source)
 {
 #ifndef USE_SSL
        if (newval)
-               ereport(ERROR,
+       {
+               ereport(GUC_complaint_elevel(source),
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("SSL is not supported by this build")));
+               return false;
+       }
 #endif
        return true;
 }
@@ -6583,12 +6867,11 @@ assign_stage_log_stats(bool newval, bool doit, GucSource source)
 {
        if (newval && log_statement_stats)
        {
-               if (source >= PGC_S_INTERACTIVE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot enable parameter when \"log_statement_stats\" is true")));
+               ereport(GUC_complaint_elevel(source),
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("cannot enable parameter when \"log_statement_stats\" is true")));
                /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
-               else if (source != PGC_S_OVERRIDE)
+               if (source != PGC_S_OVERRIDE)
                        return false;
        }
        return true;
@@ -6600,14 +6883,13 @@ assign_log_stats(bool newval, bool doit, GucSource source)
        if (newval &&
                (log_parser_stats || log_planner_stats || log_executor_stats))
        {
-               if (source >= PGC_S_INTERACTIVE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot enable \"log_statement_stats\" when "
-                                                       "\"log_parser_stats\", \"log_planner_stats\", "
-                                                       "or \"log_executor_stats\" is true")));
+               ereport(GUC_complaint_elevel(source),
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("cannot enable \"log_statement_stats\" when "
+                                               "\"log_parser_stats\", \"log_planner_stats\", "
+                                               "or \"log_executor_stats\" is true")));
                /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
-               else if (source != PGC_S_OVERRIDE)
+               if (source != PGC_S_OVERRIDE)
                        return false;
        }
        return true;
@@ -6619,12 +6901,11 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source)
        /* Can't go to r/w mode inside a r/o transaction */
        if (newval == false && XactReadOnly && IsSubTransaction())
        {
-               if (source >= PGC_S_INTERACTIVE)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot set transaction read-write mode inside a read-only transaction")));
+               ereport(GUC_complaint_elevel(source),
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("cannot set transaction read-write mode inside a read-only transaction")));
                /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
-               else if (source != PGC_S_OVERRIDE)
+               if (source != PGC_S_OVERRIDE)
                        return false;
        }
        return true;
@@ -6702,7 +6983,7 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source)
                 * and we use WARNING message level.
                 */
                if (source == PGC_S_FILE)
-                       elevel = IsUnderPostmaster ? DEBUG2 : LOG;
+                       elevel = IsUnderPostmaster ? DEBUG3 : LOG;
                else
                        elevel = WARNING;
                if (!load_tzoffsets(newval, doit, elevel))
@@ -6763,6 +7044,15 @@ assign_xmloption(const char *newval, bool doit, GucSource source)
        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)
 {
@@ -6820,13 +7110,11 @@ show_tcp_keepalives_count(void)
 static bool
 assign_maxconnections(int newval, bool doit, GucSource source)
 {
-       if (doit)
-       {
-               if (newval + autovacuum_max_workers > INT_MAX / 4)
-                       return false;
+       if (newval + autovacuum_max_workers > INT_MAX / 4)
+               return false;
 
+       if (doit)
                MaxBackends = newval + autovacuum_max_workers;
-       }
 
        return true;
 }
@@ -6834,13 +7122,11 @@ assign_maxconnections(int newval, bool doit, GucSource source)
 static bool
 assign_autovacuum_max_workers(int newval, bool doit, GucSource source)
 {
-       if (doit)
-       {
-               if (newval + MaxConnections > INT_MAX / 4)
-                       return false;
+       if (newval + MaxConnections > INT_MAX / 4)
+               return false;
 
+       if (doit)
                MaxBackends = newval + MaxConnections;
-       }
 
        return true;
 }