]> 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 849aa70d9d56010faf28399b0e6307313ad7980a..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.418 2007/09/10 01:39:19 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.436 2008/03/10 12:39:23 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -110,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
@@ -142,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,
@@ -169,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);
@@ -208,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;
@@ -422,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.
@@ -560,8 +561,7 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &XactSyncCommit,
-               /* XXX TEMPORARY FOR TESTING: sync commit should default to TRUE! */
-               false, NULL, NULL
+               true, NULL, NULL
        },
        {
                {"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -737,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
+               {"track_activities", PGC_SUSET, STATS_COLLECTOR,
+                       gettext_noop("Collects information about executing commands."),
+                       gettext_noop("Enables the collection of information on the currently "
+                                                "executing command of each session, along with "
+                                                "the time at which that command began execution.")
                },
-               &pgstat_collect_startcollector,
+               &pgstat_track_activities,
                true, NULL, NULL
        },
        {
-               {"stats_reset_on_server_start", PGC_POSTMASTER, STATS_COLLECTOR,
-                       gettext_noop("Zeroes collected statistics on server restart."),
-                       NULL
-               },
-               &pgstat_collect_resetonpmstart,
-               false, NULL, NULL
-       },
-       {
-               {"stats_row_level", PGC_SUSET, STATS_COLLECTOR,
-                       gettext_noop("Collects row-level statistics on database activity."),
+               {"track_counts", PGC_SUSET, STATS_COLLECTOR,
+                       gettext_noop("Collects statistics on database activity."),
                        NULL
                },
-               &pgstat_collect_tuplelevel,
-               true, NULL, NULL
-       },
-       {
-               {"stats_block_level", PGC_SUSET, STATS_COLLECTOR,
-                       gettext_noop("Collects block-level 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
        },
 
@@ -1077,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."),
@@ -1197,20 +1191,14 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &DeadlockTimeout,
-               1000, 1, INT_MAX/1000, 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.
         */
@@ -1563,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,
@@ -1573,13 +1561,13 @@ static struct config_int ConfigureNamesInt[] =
        },
 
        {
-               {"log_autovacuum", PGC_SIGHUP, 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 (disabling autovacuum logging)."),
+               {"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
        },
 
@@ -1599,7 +1587,7 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &bgwriter_lru_maxpages,
-               5, 0, 1000, NULL, NULL
+               100, 0, 1000, NULL, NULL
        },
 
        {
@@ -1846,12 +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."),
+               {"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES,
+                       gettext_noop("Multiple of the average buffer usage to free per round."),
                        NULL
                },
-               &bgwriter_lru_percent,
-               1.0, 0.0, 100.0, NULL, NULL
+               &bgwriter_lru_multiplier,
+               2.0, 0.0, 10.0, NULL, NULL
        },
 
        {
@@ -1861,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
        },
 
        {
@@ -1905,7 +1893,7 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &XLogArchiveCommand,
-               "", NULL, NULL
+               "", NULL, show_archive_command
        },
 
        {
@@ -1947,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
        },
 
        {
@@ -2038,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
@@ -2059,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."),
@@ -2454,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 */
        {
@@ -2500,12 +2498,12 @@ static int      GUCNestLevel = 0;       /* 1 when in main transaction */
 
 static int     guc_var_compare(const void *a, const void *b);
 static int     guc_name_compare(const char *namea, const char *nameb);
-static void push_old_value(struct config_generic * gconf);
+static void push_old_value(struct config_generic * gconf, GucAction action);
 static void ReportGUCOption(struct config_generic * record);
 static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
 static void ShowAllGUCConfig(DestReceiver *dest);
 static char *_ShowOption(struct config_generic * record, bool use_units);
-static bool is_newvalue_equal(struct config_generic *record, const char *newvalue);
+static bool is_newvalue_equal(struct config_generic * record, const char *newvalue);
 
 
 /*
@@ -2569,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;
        }
 
@@ -2593,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)
 {
@@ -2761,8 +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.  Note that the current value, as well
-        * as the boot and reset values, start out NULL.
+        * '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);
 
@@ -2879,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)
 {
@@ -2922,8 +2973,8 @@ InitializeGUCOptions(void)
        long            stack_rlimit;
 
        /*
-        * Before log_line_prefix could possibly receive a nonempty setting,
-        * make sure that timezone processing is minimally alive (see elog.c).
+        * 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();
 
@@ -2942,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;
 
@@ -2995,7 +3045,6 @@ InitializeGUCOptions(void)
 
                                        *conf->variable = NULL;
                                        conf->reset_val = NULL;
-                                       conf->tentative_val = NULL;
 
                                        if (conf->boot_val == NULL)
                                        {
@@ -3049,7 +3098,7 @@ InitializeGUCOptions(void)
 
        /*
         * For historical reasons, some GUC parameters can receive defaults from
-        * environment variables.  Process those settings.  NB: if you add or
+        * environment variables.  Process those settings.      NB: if you add or
         * remove anything here, see also ProcessConfigFile().
         */
 
@@ -3073,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);
@@ -3261,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)
                {
@@ -3274,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:
@@ -3290,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:
@@ -3306,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:
@@ -3339,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;
                                }
                }
@@ -3356,84 +3389,84 @@ 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)
 {
        GucStack   *stack;
 
-       /* If we're not inside a transaction, do nothing */
+       /* 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 >= GUCNestLevel)
-                       return;
-
-               /*
-                * We keep all the stack entries in TopTransactionContext so as to
-                * avoid allocation problems when a subtransaction back-fills stack
-                * entries for upper transaction levels.
-                */
-               stack = (GucStack *) MemoryContextAlloc(TopTransactionContext,
-                                                                                               sizeof(GucStack));
-
-               stack->prev = gconf->stack;
-               stack->nest_level = stack->prev ? stack->prev->nest_level + 1 : 1;
-               stack->status = gconf->status;
-               stack->tentative_source = gconf->tentative_source;
-               stack->source = gconf->source;
-
-               switch (gconf->vartype)
+               /* Yes, so adjust its state if necessary */
+               Assert(stack->nest_level == GUCNestLevel);
+               switch (action)
                {
-                       case PGC_BOOL:
-                               stack->tentative_val.boolval =
-                                       ((struct config_bool *) gconf)->tentative_val;
-                               stack->value.boolval =
-                                       *((struct config_bool *) gconf)->variable;
-                               break;
-
-                       case PGC_INT:
-                               stack->tentative_val.intval =
-                                       ((struct config_int *) gconf)->tentative_val;
-                               stack->value.intval =
-                                       *((struct config_int *) gconf)->variable;
+                       case GUC_ACTION_SET:
+                               /* SET overrides any prior action at same nest level */
+                               if (stack->state == GUC_SET_LOCAL)
+                               {
+                                       /* must discard old masked value */
+                                       discard_stack_value(gconf, &stack->masked);
+                               }
+                               stack->state = GUC_SET;
                                break;
-
-                       case PGC_REAL:
-                               stack->tentative_val.realval =
-                                       ((struct config_real *) gconf)->tentative_val;
-                               stack->value.realval =
-                                       *((struct config_real *) gconf)->variable;
+                       case GUC_ACTION_LOCAL:
+                               if (stack->state == GUC_SET)
+                               {
+                                       /* SET followed by SET LOCAL, remember SET's value */
+                                       set_stack_value(gconf, &stack->masked);
+                                       stack->state = GUC_SET_LOCAL;
+                               }
+                               /* in all other cases, no change to stack entry */
                                break;
-
-                       case PGC_STRING:
-                               stack->tentative_val.stringval =
-                                       ((struct config_string *) gconf)->tentative_val;
-                               stack->value.stringval =
-                                       *((struct config_string *) gconf)->variable;
+                       case GUC_ACTION_SAVE:
+                               /* Could only have a prior SAVE of same variable */
+                               Assert(stack->state == GUC_SAVE);
                                break;
                }
+               Assert(guc_dirty);              /* must be set already */
+               return;
+       }
 
-               gconf->stack = stack;
-
-               /* Set state to indicate nothing happened yet within this level */
-               gconf->status = GUC_HAVE_STACK;
+       /*
+        * Push a new stack entry
+        *
+        * We keep all the stack entries in TopTransactionContext for simplicity.
+        */
+       stack = (GucStack *) MemoryContextAllocZero(TopTransactionContext,
+                                                                                               sizeof(GucStack));
 
-               /* Ensure we remember to pop at end of xact */
-               guc_dirty = true;
+       stack->prev = gconf->stack;
+       stack->nest_level = GUCNestLevel;
+       switch (action)
+       {
+               case GUC_ACTION_SET:
+                       stack->state = GUC_SET;
+                       break;
+               case GUC_ACTION_LOCAL:
+                       stack->state = GUC_LOCAL;
+                       break;
+               case GUC_ACTION_SAVE:
+                       stack->state = GUC_SAVE;
+                       break;
        }
+       stack->source = gconf->source;
+       set_stack_value(gconf, &stack->prior);
+
+       gconf->stack = stack;
+
+       /* Ensure we remember to pop at end of xact */
+       guc_dirty = true;
 }
 
+
 /*
  * Do GUC processing at main transaction start.
  */
@@ -3441,9 +3474,9 @@ void
 AtStart_GUC(void)
 {
        /*
-        * The nest level should be 0 between transactions; if it isn't,
-        * somebody didn't call AtEOXact_GUC, or called it with the wrong
-        * nestLevel.  We throw a warning but make no other effort to clean up.
+        * 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",
@@ -3453,7 +3486,7 @@ AtStart_GUC(void)
 
 /*
  * 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
+ * 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
@@ -3472,6 +3505,7 @@ NewGUCNestLevel(void)
 void
 AtEOXact_GUC(bool isCommit, int nestLevel)
 {
+       bool            still_dirty;
        int                     i;
 
        Assert(nestLevel > 0 && nestLevel <= GUCNestLevel);
@@ -3483,246 +3517,237 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                return;
        }
 
+       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 & (GUC_HAVE_TENTATIVE |
-                                                 GUC_HAVE_LOCAL |
-                                                 GUC_HAVE_STACK)) == 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 < nestLevel)
-                       continue;
-               Assert(stack->nest_level == nestLevel);
-
-               /*
-                * 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;
+                       GucStack   *prev = stack->prev;
+                       bool            restorePrior = false;
+                       bool            restoreMasked = false;
+                       bool            changed;
 
-               /*
-                * We have two cases:
-                *
-                * If commit and HAVE_TENTATIVE, set actual value to tentative (this
-                * is to override a SET LOCAL if one occurred later than SET). We keep
-                * the tentative value and propagate HAVE_TENTATIVE to the parent
-                * status, allowing the SET's effect to percolate up. (But if we're
-                * exiting the outermost transaction, we'll drop the HAVE_TENTATIVE
-                * bit below.)
-                *
-                * Otherwise, we have a transaction that aborted or executed only SET
-                * LOCAL (or no SET at all).  In either case it should have no further
-                * effect, so restore both tentative and actual values from the stack
-                * entry.
-                */
-
-               useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0;
-               changed = false;
-
-               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 && newval)
+                                                       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 (nestLevel == 1)
-                                               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 (nestLevel == 1)
-               {
-                       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 (nestLevel == 1)
-               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;
@@ -4124,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
@@ -4142,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;
@@ -4154,7 +4184,7 @@ 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;
@@ -4171,10 +4201,10 @@ set_config_option(const char *name, const char *value,
        }
 
        /*
-        * If source is postgresql.conf, mark the found record with GUC_IS_IN_FILE.
-        * This is for the convenience of ProcessConfigFile.  Note that we do it
-        * even if changeVal is false, since ProcessConfigFile wants the marking
-        * to occur during its testing pass.
+        * If source is postgresql.conf, mark the found record with
+        * GUC_IS_IN_FILE. This is for the convenience of ProcessConfigFile.  Note
+        * that we do it even if changeVal is false, since ProcessConfigFile wants
+        * the marking to occur during its testing pass.
         */
        if (source == PGC_S_FILE)
                record->status |= GUC_IS_IN_FILE;
@@ -4204,7 +4234,7 @@ set_config_option(const char *name, const char *value,
                                /*
                                 * 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
+                                * tries to change it.  (Throwing an error would be more
                                 * consistent, but seems overly rigid.)
                                 */
                                if (changeVal && !is_newvalue_equal(record, value))
@@ -4279,10 +4309,10 @@ 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);
@@ -4307,9 +4337,6 @@ set_config_option(const char *name, const char *value,
 
        /*
         * Evaluate value and set variable.
-        *
-        * Note: if value == NULL then we are supposed to set to the reset_val,
-        * except when source == PGC_S_DEFAULT; then we set to the boot_val.
         */
        switch (record->vartype)
        {
@@ -4351,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;
@@ -4370,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;
                        }
@@ -4404,8 +4419,8 @@ set_config_option(const char *name, const char *value,
                                        {
                                                ereport(elevel,
                                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                                errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                                                               name, value),
+                                                errmsg("invalid value for parameter \"%s\": \"%s\"",
+                                                               name, value),
                                                                 hintmsg ? errhint(hintmsg) : 0));
                                                return false;
                                        }
@@ -4440,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;
@@ -4459,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;
                        }
@@ -4526,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;
@@ -4545,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;
                        }
@@ -4654,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);
@@ -4673,7 +4664,7 @@ 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;
                                                        }
@@ -4682,18 +4673,6 @@ set_config_option(const char *name, const char *value,
                                                if (newval && !string_field_used(conf, newval))
                                                        free(newval);
                                        }
-                                       else if (isLocal)
-                                       {
-                                               conf->gen.status |= GUC_HAVE_LOCAL;
-                                               guc_dirty = true;
-                                       }
-                                       else
-                                       {
-                                               set_string_field(conf, &conf->tentative_val, newval);
-                                               conf->gen.tentative_source = source;
-                                               conf->gen.status |= GUC_HAVE_TENTATIVE;
-                                               guc_dirty = true;
-                                       }
                                }
                                else if (newval)
                                        free(newval);
@@ -4717,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);
 }
 
 
@@ -4827,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,
@@ -4897,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,
@@ -4943,6 +4970,8 @@ flatten_set_variable_args(const char *name, List *args)
 void
 ExecSetVariableStmt(VariableSetStmt *stmt)
 {
+       GucAction       action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+
        switch (stmt->kind)
        {
                case VAR_SET_VALUE:
@@ -4951,13 +4980,14 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
                                                          ExtractSetVariableArgs(stmt),
                                                          (superuser() ? PGC_SUSET : PGC_USERSET),
                                                          PGC_S_SESSION,
-                                                         stmt->is_local,
+                                                         action,
                                                          true);
                        break;
                case VAR_SET_MULTI:
+
                        /*
-                        * Special case for special SQL syntax that effectively sets
-                        * more than one variable per statement.
+                        * Special case for special SQL syntax that effectively sets more
+                        * than one variable per statement.
                         */
                        if (strcmp(stmt->name, "TRANSACTION") == 0)
                        {
@@ -5007,7 +5037,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
                                                          NULL,
                                                          (superuser() ? PGC_SUSET : PGC_USERSET),
                                                          PGC_S_SESSION,
-                                                         stmt->is_local,
+                                                         action,
                                                          true);
                        break;
                case VAR_RESET_ALL:
@@ -5052,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);
 }
 
@@ -5096,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 */
@@ -5142,7 +5172,7 @@ init_custom_variable(const char *name,
  * variable into the GUC variable array, replacing any placeholder.
  */
 static void
-define_custom_variable(struct config_generic *variable)
+define_custom_variable(struct config_generic * variable)
 {
        const char *name = variable->name;
        const char **nameAddr = &name;
@@ -5174,8 +5204,8 @@ define_custom_variable(struct config_generic *variable)
        pHolder = (struct config_string *) (*res);
 
        /*
-        * Replace the placeholder.
-        * We aren't changing the name, so no re-sorting is necessary
+        * Replace the placeholder. We aren't changing the name, so no re-sorting
+        * is necessary
         */
        *res = variable;
 
@@ -5191,7 +5221,7 @@ define_custom_variable(struct config_generic *variable)
        if (value)
                set_config_option(name, value,
                                                  pHolder->gen.context, pHolder->gen.source,
-                                                 false, true);
+                                                 GUC_ACTION_SET, true);
 
        /*
         * Free up as much as we conveniently can of the placeholder structure
@@ -5199,7 +5229,6 @@ 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);
 }
@@ -5922,7 +5951,7 @@ _ShowOption(struct config_generic * record, bool use_units)
  * effects of canonicalization of string values by assign_hooks.
  */
 static bool
-is_newvalue_equal(struct config_generic *record, const char *newvalue)
+is_newvalue_equal(struct config_generic * record, const char *newvalue)
 {
        /* newvalue == NULL isn't supported */
        Assert(newvalue != NULL);
@@ -6146,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);
        }
@@ -6197,13 +6226,13 @@ ParseLongOption(const char *string, char **name, char **value)
 
 /*
  * Handle options fetched from pg_database.datconfig, pg_authid.rolconfig,
- * pg_proc.proconfig, etc.  Caller must specify proper context/source/local.
+ * 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,
-                               GucContext context, GucSource source, bool isLocal)
+                               GucContext context, GucSource source, GucAction action)
 {
        int                     i;
 
@@ -6243,7 +6272,7 @@ ProcessGUCArray(ArrayType *array,
                        continue;
                }
 
-               (void) set_config_option(name, value, context, source, isLocal, true);
+               (void) set_config_option(name, value, context, source, action, true);
 
                free(name);
                if (value)
@@ -6270,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);
@@ -6348,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);
@@ -6406,7 +6435,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
 
 
 /*
- * assign_hook subroutines
+ * assign_hook and show_hook subroutines
  */
 
 static const char *
@@ -6426,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;
        }
@@ -6440,7 +6468,7 @@ 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;
+                       newlogdest |= LOG_DESTINATION_CSVLOG;
 #ifdef HAVE_SYSLOG
                else if (pg_strcasecmp(tok, "syslog") == 0)
                        newlogdest |= LOG_DESTINATION_SYSLOG;
@@ -6451,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);
@@ -6549,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;
@@ -6739,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;
@@ -6811,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;
 }
@@ -6823,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;
 }
@@ -6835,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;
@@ -6852,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;
@@ -6871,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;
@@ -6954,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))
@@ -7015,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)
 {