]> granicus.if.org Git - postgresql/commitdiff
Revise the API for GUC variable assign hooks.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Apr 2011 04:11:01 +0000 (00:11 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Apr 2011 04:12:02 +0000 (00:12 -0400)
The previous functions of assign hooks are now split between check hooks
and assign hooks, where the former can fail but the latter shouldn't.
Aside from being conceptually clearer, this approach exposes the
"canonicalized" form of the variable value to guc.c without having to do
an actual assignment.  And that lets us fix the problem recently noted by
Bernd Helmle that the auto-tune patch for wal_buffers resulted in bogus
log messages about "parameter "wal_buffers" cannot be changed without
restarting the server".  There may be some speed advantage too, because
this design lets hook functions avoid re-parsing variable values when
restoring a previous state after a rollback (they can store a pre-parsed
representation of the value instead).  This patch also resolves a
longstanding annoyance about custom error messages from variable assign
hooks: they should modify, not appear separately from, guc.c's own message
about "invalid parameter value".

35 files changed:
contrib/auth_delay/auth_delay.c
contrib/auto_explain/auto_explain.c
contrib/pg_stat_statements/pg_stat_statements.c
contrib/sepgsql/hooks.c
src/backend/access/transam/xlog.c
src/backend/catalog/namespace.c
src/backend/commands/tablespace.c
src/backend/commands/variable.c
src/backend/nls.mk
src/backend/replication/syncrep.c
src/backend/tcop/postgres.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/pg_locale.c
src/backend/utils/cache/ts_cache.c
src/backend/utils/error/elog.c
src/backend/utils/mb/mbutils.c
src/backend/utils/misc/README
src/backend/utils/misc/guc-file.l
src/backend/utils/misc/guc.c
src/backend/utils/misc/tzparser.c
src/include/commands/variable.h
src/include/mb/pg_wchar.h
src/include/replication/syncrep.h
src/include/tcop/tcopprot.h
src/include/tsearch/ts_cache.h
src/include/utils/datetime.h
src/include/utils/elog.h
src/include/utils/guc.h
src/include/utils/guc_tables.h
src/include/utils/pg_locale.h
src/include/utils/tzparser.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_handler.c
src/test/regress/expected/guc.out
src/timezone/pgtz.c

index 199de9bb39130de2d0ae0862571a464515f107c3..ca388c4498414d86cb0fbd889ef1f851e9f93ddf 100644 (file)
@@ -53,7 +53,7 @@ auth_delay_checks(Port *port, int status)
 void
 _PG_init(void)
 {
-       /* Define custome GUC variables */
+       /* Define custom GUC variables */
        DefineCustomIntVariable("auth_delay.milliseconds",
                                                        "Milliseconds to delay before reporting authentication failure",
                                                        NULL,
@@ -63,6 +63,7 @@ _PG_init(void)
                                                        PGC_SIGHUP,
                                                        GUC_UNIT_MS,
                                                        NULL,
+                                                       NULL,
                                                        NULL);
        /* Install Hooks */
        original_client_auth_hook = ClientAuthentication_hook;
index 78e604e7bc4f0f6203bacd4047d55541afd9b9e9..b3206987484e57a91850033fef01bc9c507172e9 100644 (file)
@@ -74,6 +74,7 @@ _PG_init(void)
                                                        PGC_SUSET,
                                                        GUC_UNIT_MS,
                                                        NULL,
+                                                       NULL,
                                                        NULL);
 
        DefineCustomBoolVariable("auto_explain.log_analyze",
@@ -84,6 +85,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomBoolVariable("auto_explain.log_verbose",
@@ -94,6 +96,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomBoolVariable("auto_explain.log_buffers",
@@ -104,6 +107,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomEnumVariable("auto_explain.log_format",
@@ -115,6 +119,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomBoolVariable("auto_explain.log_nested_statements",
@@ -125,6 +130,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        EmitWarningsOnPlaceholders("auto_explain");
index 0390ec4c8ed6d3e9d6c09cd85b3cadbc2dd3a6fb..87cf8c55cf491831bbf5a9db79ca1eb3e9b81cc9 100644 (file)
@@ -219,6 +219,7 @@ _PG_init(void)
                                                        PGC_POSTMASTER,
                                                        0,
                                                        NULL,
+                                                       NULL,
                                                        NULL);
 
        DefineCustomEnumVariable("pg_stat_statements.track",
@@ -230,6 +231,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomBoolVariable("pg_stat_statements.track_utility",
@@ -240,6 +242,7 @@ _PG_init(void)
                                                         PGC_SUSET,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        DefineCustomBoolVariable("pg_stat_statements.save",
@@ -250,6 +253,7 @@ _PG_init(void)
                                                         PGC_SIGHUP,
                                                         0,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        EmitWarningsOnPlaceholders("pg_stat_statements");
index 27e85d25b1d33222a4c935b13af9c8263a720bac..5dc8a3ecaa8b704a158409d6be46d8cd755253c5 100644 (file)
@@ -394,6 +394,7 @@ _PG_init(void)
                                                         PGC_SIGHUP,
                                                         GUC_NOT_IN_SAMPLE,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        /*
@@ -412,6 +413,7 @@ _PG_init(void)
                                                         PGC_USERSET,
                                                         GUC_NOT_IN_SAMPLE,
                                                         NULL,
+                                                        NULL,
                                                         NULL);
 
        /*
index b22e498f8b50a8d4c212f0637862e81cc92cd7e2..b31c79ebbdcb03660c81e5f83c55e107ee2b6fb2 100644 (file)
@@ -4887,36 +4887,60 @@ GetSystemIdentifier(void)
 /*
  * Auto-tune the number of XLOG buffers.
  *
- * If the user-set value of wal_buffers is -1, we auto-tune to about 3% of
- * shared_buffers, with a maximum of one XLOG segment and a minimum of 8
- * blocks (8 was the default value prior to PostgreSQL 9.1, when auto-tuning
- * was added).  We also clamp manually-set values to at least 4 blocks; prior
- * to PostgreSQL 9.1, a minimum of 4 was enforced by guc.c, but since that
- * is no longer possible, we just silently treat such values as a request for
- * the minimum.
+ * The preferred setting for wal_buffers is about 3% of shared_buffers, with
+ * a maximum of one XLOG segment (there is little reason to think that more
+ * is helpful, at least so long as we force an fsync when switching log files)
+ * and a minimum of 8 blocks (which was the default value prior to PostgreSQL
+ * 9.1, when auto-tuning was added).
+ *
+ * This should not be called until NBuffers has received its final value.
  */
-static void
-XLOGTuneNumBuffers(void)
+static int
+XLOGChooseNumBuffers(void)
 {
-       int                     xbuffers = XLOGbuffers;
-       char            buf[32];
+       int                     xbuffers;
 
-       if (xbuffers == -1)
-       {
-               xbuffers = NBuffers / 32;
-               if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ)
-                       xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ;
-               if (xbuffers < 8)
-                       xbuffers = 8;
-       }
-       else if (xbuffers < 4)
-               xbuffers = 4;
+       xbuffers = NBuffers / 32;
+       if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ)
+               xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ;
+       if (xbuffers < 8)
+               xbuffers = 8;
+       return xbuffers;
+}
 
-       if (xbuffers != XLOGbuffers)
+/*
+ * GUC check_hook for wal_buffers
+ */
+bool
+check_wal_buffers(int *newval, void **extra, GucSource source)
+{
+       /*
+        * -1 indicates a request for auto-tune.
+        */
+       if (*newval == -1)
        {
-               snprintf(buf, sizeof(buf), "%d", xbuffers);
-               SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE);
+               /*
+                * If we haven't yet changed the boot_val default of -1, just let it
+                * be.  We'll fix it when XLOGShmemSize is called.
+                */
+               if (XLOGbuffers == -1)
+                       return true;
+
+               /* Otherwise, substitute the auto-tune value */
+               *newval = XLOGChooseNumBuffers();
        }
+
+       /*
+        * We clamp manually-set values to at least 4 blocks.  Prior to PostgreSQL
+        * 9.1, a minimum of 4 was enforced by guc.c, but since that is no longer
+        * the case, we just silently treat such values as a request for the
+        * minimum.  (We could throw an error instead, but that doesn't seem very
+        * helpful.)
+        */
+       if (*newval < 4)
+               *newval = 4;
+
+       return true;
 }
 
 /*
@@ -4927,8 +4951,19 @@ XLOGShmemSize(void)
 {
        Size            size;
 
-       /* Figure out how many XLOG buffers we need. */
-       XLOGTuneNumBuffers();
+       /*
+        * If the value of wal_buffers is -1, use the preferred auto-tune value.
+        * This isn't an amazingly clean place to do this, but we must wait till
+        * NBuffers has received its final value, and must do it before using
+        * the value of XLOGbuffers to do anything important.
+        */
+       if (XLOGbuffers == -1)
+       {
+               char            buf[32];
+
+               snprintf(buf, sizeof(buf), "%d", XLOGChooseNumBuffers());
+               SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE);
+       }
        Assert(XLOGbuffers > 0);
 
        /* XLogCtl */
@@ -8653,12 +8688,9 @@ get_sync_bit(int method)
 /*
  * GUC support
  */
-bool
-assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source)
+void
+assign_xlog_sync_method(int new_sync_method, void *extra)
 {
-       if (!doit)
-               return true;
-
        if (sync_method != new_sync_method)
        {
                /*
@@ -8678,8 +8710,6 @@ assign_xlog_sync_method(int new_sync_method, bool doit, GucSource source)
                                XLogFileClose();
                }
        }
-
-       return true;
 }
 
 
index 77c9805aed1c6d70214c01938b957d88377f969d..734581e48557dcd72e8e7796729f344d00afd9ce 100644 (file)
@@ -3468,31 +3468,33 @@ ResetTempTableNamespace(void)
  * Routines for handling the GUC variable 'search_path'.
  */
 
-/* assign_hook: validate new search_path, do extra actions as needed */
-const char *
-assign_search_path(const char *newval, bool doit, GucSource source)
+/* check_hook: validate new search_path, if possible */
+bool
+check_search_path(char **newval, void **extra, GucSource source)
 {
+       bool            result = true;
        char       *rawname;
        List       *namelist;
        ListCell   *l;
 
        /* Need a modifiable copy of string */
-       rawname = pstrdup(newval);
+       rawname = pstrdup(*newval);
 
        /* Parse string into list of identifiers */
        if (!SplitIdentifierString(rawname, ',', &namelist))
        {
                /* syntax error in name list */
+               GUC_check_errdetail("List syntax is invalid.");
                pfree(rawname);
                list_free(namelist);
-               return NULL;
+               return false;
        }
 
        /*
         * If we aren't inside a transaction, we cannot do database access so
         * cannot verify the individual names.  Must accept the list on faith.
         */
-       if (source >= PGC_S_INTERACTIVE && IsTransactionState())
+       if (IsTransactionState())
        {
                /*
                 * Verify that all the names are either valid namespace names or
@@ -3504,7 +3506,7 @@ assign_search_path(const char *newval, bool doit, GucSource source)
                 * DATABASE SET or ALTER USER SET command.      It could be that the
                 * intended use of the search path is for some other database, so we
                 * should not error out if it mentions schemas not present in the
-                * current database.  We reduce the message to NOTICE instead.
+                * current database.  We issue a NOTICE instead.
                 */
                foreach(l, namelist)
                {
@@ -3516,24 +3518,37 @@ assign_search_path(const char *newval, bool doit, GucSource source)
                                continue;
                        if (!SearchSysCacheExists1(NAMESPACENAME,
                                                                           CStringGetDatum(curname)))
-                               ereport((source == PGC_S_TEST) ? NOTICE : ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_SCHEMA),
-                                                errmsg("schema \"%s\" does not exist", curname)));
+                       {
+                               if (source == PGC_S_TEST)
+                                       ereport(NOTICE,
+                                                       (errcode(ERRCODE_UNDEFINED_SCHEMA),
+                                                        errmsg("schema \"%s\" does not exist", curname)));
+                               else
+                               {
+                                       GUC_check_errdetail("schema \"%s\" does not exist", curname);
+                                       result = false;
+                                       break;
+                               }
+                       }
                }
        }
 
        pfree(rawname);
        list_free(namelist);
 
+       return result;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_search_path(const char *newval, void *extra)
+{
        /*
         * We mark the path as needing recomputation, but don't do anything until
         * it's needed.  This avoids trying to do database access during GUC
-        * initialization.
+        * initialization, or outside a transaction.
         */
-       if (doit)
-               baseSearchPathValid = false;
-
-       return newval;
+       baseSearchPathValid = false;
 }
 
 /*
index b5a2d9d005e223f49913ca64068e5955ff3da403..42a704beb164f39316862f4bdcdcc6c6b6edd1ba 100644 (file)
@@ -1023,9 +1023,9 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
  * Routines for handling the GUC variable 'default_tablespace'.
  */
 
-/* assign_hook: validate new default_tablespace, do extra actions as needed */
-const char *
-assign_default_tablespace(const char *newval, bool doit, GucSource source)
+/* check_hook: validate new default_tablespace */
+bool
+check_default_tablespace(char **newval, void **extra, GucSource source)
 {
        /*
         * If we aren't inside a transaction, we cannot do database access so
@@ -1033,18 +1033,16 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source)
         */
        if (IsTransactionState())
        {
-               if (newval[0] != '\0' &&
-                       !OidIsValid(get_tablespace_oid(newval, true)))
+               if (**newval != '\0' &&
+                       !OidIsValid(get_tablespace_oid(*newval, true)))
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("tablespace \"%s\" does not exist",
-                                                       newval)));
-                       return NULL;
+                       GUC_check_errdetail("Tablespace \"%s\" does not exist.",
+                                                               *newval);
+                       return false;
                }
        }
 
-       return newval;
+       return true;
 }
 
 /*
@@ -1100,23 +1098,30 @@ GetDefaultTablespace(char relpersistence)
  * Routines for handling the GUC variable 'temp_tablespaces'.
  */
 
-/* assign_hook: validate new temp_tablespaces, do extra actions as needed */
-const char *
-assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
+typedef struct
+{
+       int                     numSpcs;
+       Oid                     tblSpcs[1];             /* VARIABLE LENGTH ARRAY */
+} temp_tablespaces_extra;
+
+/* check_hook: validate new temp_tablespaces */
+bool
+check_temp_tablespaces(char **newval, void **extra, GucSource source)
 {
        char       *rawname;
        List       *namelist;
 
        /* Need a modifiable copy of string */
-       rawname = pstrdup(newval);
+       rawname = pstrdup(*newval);
 
        /* Parse string into list of identifiers */
        if (!SplitIdentifierString(rawname, ',', &namelist))
        {
                /* syntax error in name list */
+               GUC_check_errdetail("List syntax is invalid.");
                pfree(rawname);
                list_free(namelist);
-               return NULL;
+               return false;
        }
 
        /*
@@ -1126,17 +1131,13 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
         */
        if (IsTransactionState())
        {
-               /*
-                * If we error out below, or if we are called multiple times in one
-                * transaction, we'll leak a bit of TopTransactionContext memory.
-                * Doesn't seem worth worrying about.
-                */
+               temp_tablespaces_extra *myextra;
                Oid                *tblSpcs;
                int                     numSpcs;
                ListCell   *l;
 
-               tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
-                                                                               list_length(namelist) * sizeof(Oid));
+               /* temporary workspace until we are done verifying the list */
+               tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
                numSpcs = 0;
                foreach(l, namelist)
                {
@@ -1169,7 +1170,7 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
                                continue;
                        }
 
-                       /* Check permissions similarly */
+                       /* Check permissions, similarly complaining only if interactive */
                        aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
                                                                                           ACL_CREATE);
                        if (aclresult != ACLCHECK_OK)
@@ -1182,17 +1183,41 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
                        tblSpcs[numSpcs++] = curoid;
                }
 
-               /* If actively "doing it", give the new list to fd.c */
-               if (doit)
-                       SetTempTablespaces(tblSpcs, numSpcs);
-               else
-                       pfree(tblSpcs);
+               /* Now prepare an "extra" struct for assign_temp_tablespaces */
+               myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
+                                                numSpcs * sizeof(Oid));
+               if (!myextra)
+                       return false;
+               myextra->numSpcs = numSpcs;
+               memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
+               *extra = (void *) myextra;
+
+               pfree(tblSpcs);
        }
 
        pfree(rawname);
        list_free(namelist);
 
-       return newval;
+       return true;
+}
+
+/* assign_hook: do extra actions as needed */
+void
+assign_temp_tablespaces(const char *newval, void *extra)
+{
+       temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra;
+
+       /*
+        * If check_temp_tablespaces was executed inside a transaction, then pass
+        * the list it made to fd.c.  Otherwise, clear fd.c's list; we must be
+        * still outside a transaction, or else restoring during transaction exit,
+        * and in either case we can just let the next PrepareTempTablespaces call
+        * make things sane.
+        */
+       if (myextra)
+               SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs);
+       else
+               SetTempTablespaces(NULL, 0);
 }
 
 /*
index 2a61ea3bc7cc970efffaede20d4da4d414faa121..2cec713089668825d49e6cf9e95c2dbc5ee6ea42 100644 (file)
  */
 
 /*
- * assign_datestyle: GUC assign_hook for datestyle
+ * check_datestyle: GUC check_hook for datestyle
  */
-const char *
-assign_datestyle(const char *value, bool doit, GucSource source)
+bool
+check_datestyle(char **newval, void **extra, GucSource source)
 {
        int                     newDateStyle = DateStyle;
        int                     newDateOrder = DateOrder;
@@ -44,23 +44,22 @@ assign_datestyle(const char *value, bool doit, GucSource source)
        bool            have_order = false;
        bool            ok = true;
        char       *rawstring;
+       int                *myextra;
        char       *result;
        List       *elemlist;
        ListCell   *l;
 
        /* Need a modifiable copy of string */
-       rawstring = pstrdup(value);
+       rawstring = pstrdup(*newval);
 
        /* Parse string into list of identifiers */
        if (!SplitIdentifierString(rawstring, ',', &elemlist))
        {
                /* syntax error in list */
+               GUC_check_errdetail("List syntax is invalid.");
                pfree(rawstring);
                list_free(elemlist);
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("invalid list syntax for parameter \"datestyle\"")));
-               return NULL;
+               return false;
        }
 
        foreach(l, elemlist)
@@ -130,38 +129,38 @@ assign_datestyle(const char *value, bool doit, GucSource source)
                         * Easiest way to get the current DEFAULT state is to fetch the
                         * DEFAULT string from guc.c and recursively parse it.
                         *
-                        * We can't simply "return assign_datestyle(...)" because we need
+                        * We can't simply "return check_datestyle(...)" because we need
                         * to handle constructs like "DEFAULT, ISO".
                         */
-                       int                     saveDateStyle = DateStyle;
-                       int                     saveDateOrder = DateOrder;
-                       const char *subval;
+                       char   *subval;
+                       void   *subextra = NULL;
 
-                       subval = assign_datestyle(GetConfigOptionResetString("datestyle"),
-                                                                         true, source);
-                       if (!have_style)
-                               newDateStyle = DateStyle;
-                       if (!have_order)
-                               newDateOrder = DateOrder;
-                       DateStyle = saveDateStyle;
-                       DateOrder = saveDateOrder;
+                       subval = strdup(GetConfigOptionResetString("datestyle"));
                        if (!subval)
                        {
                                ok = false;
                                break;
                        }
-                       /* Here we know that our own return value is always malloc'd */
-                       /* when doit is true */
-                       free((char *) subval);
+                       if (!check_datestyle(&subval, &subextra, source))
+                       {
+                               free(subval);
+                               ok = false;
+                               break;
+                       }
+                       myextra = (int *) subextra;
+                       if (!have_style)
+                               newDateStyle = myextra[0];
+                       if (!have_order)
+                               newDateOrder = myextra[1];
+                       free(subval);
+                       free(subextra);
                }
                else
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("unrecognized \"datestyle\" key word: \"%s\"",
-                                                       tok)));
-                       ok = false;
-                       break;
+                       GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
+                       pfree(rawstring);
+                       list_free(elemlist);
+                       return false;
                }
        }
 
@@ -170,24 +169,16 @@ assign_datestyle(const char *value, bool doit, GucSource source)
 
        if (!ok)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("conflicting \"datestyle\" specifications")));
-               return NULL;
+               GUC_check_errdetail("Conflicting \"datestyle\" specifications.");
+               return false;
        }
 
-       /*
-        * If we aren't going to do the assignment, just return OK indicator.
-        */
-       if (!doit)
-               return value;
-
        /*
         * Prepare the canonical string to return.      GUC wants it malloc'd.
         */
        result = (char *) malloc(32);
        if (!result)
-               return NULL;
+               return false;
 
        switch (newDateStyle)
        {
@@ -217,14 +208,32 @@ assign_datestyle(const char *value, bool doit, GucSource source)
                        break;
        }
 
+       free(*newval);
+       *newval = result;
+
        /*
-        * Finally, it's safe to assign to the global variables; the assignment
-        * cannot fail now.
+        * Set up the "extra" struct actually used by assign_datestyle.
         */
-       DateStyle = newDateStyle;
-       DateOrder = newDateOrder;
+       myextra = (int *) malloc(2 * sizeof(int));
+       if (!myextra)
+               return false;
+       myextra[0] = newDateStyle;
+       myextra[1] = newDateOrder;
+       *extra = (void *) myextra;
+
+       return true;
+}
+
+/*
+ * assign_datestyle: GUC assign_hook for datestyle
+ */
+void
+assign_datestyle(const char *newval, void *extra)
+{
+       int                *myextra = (int *) extra;
 
-       return result;
+       DateStyle = myextra[0];
+       DateOrder = myextra[1];
 }
 
 
@@ -232,22 +241,58 @@ assign_datestyle(const char *value, bool doit, GucSource source)
  * TIMEZONE
  */
 
+typedef struct
+{
+       pg_tz      *session_timezone;
+       int                     CTimeZone;
+       bool            HasCTZSet;
+} timezone_extra;
+
 /*
- * assign_timezone: GUC assign_hook for timezone
+ * check_timezone: GUC check_hook for timezone
  */
-const char *
-assign_timezone(const char *value, bool doit, GucSource source)
+bool
+check_timezone(char **newval, void **extra, GucSource source)
 {
-       char       *result;
+       timezone_extra myextra;
        char       *endptr;
        double          hours;
 
+       if (*newval == NULL)
+       {
+               /*
+                * The boot_val given for TimeZone in guc.c is NULL.  When we see this
+                * we just do nothing.  If this isn't overridden from the config file
+                * then pg_timezone_initialize() will eventually select a default
+                * value from the environment.  This hack has two purposes: to avoid
+                * wasting cycles loading values that might soon be overridden from
+                * the config file, and to avoid trying to read the timezone files
+                * during InitializeGUCOptions().  The latter doesn't work in an
+                * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet
+                * and so we can't locate PGSHAREDIR.
+                */
+               Assert(source == PGC_S_DEFAULT);
+               return true;
+       }
+
        /*
-        * Check for INTERVAL 'foo'
+        * Initialize the "extra" struct that will be passed to assign_timezone.
+        * We don't want to change any of the three global variables except as
+        * specified by logic below.  To avoid leaking memory during failure
+        * returns, we set up the struct contents in a local variable, and only
+        * copy it to *extra at the end.
         */
-       if (pg_strncasecmp(value, "interval", 8) == 0)
+       myextra.session_timezone = session_timezone;
+       myextra.CTimeZone = CTimeZone;
+       myextra.HasCTZSet = HasCTZSet;
+
+       if (pg_strncasecmp(*newval, "interval", 8) == 0)
        {
-               const char *valueptr = value;
+               /*
+                * Support INTERVAL 'foo'.  This is for SQL spec compliance, not
+                * because it has any actual real-world usefulness.
+                */
+               const char *valueptr = *newval;
                char       *val;
                Interval   *interval;
 
@@ -255,14 +300,14 @@ assign_timezone(const char *value, bool doit, GucSource source)
                while (isspace((unsigned char) *valueptr))
                        valueptr++;
                if (*valueptr++ != '\'')
-                       return NULL;
+                       return false;
                val = pstrdup(valueptr);
                /* Check and remove trailing quote */
                endptr = strchr(val, '\'');
                if (!endptr || endptr[1] != '\0')
                {
                        pfree(val);
-                       return NULL;
+                       return false;
                }
                *endptr = '\0';
 
@@ -280,31 +325,25 @@ assign_timezone(const char *value, bool doit, GucSource source)
                pfree(val);
                if (interval->month != 0)
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("invalid interval value for time zone: month not allowed")));
+                       GUC_check_errdetail("Cannot specify months in time zone interval.");
                        pfree(interval);
-                       return NULL;
+                       return false;
                }
                if (interval->day != 0)
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                       errmsg("invalid interval value for time zone: day not allowed")));
+                       GUC_check_errdetail("Cannot specify days in time zone interval.");
                        pfree(interval);
-                       return NULL;
+                       return false;
                }
-               if (doit)
-               {
-                       /* Here we change from SQL to Unix sign convention */
+
+               /* Here we change from SQL to Unix sign convention */
 #ifdef HAVE_INT64_TIMESTAMP
-                       CTimeZone = -(interval->time / USECS_PER_SEC);
+               myextra.CTimeZone = -(interval->time / USECS_PER_SEC);
 #else
-                       CTimeZone = -interval->time;
+               myextra.CTimeZone = -interval->time;
 #endif
+               myextra.HasCTZSet = true;
 
-                       HasCTZSet = true;
-               }
                pfree(interval);
        }
        else
@@ -312,38 +351,12 @@ assign_timezone(const char *value, bool doit, GucSource source)
                /*
                 * Try it as a numeric number of hours (possibly fractional).
                 */
-               hours = strtod(value, &endptr);
-               if (endptr != value && *endptr == '\0')
-               {
-                       if (doit)
-                       {
-                               /* Here we change from SQL to Unix sign convention */
-                               CTimeZone = -hours * SECS_PER_HOUR;
-                               HasCTZSet = true;
-                       }
-               }
-               else if (pg_strcasecmp(value, "UNKNOWN") == 0)
+               hours = strtod(*newval, &endptr);
+               if (endptr != *newval && *endptr == '\0')
                {
-                       /*
-                        * UNKNOWN is the value shown as the "default" for TimeZone in
-                        * guc.c.  We interpret it as being a complete no-op; we don't
-                        * change the timezone setting.  Note that if there is a known
-                        * timezone setting, we will return that name rather than UNKNOWN
-                        * as the canonical spelling.
-                        *
-                        * During GUC initialization, since the timezone library isn't set
-                        * up yet, pg_get_timezone_name will return NULL and we will leave
-                        * the setting as UNKNOWN.      If this isn't overridden from the
-                        * config file then pg_timezone_initialize() will eventually
-                        * select a default value from the environment.
-                        */
-                       if (doit)
-                       {
-                               const char *curzone = pg_get_timezone_name(session_timezone);
-
-                               if (curzone)
-                                       value = curzone;
-                       }
+                       /* Here we change from SQL to Unix sign convention */
+                       myextra.CTimeZone = -hours * SECS_PER_HOUR;
+                       myextra.HasCTZSet = true;
                }
                else
                {
@@ -352,61 +365,83 @@ assign_timezone(const char *value, bool doit, GucSource source)
                         */
                        pg_tz      *new_tz;
 
-                       new_tz = pg_tzset(value);
+                       new_tz = pg_tzset(*newval);
 
                        if (!new_tz)
                        {
-                               ereport(GUC_complaint_elevel(source),
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("unrecognized time zone name: \"%s\"",
-                                                               value)));
-                               return NULL;
+                               /* Doesn't seem to be any great value in errdetail here */
+                               return false;
                        }
 
                        if (!tz_acceptable(new_tz))
                        {
-                               ereport(GUC_complaint_elevel(source),
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                          errmsg("time zone \"%s\" appears to use leap seconds",
-                                                         value),
-                                       errdetail("PostgreSQL does not support leap seconds.")));
-                               return NULL;
+                               GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
+                                                                *newval);
+                               GUC_check_errdetail("PostgreSQL does not support leap seconds.");
+                               return false;
                        }
 
-                       if (doit)
-                       {
-                               /* Save the changed TZ */
-                               session_timezone = new_tz;
-                               HasCTZSet = false;
-                       }
+                       myextra.session_timezone = new_tz;
+                       myextra.HasCTZSet = false;
                }
        }
 
-       /*
-        * If we aren't going to do the assignment, just return OK indicator.
-        */
-       if (!doit)
-               return value;
-
        /*
         * Prepare the canonical string to return.      GUC wants it malloc'd.
+        *
+        * Note: the result string should be something that we'd accept as input.
+        * We use the numeric format for interval cases, because it's simpler to
+        * reload.  In the named-timezone case, *newval is already OK and need not
+        * be changed; it might not have the canonical casing, but that's taken
+        * care of by show_timezone.
         */
-       if (HasCTZSet)
+       if (myextra.HasCTZSet)
        {
-               result = (char *) malloc(64);
+               char    *result = (char *) malloc(64);
+
                if (!result)
-                       return NULL;
+                       return false;
                snprintf(result, 64, "%.5f",
-                                (double) (-CTimeZone) / (double) SECS_PER_HOUR);
+                                (double) (-myextra.CTimeZone) / (double) SECS_PER_HOUR);
+               free(*newval);
+               *newval = result;
        }
-       else
-               result = strdup(value);
 
-       return result;
+       /*
+        * Pass back data for assign_timezone to use
+        */
+       *extra = malloc(sizeof(timezone_extra));
+       if (!*extra)
+               return false;
+       memcpy(*extra, &myextra, sizeof(timezone_extra));
+
+       return true;
+}
+
+/*
+ * assign_timezone: GUC assign_hook for timezone
+ */
+void
+assign_timezone(const char *newval, void *extra)
+{
+       timezone_extra *myextra = (timezone_extra *) extra;
+
+       /* Do nothing for the boot_val default of NULL */
+       if (!myextra)
+               return;
+
+       session_timezone = myextra->session_timezone;
+       CTimeZone = myextra->CTimeZone;
+       HasCTZSet = myextra->HasCTZSet;
 }
 
 /*
  * show_timezone: GUC show_hook for timezone
+ *
+ * We wouldn't need this, except that historically interval values have been
+ * shown without an INTERVAL prefix, so the display format isn't what would
+ * be accepted as input.  Otherwise we could have check_timezone return the
+ * preferred string to begin with.
  */
 const char *
 show_timezone(void)
@@ -447,83 +482,66 @@ show_timezone(void)
  */
 
 /*
- * assign_log_timezone: GUC assign_hook for log_timezone
+ * check_log_timezone: GUC check_hook for log_timezone
  */
-const char *
-assign_log_timezone(const char *value, bool doit, GucSource source)
+bool
+check_log_timezone(char **newval, void **extra, GucSource source)
 {
-       char       *result;
+       pg_tz      *new_tz;
 
-       if (pg_strcasecmp(value, "UNKNOWN") == 0)
+       if (*newval == NULL)
        {
                /*
-                * UNKNOWN is the value shown as the "default" for log_timezone in
-                * guc.c.  We interpret it as being a complete no-op; we don't change
-                * the timezone setting.  Note that if there is a known timezone
-                * setting, we will return that name rather than UNKNOWN as the
-                * canonical spelling.
-                *
-                * During GUC initialization, since the timezone library isn't set up
-                * yet, pg_get_timezone_name will return NULL and we will leave the
-                * setting as UNKNOWN.  If this isn't overridden from the config file
-                * then pg_timezone_initialize() will eventually select a default
+                * The boot_val given for log_timezone in guc.c is NULL.  When we see
+                * this we just do nothing.  If this isn't overridden from the config
+                * file then pg_timezone_initialize() will eventually select a default
                 * value from the environment.
                 */
-               if (doit)
-               {
-                       const char *curzone = pg_get_timezone_name(log_timezone);
-
-                       if (curzone)
-                               value = curzone;
-               }
+               Assert(source == PGC_S_DEFAULT);
+               return true;
        }
-       else
-       {
-               /*
-                * Otherwise assume it is a timezone name, and try to load it.
-                */
-               pg_tz      *new_tz;
-
-               new_tz = pg_tzset(value);
 
-               if (!new_tz)
-               {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("unrecognized time zone name: \"%s\"",
-                                                       value)));
-                       return NULL;
-               }
+       /*
+        * Otherwise assume it is a timezone name, and try to load it.
+        */
+       new_tz = pg_tzset(*newval);
 
-               if (!tz_acceptable(new_tz))
-               {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("time zone \"%s\" appears to use leap seconds",
-                                                       value),
-                                        errdetail("PostgreSQL does not support leap seconds.")));
-                       return NULL;
-               }
+       if (!new_tz)
+       {
+               /* Doesn't seem to be any great value in errdetail here */
+               return false;
+       }
 
-               if (doit)
-               {
-                       /* Save the changed TZ */
-                       log_timezone = new_tz;
-               }
+       if (!tz_acceptable(new_tz))
+       {
+               GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
+                                                *newval);
+               GUC_check_errdetail("PostgreSQL does not support leap seconds.");
+               return false;
        }
 
        /*
-        * If we aren't going to do the assignment, just return OK indicator.
+        * Pass back data for assign_log_timezone to use
         */
-       if (!doit)
-               return value;
+       *extra = malloc(sizeof(pg_tz *));
+       if (!*extra)
+               return false;
+       memcpy(*extra, &new_tz, sizeof(pg_tz *));
 
-       /*
-        * Prepare the canonical string to return.      GUC wants it malloc'd.
-        */
-       result = strdup(value);
+       return true;
+}
+
+/*
+ * assign_log_timezone: GUC assign_hook for log_timezone
+ */
+void
+assign_log_timezone(const char *newval, void *extra)
+{
+       /* Do nothing for the boot_val default of NULL */
+       if (!extra)
+               return;
 
-       return result;
+       log_timezone = *((pg_tz **) extra);
 }
 
 /*
@@ -548,38 +566,33 @@ show_log_timezone(void)
  *
  * We allow idempotent changes (r/w -> r/w and r/o -> r/o) at any time, and
  * we also always allow changes from read-write to read-only.  However,
- * read-only to read-write may be changed only when source == PGC_S_OVERRIDE
- * (i.e. we're aborting a read only transaction and restoring the previous
- * setting) or in a top-level transaction that has not yet taken an initial
- * snapshot.
+ * read-only may be changed to read-write only when in a top-level transaction
+ * that has not yet taken an initial snapshot.  Can't do it in a hot standby
+ * slave, either.
  */
 bool
-assign_transaction_read_only(bool newval, bool doit, GucSource source)
+check_transaction_read_only(bool *newval, void **extra, GucSource source)
 {
-       if (source != PGC_S_OVERRIDE && newval == false && XactReadOnly)
+       if (*newval == false && XactReadOnly)
        {
                /* Can't go to r/w mode inside a r/o transaction */
                if (IsSubTransaction())
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot set transaction read-write mode inside a read-only transaction")));
+                       GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+                       GUC_check_errmsg("cannot set transaction read-write mode inside a read-only transaction");
                        return false;
                }
                /* Top level transaction can't change to r/w after first snapshot. */
                if (FirstSnapshotSet)
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
-                                        errmsg("transaction read-write mode must be set before any query")));
+                       GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+                       GUC_check_errmsg("transaction read-write mode must be set before any query");
                        return false;
                }
                /* Can't go to r/w mode while recovery is still active */
                if (RecoveryInProgress())
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot set transaction read-write mode during recovery")));
+                       GUC_check_errmsg("cannot set transaction read-write mode during recovery");
                        return false;
                }
        }
@@ -591,76 +604,78 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source)
  * SET TRANSACTION ISOLATION LEVEL
  *
  * We allow idempotent changes at any time, but otherwise this can only be
- * changed from a toplevel transaction that has not yet taken a snapshot, or
- * when source == PGC_S_OVERRIDE (i.e. we're aborting a transaction and
- * restoring the previously set value).
+ * changed in a toplevel transaction that has not yet taken a snapshot.
  */
-const char *
-assign_XactIsoLevel(const char *value, bool doit, GucSource source)
+bool
+check_XactIsoLevel(char **newval, void **extra, GucSource source)
 {
-       /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
-       if (source != PGC_S_OVERRIDE && strcmp(value, XactIsoLevel_string) != 0)
+       int                     newXactIsoLevel;
+
+       if (strcmp(*newval, "serializable") == 0)
+       {
+               newXactIsoLevel = XACT_SERIALIZABLE;
+       }
+       else if (strcmp(*newval, "repeatable read") == 0)
+       {
+               newXactIsoLevel = XACT_REPEATABLE_READ;
+       }
+       else if (strcmp(*newval, "read committed") == 0)
+       {
+               newXactIsoLevel = XACT_READ_COMMITTED;
+       }
+       else if (strcmp(*newval, "read uncommitted") == 0)
+       {
+               newXactIsoLevel = XACT_READ_UNCOMMITTED;
+       }
+       else if (strcmp(*newval, "default") == 0)
+       {
+               newXactIsoLevel = DefaultXactIsoLevel;
+       }
+       else
+               return false;
+
+       if (newXactIsoLevel != XactIsoLevel)
        {
                if (FirstSnapshotSet)
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
-                                        errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query")));
-                       return NULL;
+                       GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+                       GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query");
+                       return false;
                }
                /* We ignore a subtransaction setting it to the existing value. */
                if (IsSubTransaction())
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
-                                        errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction")));
-                       return NULL;
+                       GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+                       GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction");
+                       return false;
                }
                /* Can't go to serializable mode while recovery is still active */
-               if (RecoveryInProgress() && strcmp(value, "serializable") == 0)
+               if (newXactIsoLevel == XACT_SERIALIZABLE && RecoveryInProgress())
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("cannot use serializable mode in a hot standby"),
-                                        errhint("You can use REPEATABLE READ instead.")));
+                       GUC_check_errmsg("cannot use serializable mode in a hot standby");
+                       GUC_check_errhint("You can use REPEATABLE READ instead.");
                        return false;
                }
        }
 
-       if (strcmp(value, "serializable") == 0)
-       {
-               if (doit)
-                       XactIsoLevel = XACT_SERIALIZABLE;
-       }
-       else if (strcmp(value, "repeatable read") == 0)
-       {
-               if (doit)
-                       XactIsoLevel = XACT_REPEATABLE_READ;
-       }
-       else if (strcmp(value, "read committed") == 0)
-       {
-               if (doit)
-                       XactIsoLevel = XACT_READ_COMMITTED;
-       }
-       else if (strcmp(value, "read uncommitted") == 0)
-       {
-               if (doit)
-                       XactIsoLevel = XACT_READ_UNCOMMITTED;
-       }
-       else if (strcmp(value, "default") == 0)
-       {
-               if (doit)
-                       XactIsoLevel = DefaultXactIsoLevel;
-       }
-       else
-               return NULL;
+       *extra = malloc(sizeof(int));
+       if (!*extra)
+               return false;
+       *((int *) *extra) = newXactIsoLevel;
+
+       return true;
+}
 
-       return value;
+void
+assign_XactIsoLevel(const char *newval, void *extra)
+{
+       XactIsoLevel = *((int *) extra);
 }
 
 const char *
 show_XactIsoLevel(void)
 {
+       /* We need this because we don't want to show "default". */
        switch (XactIsoLevel)
        {
                case XACT_READ_UNCOMMITTED:
@@ -681,25 +696,18 @@ show_XactIsoLevel(void)
  */
 
 bool
-assign_transaction_deferrable(bool newval, bool doit, GucSource source)
+check_transaction_deferrable(bool *newval, void **extra, GucSource source)
 {
-       /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */
-       if (source == PGC_S_OVERRIDE)
-               return true;
-
        if (IsSubTransaction())
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
-                                errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction")));
+               GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+               GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction");
                return false;
        }
-
        if (FirstSnapshotSet)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
-                                errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query")));
+               GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
+               GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query");
                return false;
        }
 
@@ -708,17 +716,34 @@ assign_transaction_deferrable(bool newval, bool doit, GucSource source)
 
 /*
  * Random number seed
+ *
+ * We can't roll back the random sequence on error, and we don't want
+ * config file reloads to affect it, so we only want interactive SET SEED
+ * commands to set it.  We use the "extra" storage to ensure that rollbacks
+ * don't try to do the operation again.
  */
 
 bool
-assign_random_seed(double value, bool doit, GucSource source)
+check_random_seed(double *newval, void **extra, GucSource source)
 {
-       /* Can't really roll back on error, so ignore non-interactive setting */
-       if (doit && source >= PGC_S_INTERACTIVE)
-               DirectFunctionCall1(setseed, Float8GetDatum(value));
+       *extra = malloc(sizeof(int));
+       if (!*extra)
+               return false;
+       /* Arm the assign only if source of value is an interactive SET */
+       *((int *) *extra) = (source >= PGC_S_INTERACTIVE);
+
        return true;
 }
 
+void
+assign_random_seed(double newval, void *extra)
+{
+       /* We'll do this at most once for any setting of the GUC variable */
+       if (*((int *) extra))
+               DirectFunctionCall1(setseed, Float8GetDatum(newval));
+       *((int *) extra) = 0;
+}
+
 const char *
 show_random_seed(void)
 {
@@ -727,214 +752,189 @@ show_random_seed(void)
 
 
 /*
- * encoding handling functions
+ * SET CLIENT_ENCODING
  */
 
-const char *
-assign_client_encoding(const char *value, bool doit, GucSource source)
+bool
+check_client_encoding(char **newval, void **extra, GucSource source)
 {
        int                     encoding;
 
-       encoding = pg_valid_client_encoding(value);
+       /* Look up the encoding by name */
+       encoding = pg_valid_client_encoding(*newval);
        if (encoding < 0)
-               return NULL;
+               return false;
 
        /*
-        * Note: if we are in startup phase then SetClientEncoding may not be able
-        * to really set the encoding.  In this case we will assume that the
-        * encoding is okay, and InitializeClientEncoding() will fix things once
-        * initialization is complete.
+        * If we are not within a transaction then PrepareClientEncoding will not
+        * be able to look up the necessary conversion procs.  If we are still
+        * starting up, it will return "OK" anyway, and InitializeClientEncoding
+        * will fix things once initialization is far enough along.  After
+        * startup, we'll fail.  This would only happen if someone tries to change
+        * client_encoding in postgresql.conf and then SIGHUP existing sessions.
+        * It seems like a bad idea for client_encoding to change that way anyhow,
+        * so we don't go out of our way to support it.
+        *
+        * Note: in the postmaster, or any other process that never calls
+        * InitializeClientEncoding, PrepareClientEncoding will always succeed,
+        * and so will SetClientEncoding; but they won't do anything, which is OK.
         */
-       if (SetClientEncoding(encoding, doit) < 0)
+       if (PrepareClientEncoding(encoding) < 0)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("conversion between %s and %s is not supported",
-                                               value, GetDatabaseEncodingName())));
-               return NULL;
+               if (IsTransactionState())
+               {
+                       /* Must be a genuine no-such-conversion problem */
+                       GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
+                       GUC_check_errdetail("Conversion between %s and %s is not supported.",
+                                                               pg_encoding_to_char(encoding),
+                                                               GetDatabaseEncodingName());
+               }
+               else
+               {
+                       /* Provide a useful complaint */
+                       GUC_check_errdetail("Cannot change \"client_encoding\" now.");
+               }
+               return false;
        }
-       return value;
+
+       /*
+        * Return the encoding's canonical name, and save its ID in *extra.
+        */
+       free(*newval);
+       *newval = strdup(pg_encoding_to_char(encoding));
+       if (!*newval)
+               return false;
+
+       *extra = malloc(sizeof(int));
+       if (!*extra)
+               return false;
+       *((int *) *extra) = encoding;
+
+       return true;
+}
+
+void
+assign_client_encoding(const char *newval, void *extra)
+{
+       int                     encoding = *((int *) extra);
+
+       /* We do not expect an error if PrepareClientEncoding succeeded */
+       if (SetClientEncoding(encoding) < 0)
+               elog(LOG, "SetClientEncoding(%d) failed", encoding);
 }
 
 
 /*
  * SET SESSION AUTHORIZATION
- *
- * When resetting session auth after an error, we can't expect to do catalog
- * lookups.  Hence, the stored form of the value must provide a numeric oid
- * that can be re-used directly.  We store the string in the form of
- * NAMEDATALEN 'x's, followed by T or F to indicate superuserness, followed
- * by the numeric oid, followed by a comma, followed by the role name.
- * This cannot be confused with a plain role name because of the NAMEDATALEN
- * limit on names, so we can tell whether we're being passed an initial
- * role name or a saved/restored value.  (NOTE: we rely on guc.c to have
- * properly truncated any incoming value, but not to truncate already-stored
- * values.     See GUC_IS_NAME processing.)
  */
-extern char *session_authorization_string;             /* in guc.c */
 
-const char *
-assign_session_authorization(const char *value, bool doit, GucSource source)
+typedef struct
 {
-       Oid                     roleid = InvalidOid;
-       bool            is_superuser = false;
-       const char *actual_rolename = NULL;
-       char       *result;
+       /* This is the "extra" state for both SESSION AUTHORIZATION and ROLE */
+       Oid                     roleid;
+       bool            is_superuser;
+} role_auth_extra;
 
-       if (strspn(value, "x") == NAMEDATALEN &&
-               (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
-       {
-               /* might be a saved userid string */
-               Oid                     savedoid;
-               char       *endptr;
+bool
+check_session_authorization(char **newval, void **extra, GucSource source)
+{
+       HeapTuple       roleTup;
+       Oid                     roleid;
+       bool            is_superuser;
+       role_auth_extra *myextra;
 
-               savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
+       /* Do nothing for the boot_val default of NULL */
+       if (*newval == NULL)
+               return true;
 
-               if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
-               {
-                       /* syntactically valid, so break out the data */
-                       roleid = savedoid;
-                       is_superuser = (value[NAMEDATALEN] == 'T');
-                       actual_rolename = endptr + 1;
-               }
+       if (!IsTransactionState())
+       {
+               /*
+                * Can't do catalog lookups, so fail.  The result of this is that
+                * session_authorization cannot be set in postgresql.conf, which
+                * seems like a good thing anyway, so we don't work hard to avoid it.
+                */
+               return false;
        }
 
-       if (roleid == InvalidOid)
+       /* Look up the username */
+       roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
+       if (!HeapTupleIsValid(roleTup))
        {
-               /* not a saved ID, so look it up */
-               HeapTuple       roleTup;
-
-               if (!IsTransactionState())
-               {
-                       /*
-                        * Can't do catalog lookups, so fail.  The upshot of this is that
-                        * session_authorization cannot be set in postgresql.conf, which
-                        * seems like a good thing anyway.
-                        */
-                       return NULL;
-               }
-
-               roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(value));
-               if (!HeapTupleIsValid(roleTup))
-               {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("role \"%s\" does not exist", value)));
-                       return NULL;
-               }
-
-               roleid = HeapTupleGetOid(roleTup);
-               is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
-               actual_rolename = value;
-
-               ReleaseSysCache(roleTup);
+               GUC_check_errmsg("role \"%s\" does not exist", *newval);
+               return false;
        }
 
-       if (doit)
-               SetSessionAuthorization(roleid, is_superuser);
+       roleid = HeapTupleGetOid(roleTup);
+       is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
 
-       result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
-       if (!result)
-               return NULL;
-
-       memset(result, 'x', NAMEDATALEN);
+       ReleaseSysCache(roleTup);
 
-       sprintf(result + NAMEDATALEN, "%c%u,%s",
-                       is_superuser ? 'T' : 'F',
-                       roleid,
-                       actual_rolename);
+       /* Set up "extra" struct for assign_session_authorization to use */
+       myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
+       if (!myextra)
+               return false;
+       myextra->roleid = roleid;
+       myextra->is_superuser = is_superuser;
+       *extra = (void *) myextra;
 
-       return result;
+       return true;
 }
 
-const char *
-show_session_authorization(void)
+void
+assign_session_authorization(const char *newval, void *extra)
 {
-       /*
-        * Extract the user name from the stored string; see
-        * assign_session_authorization
-        */
-       const char *value = session_authorization_string;
-       Oid                     savedoid;
-       char       *endptr;
+       role_auth_extra *myextra = (role_auth_extra *) extra;
 
-       /* If session_authorization hasn't been set in this process, return "" */
-       if (value == NULL || value[0] == '\0')
-               return "";
+       /* Do nothing for the boot_val default of NULL */
+       if (!myextra)
+               return;
 
-       Assert(strspn(value, "x") == NAMEDATALEN &&
-                  (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
-
-       savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
-
-       Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
-
-       return endptr + 1;
+       SetSessionAuthorization(myextra->roleid, myextra->is_superuser);
 }
 
 
 /*
  * SET ROLE
  *
- * When resetting session auth after an error, we can't expect to do catalog
- * lookups.  Hence, the stored form of the value must provide a numeric oid
- * that can be re-used directly.  We implement this exactly like SET
- * SESSION AUTHORIZATION.
- *
  * The SQL spec requires "SET ROLE NONE" to unset the role, so we hardwire
- * a translation of "none" to InvalidOid.
+ * a translation of "none" to InvalidOid.  Otherwise this is much like
+ * SET SESSION AUTHORIZATION.
  */
 extern char *role_string;              /* in guc.c */
 
-const char *
-assign_role(const char *value, bool doit, GucSource source)
+bool
+check_role(char **newval, void **extra, GucSource source)
 {
-       Oid                     roleid = InvalidOid;
-       bool            is_superuser = false;
-       const char *actual_rolename = value;
-       char       *result;
+       HeapTuple       roleTup;
+       Oid                     roleid;
+       bool            is_superuser;
+       role_auth_extra *myextra;
 
-       if (strspn(value, "x") == NAMEDATALEN &&
-               (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
+       if (strcmp(*newval, "none") == 0)
        {
-               /* might be a saved userid string */
-               Oid                     savedoid;
-               char       *endptr;
-
-               savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
-
-               if (endptr != value + NAMEDATALEN + 1 && *endptr == ',')
-               {
-                       /* syntactically valid, so break out the data */
-                       roleid = savedoid;
-                       is_superuser = (value[NAMEDATALEN] == 'T');
-                       actual_rolename = endptr + 1;
-               }
+               /* hardwired translation */
+               roleid = InvalidOid;
+               is_superuser = false;
        }
-
-       if (roleid == InvalidOid &&
-               strcmp(actual_rolename, "none") != 0)
+       else
        {
-               /* not a saved ID, so look it up */
-               HeapTuple       roleTup;
-
                if (!IsTransactionState())
                {
                        /*
-                        * Can't do catalog lookups, so fail.  The upshot of this is that
+                        * Can't do catalog lookups, so fail.  The result of this is that
                         * role cannot be set in postgresql.conf, which seems like a good
-                        * thing anyway.
+                        * thing anyway, so we don't work hard to avoid it.
                         */
-                       return NULL;
+                       return false;
                }
 
-               roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(value));
+               /* Look up the username */
+               roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval));
                if (!HeapTupleIsValid(roleTup))
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                        errmsg("role \"%s\" does not exist", value)));
-                       return NULL;
+                       GUC_check_errmsg("role \"%s\" does not exist", *newval);
+                       return false;
                }
 
                roleid = HeapTupleGetOid(roleTup);
@@ -947,61 +947,45 @@ assign_role(const char *value, bool doit, GucSource source)
                 */
                if (!is_member_of_role(GetSessionUserId(), roleid))
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("permission denied to set role \"%s\"",
-                                                       value)));
-                       return NULL;
+                       GUC_check_errcode(ERRCODE_INSUFFICIENT_PRIVILEGE);
+                       GUC_check_errmsg("permission denied to set role \"%s\"",
+                                                        *newval);
+                       return false;
                }
        }
 
-       if (doit)
-               SetCurrentRoleId(roleid, is_superuser);
-
-       result = (char *) malloc(NAMEDATALEN + 32 + strlen(actual_rolename));
-       if (!result)
-               return NULL;
+       /* Set up "extra" struct for assign_role to use */
+       myextra = (role_auth_extra *) malloc(sizeof(role_auth_extra));
+       if (!myextra)
+               return false;
+       myextra->roleid = roleid;
+       myextra->is_superuser = is_superuser;
+       *extra = (void *) myextra;
 
-       memset(result, 'x', NAMEDATALEN);
+       return true;
+}
 
-       sprintf(result + NAMEDATALEN, "%c%u,%s",
-                       is_superuser ? 'T' : 'F',
-                       roleid,
-                       actual_rolename);
+void
+assign_role(const char *newval, void *extra)
+{
+       role_auth_extra *myextra = (role_auth_extra *) extra;
 
-       return result;
+       SetCurrentRoleId(myextra->roleid, myextra->is_superuser);
 }
 
 const char *
 show_role(void)
 {
        /*
-        * Extract the role name from the stored string; see assign_role
-        */
-       const char *value = role_string;
-       Oid                     savedoid;
-       char       *endptr;
-
-       /* This special case only applies if no SET ROLE has been done */
-       if (value == NULL || strcmp(value, "none") == 0)
-               return "none";
-
-       Assert(strspn(value, "x") == NAMEDATALEN &&
-                  (value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'));
-
-       savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
-
-       Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
-
-       /*
-        * Check that the stored string still matches the effective setting, else
-        * return "none".  This is a kluge to deal with the fact that SET SESSION
-        * AUTHORIZATION logically resets SET ROLE to NONE, but we cannot set the
-        * GUC role variable from assign_session_authorization (because we haven't
-        * got enough info to call set_config_option).
+        * Check whether SET ROLE is active; if not return "none".  This is a
+        * kluge to deal with the fact that SET SESSION AUTHORIZATION logically
+        * resets SET ROLE to NONE, but we cannot set the GUC role variable from
+        * assign_session_authorization (because we haven't got enough info to
+        * call set_config_option).
         */
-       if (savedoid != GetCurrentRoleId())
+       if (!OidIsValid(GetCurrentRoleId()))
                return "none";
 
-       return endptr + 1;
+       /* Otherwise we can just use the GUC string */
+       return role_string ? role_string : "none";
 }
index eec8a5e2b0dd409945b284c9a2e296385494e890..cf09335a01cd358141b1e86d04f2f610d72ac9f6 100644 (file)
@@ -2,7 +2,10 @@
 CATALOG_NAME   := postgres
 AVAIL_LANGUAGES        := de es fr ja pt_BR tr
 GETTEXT_FILES  := + gettext-files
-GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log errdetail_plural:1,2 errhint errcontext write_stderr yyerror parser_yyerror
+GETTEXT_TRIGGERS:= _ errmsg errmsg_plural:1,2 errdetail errdetail_log \
+    errdetail_plural:1,2 errhint errcontext \
+    GUC_check_errmsg GUC_check_errdetail GUC_check_errhint \
+    write_stderr yyerror parser_yyerror
 
 gettext-files: distprep
        find $(srcdir)/ $(srcdir)/../port/ -name '*.c' -print >$@
index 0d3381465108919d5c086e8ae01223c32afb2742..192aac9aea3fd6582710af5c3786dd3baf505889 100644 (file)
@@ -639,25 +639,23 @@ SyncRepQueueIsOrderedByLSN(void)
  * ===========================================================
  */
 
-const char *
-assign_synchronous_standby_names(const char *newval, bool doit, GucSource source)
+bool
+check_synchronous_standby_names(char **newval, void **extra, GucSource source)
 {
        char       *rawstring;
        List       *elemlist;
 
        /* Need a modifiable copy of string */
-       rawstring = pstrdup(newval);
+       rawstring = pstrdup(*newval);
 
        /* Parse string into list of identifiers */
        if (!SplitIdentifierString(rawstring, ',', &elemlist))
        {
                /* syntax error in list */
+               GUC_check_errdetail("List syntax is invalid.");
                pfree(rawstring);
                list_free(elemlist);
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                  errmsg("invalid list syntax for parameter \"synchronous_standby_names\"")));
-               return NULL;
+               return false;
        }
 
        /*
@@ -671,5 +669,5 @@ assign_synchronous_standby_names(const char *newval, bool doit, GucSource source
        pfree(rawstring);
        list_free(elemlist);
 
-       return newval;
+       return true;
 }
index 79c0f68966ea33e0ad7f76625b1e582eda6da4d5..cc7945aeb669cc9d558854dcf51cf69e12c3f801 100644 (file)
@@ -2804,7 +2804,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
                                break;
 
                        default:
-                               elog(FATAL, "Unknown conflict mode");
+                               elog(FATAL, "unrecognized conflict mode: %d",
+                                        (int) reason);
                }
 
                Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
@@ -3062,27 +3063,32 @@ check_stack_depth(void)
 #endif /* IA64 */
 }
 
-/* GUC assign hook for max_stack_depth */
+/* GUC check hook for max_stack_depth */
 bool
-assign_max_stack_depth(int newval, bool doit, GucSource source)
+check_max_stack_depth(int *newval, void **extra, GucSource source)
 {
-       long            newval_bytes = newval * 1024L;
+       long            newval_bytes = *newval * 1024L;
        long            stack_rlimit = get_stack_depth_rlimit();
 
        if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("\"max_stack_depth\" must not exceed %ldkB",
-                                               (stack_rlimit - STACK_DEPTH_SLOP) / 1024L),
-                                errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.")));
+               GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.",
+                                                       (stack_rlimit - STACK_DEPTH_SLOP) / 1024L);
+               GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.");
                return false;
        }
-       if (doit)
-               max_stack_depth_bytes = newval_bytes;
        return true;
 }
 
+/* GUC assign hook for max_stack_depth */
+void
+assign_max_stack_depth(int newval, void *extra)
+{
+       long            newval_bytes = newval * 1024L;
+
+       max_stack_depth_bytes = newval_bytes;
+}
+
 
 /*
  * set_debug_options --- apply "-d N" command line option
index f0fe2e31a283293570a343f2ba4b215c647b28c0..0410b8384e9d18dfb62f8bc6e59239f2da17aa86 100644 (file)
@@ -4140,20 +4140,17 @@ CheckDateTokenTables(void)
 /*
  * This function gets called during timezone config file load or reload
  * to create the final array of timezone tokens.  The argument array
- * is already sorted in name order.  This data is in a temporary memory
- * context and must be copied to somewhere permanent.
+ * is already sorted in name order.  The data is converted to datetkn
+ * format and installed in *tbl, which must be allocated by the caller.
  */
 void
-InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n)
+ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
+                                          struct tzEntry *abbrevs, int n)
 {
-       datetkn    *newtbl;
+       datetkn    *newtbl = tbl->abbrevs;
        int                     i;
 
-       /*
-        * Copy the data into TopMemoryContext and convert to datetkn format.
-        */
-       newtbl = (datetkn *) MemoryContextAlloc(TopMemoryContext,
-                                                                                       n * sizeof(datetkn));
+       tbl->numabbrevs = n;
        for (i = 0; i < n; i++)
        {
                strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN);
@@ -4163,12 +4160,20 @@ InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n)
 
        /* Check the ordering, if testing */
        Assert(CheckDateTokenTable("timezone offset", newtbl, n));
+}
+
+/*
+ * Install a TimeZoneAbbrevTable as the active table.
+ *
+ * Caller is responsible that the passed table doesn't go away while in use.
+ */
+void
+InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
+{
+       int                     i;
 
-       /* Now safe to replace existing table (if any) */
-       if (timezonetktbl)
-               pfree(timezonetktbl);
-       timezonetktbl = newtbl;
-       sztimezonetktbl = n;
+       timezonetktbl = tbl->abbrevs;
+       sztimezonetktbl = tbl->numabbrevs;
 
        /* clear date cache in case it contains any stale timezone names */
        for (i = 0; i < MAXDATEFIELDS; i++)
index 163856d5b18d24ce2a6e5667bd75a71d2c153a39..cbf74a07f2dcde385578562987d6ce486501505b 100644 (file)
@@ -236,52 +236,53 @@ check_locale(int category, const char *value)
        return ret;
 }
 
-/* GUC assign hooks */
 
 /*
- * This is common code for several locale categories.  This doesn't
- * actually set the locale permanently, it only tests if the locale is
- * valid.  (See explanation at the top of this file.)
+ * GUC check/assign hooks
+ *
+ * For most locale categories, the assign hook doesn't actually set the locale
+ * permanently, just reset flags so that the next use will cache the
+ * appropriate values.  (See explanation at the top of this file.)
  *
  * Note: we accept value = "" as selecting the postmaster's environment
  * value, whatever it was (so long as the environment setting is legal).
  * This will have been locked down by an earlier call to pg_perm_setlocale.
  */
-static const char *
-locale_xxx_assign(int category, const char *value, bool doit, GucSource source)
+bool
+check_locale_monetary(char **newval, void **extra, GucSource source)
 {
-       if (!check_locale(category, value))
-               value = NULL;                   /* set failure return marker */
-
-       /* need to reload cache next time? */
-       if (doit && value != NULL)
-       {
-               CurrentLocaleConvValid = false;
-               CurrentLCTimeValid = false;
-       }
-
-       return value;
+       return check_locale(LC_MONETARY, *newval);
 }
 
+void
+assign_locale_monetary(const char *newval, void *extra)
+{
+       CurrentLocaleConvValid = false;
+}
 
-const char *
-locale_monetary_assign(const char *value, bool doit, GucSource source)
+bool
+check_locale_numeric(char **newval, void **extra, GucSource source)
 {
-       return locale_xxx_assign(LC_MONETARY, value, doit, source);
+       return check_locale(LC_NUMERIC, *newval);
 }
 
-const char *
-locale_numeric_assign(const char *value, bool doit, GucSource source)
+void
+assign_locale_numeric(const char *newval, void *extra)
 {
-       return locale_xxx_assign(LC_NUMERIC, value, doit, source);
+       CurrentLocaleConvValid = false;
 }
 
-const char *
-locale_time_assign(const char *value, bool doit, GucSource source)
+bool
+check_locale_time(char **newval, void **extra, GucSource source)
 {
-       return locale_xxx_assign(LC_TIME, value, doit, source);
+       return check_locale(LC_TIME, *newval);
 }
 
+void
+assign_locale_time(const char *newval, void *extra)
+{
+       CurrentLCTimeValid = false;
+}
 
 /*
  * We allow LC_MESSAGES to actually be set globally.
@@ -293,31 +294,39 @@ locale_time_assign(const char *value, bool doit, GucSource source)
  * The idea there is just to accept the environment setting *if possible*
  * during startup, until we can read the proper value from postgresql.conf.
  */
-const char *
-locale_messages_assign(const char *value, bool doit, GucSource source)
+bool
+check_locale_messages(char **newval, void **extra, GucSource source)
 {
-       if (*value == '\0' && source != PGC_S_DEFAULT)
-               return NULL;
+       if (**newval == '\0')
+       {
+               if (source == PGC_S_DEFAULT)
+                       return true;
+               else
+                       return false;
+       }
 
        /*
         * LC_MESSAGES category does not exist everywhere, but accept it anyway
         *
-        * On Windows, we can't even check the value, so the non-doit case is a
-        * no-op
+        * On Windows, we can't even check the value, so accept blindly
+        */
+#if defined(LC_MESSAGES) && !defined(WIN32)
+       return check_locale(LC_MESSAGES, *newval);
+#else
+       return true;
+#endif
+}
+
+void
+assign_locale_messages(const char *newval, void *extra)
+{
+       /*
+        * LC_MESSAGES category does not exist everywhere, but accept it anyway.
+        * We ignore failure, as per comment above.
         */
 #ifdef LC_MESSAGES
-       if (doit)
-       {
-               if (!pg_perm_setlocale(LC_MESSAGES, value))
-                       if (source != PGC_S_DEFAULT)
-                               return NULL;
-       }
-#ifndef WIN32
-       else
-               value = locale_xxx_assign(LC_MESSAGES, value, false, source);
-#endif   /* WIN32 */
-#endif   /* LC_MESSAGES */
-       return value;
+       (void) pg_perm_setlocale(LC_MESSAGES, newval);
+#endif
 }
 
 
index 6a99e78c6a42c7fac68671948850d7a851bb9ad6..fc93551d069d2de1bce3de51dacd8385fab6dea9 100644 (file)
@@ -587,8 +587,9 @@ getTSCurrentConfig(bool emitError)
        return TSCurrentConfigCache;
 }
 
-const char *
-assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
+/* GUC check_hook for default_text_search_config */
+bool
+check_TSCurrentConfig(char **newval, void **extra, GucSource source)
 {
        /*
         * If we aren't inside a transaction, we cannot do database access so
@@ -601,10 +602,10 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
                Form_pg_ts_config cfg;
                char       *buf;
 
-               cfgId = get_ts_config_oid(stringToQualifiedNameList(newval), true);
+               cfgId = get_ts_config_oid(stringToQualifiedNameList(*newval), true);
 
                if (!OidIsValid(cfgId))
-                       return NULL;
+                       return false;
 
                /*
                 * Modify the actually stored value to be fully qualified, to ensure
@@ -622,17 +623,20 @@ assignTSCurrentConfig(const char *newval, bool doit, GucSource source)
                ReleaseSysCache(tuple);
 
                /* GUC wants it malloc'd not palloc'd */
-               newval = strdup(buf);
+               free(*newval);
+               *newval = strdup(buf);
                pfree(buf);
-
-               if (doit && newval)
-                       TSCurrentConfigCache = cfgId;
-       }
-       else
-       {
-               if (doit)
-                       TSCurrentConfigCache = InvalidOid;
+               if (!newval)
+                       return false;
        }
 
-       return newval;
+       return true;
+}
+
+/* GUC assign_hook for default_text_search_config */
+void
+assign_TSCurrentConfig(const char *newval, void *extra)
+{
+       /* Just reset the cache to force a lookup on first use */
+       TSCurrentConfigCache = InvalidOid;
 }
index e7bc046d87007d40431d262a9a0e80aa6edac12f..9e58735aeec2fdc48e9838a5d1997e52db51afb9 100644 (file)
@@ -1156,6 +1156,62 @@ elog_finish(int elevel, const char *fmt,...)
        errfinish(0);
 }
 
+
+/*
+ * Functions to allow construction of error message strings separately from
+ * the ereport() call itself.
+ *
+ * The expected calling convention is
+ *
+ *     pre_format_elog_string(errno, domain), var = format_elog_string(format,...)
+ *
+ * which can be hidden behind a macro such as GUC_check_errdetail().  We
+ * assume that any functions called in the arguments of format_elog_string()
+ * cannot result in re-entrant use of these functions --- otherwise the wrong
+ * text domain might be used, or the wrong errno substituted for %m.  This is
+ * okay for the current usage with GUC check hooks, but might need further
+ * effort someday.
+ *
+ * The result of format_elog_string() is stored in ErrorContext, and will
+ * therefore survive until FlushErrorState() is called.
+ */
+static int save_format_errnumber;
+static const char *save_format_domain;
+
+void
+pre_format_elog_string(int errnumber, const char *domain)
+{
+       /* Save errno before evaluation of argument functions can change it */
+       save_format_errnumber = errnumber;
+       /* Save caller's text domain */
+       save_format_domain = domain;
+}
+
+char *
+format_elog_string(const char *fmt, ...)
+{
+       ErrorData       errdata;
+       ErrorData  *edata;
+       MemoryContext oldcontext;
+
+       /* Initialize a mostly-dummy error frame */
+       edata = &errdata;
+       MemSet(edata, 0, sizeof(ErrorData));
+       /* the default text domain is the backend's */
+       edata->domain = save_format_domain ? save_format_domain : PG_TEXTDOMAIN("postgres");
+       /* set the errno to be used to interpret %m */
+       edata->saved_errno = save_format_errnumber;
+
+       oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+       EVALUATE_MESSAGE(message, false, true);
+
+       MemoryContextSwitchTo(oldcontext);
+
+       return edata->message;
+}
+
+
 /*
  * Actual output of the top-of-stack error message
  *
index a60188df1099a944f6b0b6fec157052ce31f4381..b1281778036a398cf4f31fafcf8a728ac742affb 100644 (file)
@@ -77,12 +77,16 @@ static int  cliplen(const char *str, int len, int limit);
 
 
 /*
- * Set the client encoding and save fmgrinfo for the conversion
- * function if necessary.  Returns 0 if okay, -1 if not (bad encoding
- * or can't support conversion)
+ * Prepare for a future call to SetClientEncoding.  Success should mean
+ * that SetClientEncoding is guaranteed to succeed for this encoding request.
+ *
+ * (But note that success before backend_startup_complete does not guarantee
+ * success after ...)
+ *
+ * Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
  */
 int
-SetClientEncoding(int encoding, bool doit)
+PrepareClientEncoding(int encoding)
 {
        int                     current_server_encoding;
        ListCell   *lc;
@@ -92,11 +96,7 @@ SetClientEncoding(int encoding, bool doit)
 
        /* Can't do anything during startup, per notes above */
        if (!backend_startup_complete)
-       {
-               if (doit)
-                       pending_client_encoding = encoding;
                return 0;
-       }
 
        current_server_encoding = GetDatabaseEncoding();
 
@@ -106,15 +106,7 @@ SetClientEncoding(int encoding, bool doit)
        if (current_server_encoding == encoding ||
                current_server_encoding == PG_SQL_ASCII ||
                encoding == PG_SQL_ASCII)
-       {
-               if (doit)
-               {
-                       ClientEncoding = &pg_enc2name_tbl[encoding];
-                       ToServerConvProc = NULL;
-                       ToClientConvProc = NULL;
-               }
                return 0;
-       }
 
        if (IsTransactionState())
        {
@@ -138,12 +130,6 @@ SetClientEncoding(int encoding, bool doit)
                if (!OidIsValid(to_client_proc))
                        return -1;
 
-               /*
-                * Done if not wanting to actually apply setting.
-                */
-               if (!doit)
-                       return 0;
-
                /*
                 * Load the fmgr info into TopMemoryContext (could still fail here)
                 */
@@ -162,30 +148,9 @@ SetClientEncoding(int encoding, bool doit)
                MemoryContextSwitchTo(oldcontext);
 
                /*
-                * Everything is okay, so apply the setting.
+                * We cannot yet remove any older entry for the same encoding pair,
+                * since it could still be in use.  SetClientEncoding will clean up.
                 */
-               ClientEncoding = &pg_enc2name_tbl[encoding];
-               ToServerConvProc = &convinfo->to_server_info;
-               ToClientConvProc = &convinfo->to_client_info;
-
-               /*
-                * Remove any older entry for the same encoding pair (this is just to
-                * avoid memory leakage).
-                */
-               foreach(lc, ConvProcList)
-               {
-                       ConvProcInfo *oldinfo = (ConvProcInfo *) lfirst(lc);
-
-                       if (oldinfo == convinfo)
-                               continue;
-                       if (oldinfo->s_encoding == convinfo->s_encoding &&
-                               oldinfo->c_encoding == convinfo->c_encoding)
-                       {
-                               ConvProcList = list_delete_ptr(ConvProcList, oldinfo);
-                               pfree(oldinfo);
-                               break;                  /* need not look further */
-                       }
-               }
 
                return 0;                               /* success */
        }
@@ -205,15 +170,7 @@ SetClientEncoding(int encoding, bool doit)
 
                        if (oldinfo->s_encoding == current_server_encoding &&
                                oldinfo->c_encoding == encoding)
-                       {
-                               if (doit)
-                               {
-                                       ClientEncoding = &pg_enc2name_tbl[encoding];
-                                       ToServerConvProc = &oldinfo->to_server_info;
-                                       ToClientConvProc = &oldinfo->to_client_info;
-                               }
                                return 0;
-                       }
                }
 
                return -1;                              /* it's not cached, so fail */
@@ -221,8 +178,83 @@ SetClientEncoding(int encoding, bool doit)
 }
 
 /*
- * Initialize client encoding if necessary.
- *             called from InitPostgres() once during backend startup.
+ * Set the active client encoding and set up the conversion-function pointers.
+ * PrepareClientEncoding should have been called previously for this encoding.
+ *
+ * Returns 0 if okay, -1 if not (bad encoding or can't support conversion)
+ */
+int
+SetClientEncoding(int encoding)
+{
+       int                     current_server_encoding;
+       bool            found;
+       ListCell   *lc;
+
+       if (!PG_VALID_FE_ENCODING(encoding))
+               return -1;
+
+       /* Can't do anything during startup, per notes above */
+       if (!backend_startup_complete)
+       {
+               pending_client_encoding = encoding;
+               return 0;
+       }
+
+       current_server_encoding = GetDatabaseEncoding();
+
+       /*
+        * Check for cases that require no conversion function.
+        */
+       if (current_server_encoding == encoding ||
+               current_server_encoding == PG_SQL_ASCII ||
+               encoding == PG_SQL_ASCII)
+       {
+               ClientEncoding = &pg_enc2name_tbl[encoding];
+               ToServerConvProc = NULL;
+               ToClientConvProc = NULL;
+               return 0;
+       }
+
+       /*
+        * Search the cache for the entry previously prepared by
+        * PrepareClientEncoding; if there isn't one, we lose.  While at it,
+        * release any duplicate entries so that repeated Prepare/Set cycles
+        * don't leak memory.
+        */
+       found = false;
+       foreach(lc, ConvProcList)
+       {
+               ConvProcInfo *convinfo = (ConvProcInfo *) lfirst(lc);
+
+               if (convinfo->s_encoding == current_server_encoding &&
+                       convinfo->c_encoding == encoding)
+               {
+                       if (!found)
+                       {
+                               /* Found newest entry, so set up */
+                               ClientEncoding = &pg_enc2name_tbl[encoding];
+                               ToServerConvProc = &convinfo->to_server_info;
+                               ToClientConvProc = &convinfo->to_client_info;
+                               found = true;
+                       }
+                       else
+                       {
+                               /* Duplicate entry, release it */
+                               ConvProcList = list_delete_ptr(ConvProcList, convinfo);
+                               pfree(convinfo);
+                       }
+               }
+       }
+
+       if (found)
+               return 0;                               /* success */
+       else
+               return -1;                              /* it's not cached, so fail */
+}
+
+/*
+ * Initialize client encoding conversions.
+ *             Called from InitPostgres() once during backend startup.
  */
 void
 InitializeClientEncoding(void)
@@ -230,7 +262,8 @@ InitializeClientEncoding(void)
        Assert(!backend_startup_complete);
        backend_startup_complete = true;
 
-       if (SetClientEncoding(pending_client_encoding, true) < 0)
+       if (PrepareClientEncoding(pending_client_encoding) < 0 ||
+               SetClientEncoding(pending_client_encoding) < 0)
        {
                /*
                 * Oops, the requested conversion is not available. We couldn't fail
index 881862a30b182a94cd71c6ed1a98dd6e6d5e51e0..70244ced18d869c5e46e8f1a6a455f9704c27ae0 100644 (file)
@@ -1,10 +1,10 @@
 src/backend/utils/misc/README
 
-Guc Implementation Notes
+GUC Implementation Notes
 ========================
 
 The GUC (Grand Unified Configuration) module implements configuration
-variables of multiple types (currently boolean, enum, int, float, and string).
+variables of multiple types (currently boolean, enum, int, real, and string).
 Variable settings can come from various places, with a priority ordering
 determining which setting is used.
 
@@ -12,65 +12,112 @@ determining which setting is used.
 Per-Variable Hooks
 ------------------
 
-Each variable known to GUC can optionally have an assign_hook and/or
-a show_hook to provide customized behavior.  Assign hooks are used to
-perform validity checking on variable values (above and beyond what
-GUC can do).  They are also used to update any derived state that needs
-to change when a GUC variable is set.  Show hooks are used to modify
-the default SHOW display for a variable.
+Each variable known to GUC can optionally have a check_hook, an
+assign_hook, and/or a show_hook to provide customized behavior.
+Check hooks are used to perform validity checking on variable values
+(above and beyond what GUC can do), to compute derived settings when
+nontrivial work is needed to do that, and optionally to "canonicalize"
+user-supplied values.  Assign hooks are used to update any derived state
+that needs to change when a GUC variable is set.  Show hooks are used to
+modify the default SHOW display for a variable.
+
+
+If a check_hook is provided, it points to a function of the signature
+       bool check_hook(datatype *newvalue, void **extra, GucSource source)
+The "newvalue" argument is of type bool *, int *, double *, or char **
+for bool, int/enum, real, or string variables respectively.  The check
+function should validate the proposed new value, and return true if it is
+OK or false if not.  The function can optionally do a few other things:
+
+* When rejecting a bad proposed value, it may be useful to append some
+additional information to the generic "invalid value for parameter FOO"
+complaint that guc.c will emit.  To do that, call
+       void GUC_check_errdetail(const char *format, ...)
+where the format string and additional arguments follow the rules for
+errdetail() arguments.  The resulting string will be emitted as the
+DETAIL line of guc.c's error report, so it should follow the message style
+guidelines for DETAIL messages.  There is also
+       void GUC_check_errhint(const char *format, ...)
+which can be used in the same way to append a HINT message.
+Occasionally it may even be appropriate to override guc.c's generic primary
+message or error code, which can be done with
+       void GUC_check_errcode(int sqlerrcode)
+       void GUC_check_errmsg(const char *format, ...)
+In general, check_hooks should avoid throwing errors directly if possible,
+though this may be impractical to avoid for some corner cases such as
+out-of-memory.
+
+* Since the newvalue is pass-by-reference, the function can modify it.
+This might be used for example to canonicalize the spelling of a string
+value, round off a buffer size to the nearest supported value, or replace
+a special value such as "-1" with a computed default value.  If the
+function wishes to replace a string value, it must malloc (not palloc)
+the replacement value, and be sure to free() the previous value.
+
+* Derived information, such as the role OID represented by a user name,
+can be stored for use by the assign hook.  To do this, malloc (not palloc)
+storage space for the information, and return its address at *extra.
+guc.c will automatically free() this space when the associated GUC setting
+is no longer of interest.  *extra is initialized to NULL before call, so
+it can be ignored if not needed.
+
+The "source" argument indicates the source of the proposed new value,
+If it is >= PGC_S_INTERACTIVE, then we are performing an interactive
+assignment (e.g., a SET command).  But when source < PGC_S_INTERACTIVE,
+we are reading a non-interactive option source, such as postgresql.conf.
+This is sometimes needed to determine whether a setting should be
+allowed.  The check_hook might also look at the current actual value of
+the variable to determine what is allowed.
+
+Note that check hooks are sometimes called just to validate a value,
+without any intention of actually changing the setting.  Therefore the
+check hook must *not* take any action based on the assumption that an
+assignment will occur.
+
 
 If an assign_hook is provided, it points to a function of the signature
-       bool assign_hook(newvalue, bool doit, GucSource source)
-where the type of "newvalue" matches the kind of variable.  This function
-is called immediately before actually setting the variable's value (so it
-can look at the actual variable to determine the old value).  If the
-function returns "true" then the assignment is completed; if it returns
-"false" then newvalue is considered invalid and the assignment is not
-performed.  If "doit" is false then the function should simply check
-validity of newvalue and not change any derived state.  The "source" parameter
-indicates where the new value came from.  If it is >= PGC_S_INTERACTIVE,
-then we are performing an interactive assignment (e.g., a SET command), and
-ereport(ERROR) is safe to do.  But when source < PGC_S_INTERACTIVE, we are
-reading a non-interactive option source, such as postgresql.conf.  In this
-case the assign_hook should *not* ereport but should just return false if it
-doesn't like the newvalue.
-
-If an assign_hook returns false then guc.c will report a generic "invalid
-value for option FOO" error message.  If you feel the need to provide a more
-specific error message, ereport() it using "GUC_complaint_elevel(source)"
-as the error level.  Note that this might return either ERROR or a lower level
-such as LOG, so the ereport call might or might not return.  If it does
-return, return false out of the assign_hook.
-
-For string variables, the signature for assign hooks is a bit different:
-       const char *assign_hook(const char *newvalue,
-                               bool doit,
-                               GucSource source)
-The meanings of the parameters are the same as for the other types of GUC
-variables, but the return value is handled differently:
-       NULL --- assignment fails (like returning false for other datatypes)
-       newvalue --- assignment succeeds, assign the newvalue as-is
-       malloc'd (not palloc'd!!!) string --- assign that value instead
-The third choice is allowed in case the assign_hook wants to return a
-"canonical" version of the new value.  For example, the assign_hook for
-datestyle always returns a string that includes both output and input
-datestyle options, although the input might have specified only one.
-
-Note that a string variable's assign_hook will NEVER be called with a NULL
-value for newvalue, since there would be no way to distinguish success
-and failure returns.  If the boot_val or reset_val for a string variable
-is NULL, it will just be assigned without calling the assign_hook.
-Therefore, a NULL boot_val should never be used in combination with an
-assign_hook that has side-effects, as the side-effects wouldn't happen
-during a RESET that re-institutes the boot-time setting.
+       void assign_hook(datatype newvalue, void *extra)
+where the type of "newvalue" matches the kind of variable, and "extra"
+is the derived-information pointer returned by the check_hook (always
+NULL if there is no check_hook).  This function is called immediately
+before actually setting the variable's value (so it can look at the actual
+variable to determine the old value, for example to avoid doing work when
+the value isn't really changing).
+
+Note that there is no provision for a failure result code.  assign_hooks
+should never fail except under the most dire circumstances, since a failure
+may for example result in GUC settings not being rolled back properly during
+transaction abort.  In general, try to do anything that could conceivably
+fail in a check_hook instead, and pass along the results in an "extra"
+struct, so that the assign hook has little to do beyond copying the data to
+someplace.  This applies particularly to catalog lookups: any required
+lookups must be done in the check_hook, since the assign_hook may be
+executed during transaction rollback when lookups will be unsafe.
+
+Note that check_hooks are sometimes called outside any transaction, too.
+This happens when processing the wired-in "bootstrap" value, values coming
+from the postmaster command line or environment, or values coming from
+postgresql.conf.  Therefore, any catalog lookups done in a check_hook
+should be guarded with an IsTransactionState() test, and there must be a
+fallback path to allow derived values to be computed during the first
+subsequent use of the GUC setting within a transaction.  A typical
+arrangement is for the catalog values computed by the check_hook and
+installed by the assign_hook to be used only for the remainder of the
+transaction in which the new setting is made.  Each subsequent transaction
+looks up the values afresh on first use.  This arrangement is useful to
+prevent use of stale catalog values, independently of the problem of
+needing to check GUC values outside a transaction.
+
 
 If a show_hook is provided, it points to a function of the signature
        const char *show_hook(void)
 This hook allows variable-specific computation of the value displayed
-by SHOW.
+by SHOW (and other SQL features for showing GUC variable values).
+The return value can point to a static buffer, since show functions are
+not used re-entrantly.
 
 
-Saving/Restoring Guc Variable Values
+Saving/Restoring GUC Variable Values
 ------------------------------------
 
 Prior values of configuration variables must be remembered in order to deal
@@ -200,27 +247,49 @@ these has a current source priority <= PGC_S_FILE.  (It is thus possible
 for reset_val to track the config-file setting even if there is
 currently a different interactive value of the actual variable.)
 
-The assign_hook and show_hook routines work only with the actual variable,
-and are not directly aware of the additional values maintained by GUC.
-This is not a problem for normal usage, since we can assign first to the
-actual variable and then (if that succeeds) to the additional values as
-needed.  However, for SIGHUP rereads we may not want to assign to the
-actual variable.  Our procedure in that case is to call the assign_hook
-with doit = false so that the value is validated, but no derived state is
-changed.
+The check_hook, assign_hook and show_hook routines work only with the
+actual variable, and are not directly aware of the additional values
+maintained by GUC.
 
 
-String Memory Handling
-----------------------
+GUC Memory Handling
+-------------------
 
-String option values are allocated with strdup, not with the
-pstrdup/palloc mechanisms.  We would need to keep them in a permanent
-context anyway, and strdup gives us more control over handling
+String variable values are allocated with malloc/strdup, not with the
+palloc/pstrdup mechanisms.  We would need to keep them in a permanent
+context anyway, and malloc gives us more control over handling
 out-of-memory failures.
 
 We allow a string variable's actual value, reset_val, boot_val, and stacked
 values to point at the same storage.  This makes it slightly harder to free
 space (we must test whether a value to be freed isn't equal to any of the
 other pointers in the GUC entry or associated stack items).  The main
-advantage is that we never need to strdup during transaction commit/abort,
+advantage is that we never need to malloc during transaction commit/abort,
 so cannot cause an out-of-memory failure there.
+
+"Extra" structs returned by check_hook routines are managed in the same
+way as string values.  Note that we support "extra" structs for all types
+of GUC variables, although they are mainly useful with strings.
+
+
+GUC and Null String Variables
+-----------------------------
+
+A GUC string variable can have a boot_val of NULL.  guc.c handles this
+unsurprisingly, assigning the NULL to the underlying C variable.  Any code
+using such a variable, as well as any hook functions for it, must then be
+prepared to deal with a NULL value.
+
+However, it is not possible to assign a NULL value to a GUC string
+variable in any other way: values coming from SET, postgresql.conf, etc,
+might be empty strings, but they'll never be NULL.  And SHOW displays
+a NULL the same as an empty string.  It is therefore not appropriate to
+treat a NULL value as a distinct user-visible setting.  A typical use
+for a NULL boot_val is to denote that a value hasn't yet been set for
+a variable that will receive a real value later in startup.
+
+If it's undesirable for code using the underlying C variable to have to
+worry about NULL values ever, the variable can be given a non-null static
+initializer as well as a non-null boot_val.  guc.c will overwrite the
+static initializer pointer with a copy of the boot_val during
+InitializeGUCOptions, but the variable will never contain a NULL.
index e660395898274a94ca182641dacdbdc8fefa8bce..10ef12eb24be0e0b4d463b27e111962dd4a1fcb0 100644 (file)
@@ -141,7 +141,8 @@ ProcessConfigFile(GucContext context)
         */
        cvc_struct = (struct config_string *)
                find_option("custom_variable_classes", false, elevel);
-       if (cvc_struct && cvc_struct->gen.reset_source > PGC_S_FILE)
+       Assert(cvc_struct);
+       if (cvc_struct->gen.reset_source > PGC_S_FILE)
        {
                cvc = guc_strdup(elevel, cvc_struct->reset_val);
                if (cvc == NULL)
@@ -151,19 +152,18 @@ ProcessConfigFile(GucContext context)
                         guc_name_compare(head->name, "custom_variable_classes") == 0)
        {
                /*
-                * Need to canonicalize the value via the assign hook.  Casting away
-                * const is a bit ugly, but we know the result is malloc'd.
+                * Need to canonicalize the value by calling the check hook.
                 */
-               cvc = (char *) assign_custom_variable_classes(head->value,
-                                                                                                         false, PGC_S_FILE);
+               void   *extra = NULL;
+
+               cvc = guc_strdup(elevel, head->value);
                if (cvc == NULL)
-               {
-                       ereport(elevel,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                                       head->name, head->value)));
                        goto cleanup_list;
-               }
+               if (!call_string_check_hook(cvc_struct, &cvc, &extra,
+                                                                       PGC_S_FILE, elevel))
+                       goto cleanup_list;
+               if (extra)
+                       free(extra);
        }
 
        /*
index 2151fde36185612f43c3299e8e5db4c1f54a2e57..5e4904aeb7fc8a34828358b68c41661365f573bd 100644 (file)
  * backend ID as a 3-byte signed integer.  Even if that limitation were
  * removed, we still could not exceed INT_MAX/4 because some places compute
  * 4*MaxBackends without any overflow check.  This is rechecked in
- * assign_maxconnections, since MaxBackends is computed as MaxConnections
+ * check_maxconnections, since MaxBackends is computed as MaxConnections
  * plus autovacuum_max_workers plus one (for the autovacuum launcher).
  */
 #define MAX_BACKENDS   0x7fffff
@@ -143,11 +143,29 @@ extern bool trace_syncscan;
 extern bool optimize_bounded_sort;
 #endif
 
+static int     GUC_check_errcode_value;
+
+/* global variables for check hook support */
+char   *GUC_check_errmsg_string;
+char   *GUC_check_errdetail_string;
+char   *GUC_check_errhint_string;
+
+
 static void set_config_sourcefile(const char *name, char *sourcefile,
                                          int sourceline);
-
-static const char *assign_log_destination(const char *value,
-                                          bool doit, GucSource source);
+static bool call_bool_check_hook(struct config_bool *conf, bool *newval,
+                                                                void **extra, GucSource source, int elevel);
+static bool call_int_check_hook(struct config_int *conf, int *newval,
+                                                               void **extra, GucSource source, int elevel);
+static bool call_real_check_hook(struct config_real *conf, double *newval,
+                                                                void **extra, GucSource source, int elevel);
+static bool call_string_check_hook(struct config_string *conf, char **newval,
+                                                                  void **extra, GucSource source, int elevel);
+static bool call_enum_check_hook(struct config_enum *conf, int *newval,
+                                                                void **extra, GucSource source, int elevel);
+
+static bool check_log_destination(char **newval, void **extra, GucSource source);
+static void assign_log_destination(const char *newval, void *extra);
 
 #ifdef HAVE_SYSLOG
 static int     syslog_facility = LOG_LOCAL0;
@@ -155,36 +173,36 @@ static int        syslog_facility = LOG_LOCAL0;
 static int     syslog_facility = 0;
 #endif
 
-static bool assign_syslog_facility(int newval,
-                                          bool doit, GucSource source);
-static const char *assign_syslog_ident(const char *ident,
-                                       bool doit, GucSource source);
-
-static bool assign_session_replication_role(int newval, bool doit,
-                                                               GucSource source);
-static const char *show_num_temp_buffers(void);
-static bool assign_phony_autocommit(bool newval, bool doit, GucSource source);
-static const char *assign_custom_variable_classes(const char *newval, bool doit,
-                                                          GucSource source);
-static bool assign_debug_assertions(bool newval, bool doit, GucSource source);
-static bool assign_bonjour(bool newval, bool doit, GucSource source);
-static bool assign_ssl(bool newval, bool doit, GucSource source);
-static bool assign_stage_log_stats(bool newval, bool doit, GucSource source);
-static bool assign_log_stats(bool newval, bool doit, GucSource source);
-static const char *assign_canonical_path(const char *newval, bool doit, GucSource source);
-static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source);
+static void assign_syslog_facility(int newval, void *extra);
+static void assign_syslog_ident(const char *newval, void *extra);
+static void assign_session_replication_role(int newval, void *extra);
+static bool check_temp_buffers(int *newval, void **extra, GucSource source);
+static bool check_phony_autocommit(bool *newval, void **extra, GucSource source);
+static bool check_custom_variable_classes(char **newval, void **extra, GucSource source);
+static bool check_debug_assertions(bool *newval, void **extra, GucSource source);
+static bool check_bonjour(bool *newval, void **extra, GucSource source);
+static bool check_ssl(bool *newval, void **extra, GucSource source);
+static bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
+static bool check_log_stats(bool *newval, void **extra, GucSource source);
+static bool check_canonical_path(char **newval, void **extra, GucSource source);
+static bool check_timezone_abbreviations(char **newval, void **extra, GucSource source);
+static void assign_timezone_abbreviations(const char *newval, void *extra);
 static const char *show_archive_command(void);
-static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
-static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
-static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source);
+static void assign_tcp_keepalives_idle(int newval, void *extra);
+static void assign_tcp_keepalives_interval(int newval, void *extra);
+static void assign_tcp_keepalives_count(int newval, void *extra);
 static const char *show_tcp_keepalives_idle(void);
 static const char *show_tcp_keepalives_interval(void);
 static const char *show_tcp_keepalives_count(void);
-static bool assign_maxconnections(int newval, bool doit, GucSource source);
-static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source);
-static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source);
-static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source);
-static const char *assign_application_name(const char *newval, bool doit, GucSource source);
+static bool check_maxconnections(int *newval, void **extra, GucSource source);
+static void assign_maxconnections(int newval, void *extra);
+static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
+static void assign_autovacuum_max_workers(int newval, void *extra);
+static bool check_effective_io_concurrency(int *newval, void **extra, GucSource source);
+static void assign_effective_io_concurrency(int newval, void *extra);
+static void assign_pgstat_temp_directory(const char *newval, void *extra);
+static bool check_application_name(char **newval, void **extra, GucSource source);
+static void assign_application_name(const char *newval, void *extra);
 static const char *show_unix_socket_permissions(void);
 static const char *show_log_file_mode(void);
 
@@ -407,7 +425,7 @@ int                 log_min_duration_statement = -1;
 int                    log_temp_files = -1;
 int                    trace_recovery_messages = LOG;
 
-int                    num_temp_buffers = 1000;
+int                    num_temp_buffers = 1024;
 
 char      *data_directory;
 char      *ConfigFileName;
@@ -444,6 +462,8 @@ static int  server_version_num;
 static char *timezone_string;
 static char *log_timezone_string;
 static char *timezone_abbreviations_string;
+static char *XactIsoLevel_string;
+static char *session_authorization_string;
 static char *custom_variable_classes;
 static int     max_function_args;
 static int     max_index_keys;
@@ -455,10 +475,8 @@ static int wal_segment_size;
 static bool integer_datetimes;
 static int     effective_io_concurrency;
 
-/* should be static, but commands/variable.c needs to get at these */
+/* should be static, but commands/variable.c needs to get at this */
 char      *role_string;
-char      *session_authorization_string;
-char      *XactIsoLevel_string;
 
 
 /*
@@ -643,7 +661,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_seqscan,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -651,7 +670,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_indexscan,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -659,7 +679,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_bitmapscan,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -667,7 +688,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_tidscan,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -675,7 +697,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_sort,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -683,7 +706,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_hashagg,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -691,7 +715,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_material,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -699,7 +724,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_nestloop,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -707,7 +733,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_mergejoin,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
@@ -715,7 +742,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_hashjoin,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -724,7 +752,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "exhaustive searching.")
                },
                &enable_geqo,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                /* Not for general use --- used by SET SESSION AUTHORIZATION */
@@ -734,7 +763,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &session_auth_is_superuser,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
@@ -742,7 +772,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &enable_bonjour,
-               false, assign_bonjour, NULL
+               false,
+               check_bonjour, NULL, NULL
        },
        {
                {"ssl", PGC_POSTMASTER, CONN_AUTH_SECURITY,
@@ -750,7 +781,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &EnableSSL,
-               false, assign_ssl, NULL
+               false,
+               check_ssl, NULL, NULL
        },
        {
                {"fsync", PGC_SIGHUP, WAL_SETTINGS,
@@ -761,7 +793,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "an operating system or hardware crash.")
                },
                &enableFsync,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -774,7 +807,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &zero_damaged_pages,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"full_page_writes", PGC_SIGHUP, WAL_SETTINGS,
@@ -786,7 +820,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "is possible.")
                },
                &fullPageWrites,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"silent_mode", PGC_POSTMASTER, LOGGING_WHERE,
@@ -795,7 +830,8 @@ static struct config_bool ConfigureNamesBool[] =
                                 "background and any controlling terminals are dissociated.")
                },
                &SilentMode,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
@@ -803,7 +839,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_checkpoints,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"log_connections", PGC_BACKEND, LOGGING_WHAT,
@@ -811,7 +848,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Log_connections,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"log_disconnections", PGC_BACKEND, LOGGING_WHAT,
@@ -819,7 +857,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Log_disconnections,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_assertions", PGC_USERSET, DEVELOPER_OPTIONS,
@@ -833,7 +872,7 @@ static struct config_bool ConfigureNamesBool[] =
 #else
                false,
 #endif
-               assign_debug_assertions, NULL
+               check_debug_assertions, NULL, NULL
        },
 
        {
@@ -842,7 +881,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &ExitOnAnyError,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"restart_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
@@ -850,7 +890,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &restart_after_crash,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -859,7 +900,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_duration,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
@@ -867,7 +909,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Debug_print_parse,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
@@ -875,7 +918,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Debug_print_rewritten,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
@@ -883,7 +927,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Debug_print_plan,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
@@ -891,7 +936,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Debug_pretty_print,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"log_parser_stats", PGC_SUSET, STATS_MONITORING,
@@ -899,7 +945,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_parser_stats,
-               false, assign_stage_log_stats, NULL
+               false,
+               check_stage_log_stats, NULL, NULL
        },
        {
                {"log_planner_stats", PGC_SUSET, STATS_MONITORING,
@@ -907,7 +954,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_planner_stats,
-               false, assign_stage_log_stats, NULL
+               false,
+               check_stage_log_stats, NULL, NULL
        },
        {
                {"log_executor_stats", PGC_SUSET, STATS_MONITORING,
@@ -915,7 +963,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_executor_stats,
-               false, assign_stage_log_stats, NULL
+               false,
+               check_stage_log_stats, NULL, NULL
        },
        {
                {"log_statement_stats", PGC_SUSET, STATS_MONITORING,
@@ -923,7 +972,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_statement_stats,
-               false, assign_log_stats, NULL
+               false,
+               check_log_stats, NULL, NULL
        },
 #ifdef BTREE_BUILD_STATS
        {
@@ -933,7 +983,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &log_btree_build_stats,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -945,7 +996,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "the time at which that command began execution.")
                },
                &pgstat_track_activities,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"track_counts", PGC_SUSET, STATS_COLLECTOR,
@@ -953,7 +1005,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &pgstat_track_counts,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -962,7 +1015,8 @@ static struct config_bool ConfigureNamesBool[] =
                        gettext_noop("Enables updating of the process title every time a new SQL command is received by the server.")
                },
                &update_process_title,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -971,7 +1025,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &autovacuum_start_daemon,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -981,7 +1036,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_notify,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
 #ifdef LOCK_DEBUG
@@ -992,7 +1048,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_locks,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"trace_userlocks", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -1001,7 +1058,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_userlocks,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"trace_lwlocks", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -1010,7 +1068,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_lwlocks,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"debug_deadlocks", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -1019,7 +1078,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Debug_deadlocks,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1029,7 +1089,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &log_lock_waits,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1041,7 +1102,8 @@ static struct config_bool ConfigureNamesBool[] =
                           "setup it might impose a non-negligible performance penalty.")
                },
                &log_hostname,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"sql_inheritance", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
@@ -1049,7 +1111,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &SQL_inheritance,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"password_encryption", PGC_USERSET, CONN_AUTH_SECURITY,
@@ -1059,7 +1122,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "this parameter determines whether the password is to be encrypted.")
                },
                &Password_encryption,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
@@ -1071,7 +1135,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "return null (unknown).")
                },
                &Transform_null_equals,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"db_user_namespace", PGC_SIGHUP, CONN_AUTH_SECURITY,
@@ -1079,7 +1144,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Db_user_namespace,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                /* only here for backwards compatibility */
@@ -1089,7 +1155,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
                },
                &phony_autocommit,
-               true, assign_phony_autocommit, NULL
+               true,
+               check_phony_autocommit, NULL, NULL
        },
        {
                {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1097,7 +1164,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &DefaultXactReadOnly,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1106,7 +1174,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &XactReadOnly,
-               false, assign_transaction_read_only, NULL
+               false,
+               check_transaction_read_only, NULL, NULL
        },
        {
                {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1114,7 +1183,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &DefaultXactDeferrable,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1123,7 +1193,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &XactDeferrable,
-               false, assign_transaction_deferrable, NULL
+               false,
+               check_transaction_deferrable, NULL, NULL
        },
        {
                {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -1131,7 +1202,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &check_function_bodies,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
@@ -1141,7 +1213,8 @@ static struct config_bool ConfigureNamesBool[] =
                                                 "otherwise it is taken literally.")
                },
                &Array_nulls,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
        {
                {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
@@ -1149,7 +1222,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &default_with_oids,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
@@ -1157,7 +1231,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Logging_collector,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
        {
                {"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE,
@@ -1165,7 +1240,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &Log_truncate_on_rotation,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
 #ifdef TRACE_SORT
@@ -1176,7 +1252,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &trace_sort,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1189,7 +1266,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &trace_syncscan,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1203,7 +1281,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &optimize_bounded_sort,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1215,7 +1294,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &XLOG_DEBUG,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1227,10 +1307,11 @@ static struct config_bool ConfigureNamesBool[] =
                },
                &integer_datetimes,
 #ifdef HAVE_INT64_TIMESTAMP
-               true, NULL, NULL
+               true,
 #else
-               false, NULL, NULL
+               false,
 #endif
+               NULL, NULL, NULL
        },
 
        {
@@ -1239,7 +1320,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &pg_krb_caseins_users,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1248,7 +1330,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &escape_string_warning,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -1258,7 +1341,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_REPORT
                },
                &standard_conforming_strings,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -1267,7 +1351,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &synchronize_seqscans,
-               true, NULL, NULL
+               true,
+               NULL, NULL, NULL
        },
 
        {
@@ -1276,7 +1361,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &XLogArchiveMode,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1285,7 +1371,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &EnableHotStandby,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1294,7 +1381,8 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL
                },
                &hot_standby_feedback,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1304,7 +1392,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &allowSystemTableMods,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1315,7 +1404,8 @@ static struct config_bool ConfigureNamesBool[] =
                        GUC_NOT_IN_SAMPLE
                },
                &IgnoreSystemIndexes,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1325,7 +1415,8 @@ static struct config_bool ConfigureNamesBool[] =
                                  "for compatibility with PostgreSQL releases prior to 9.0.")
                },
                &lo_compat_privileges,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        {
@@ -1334,12 +1425,13 @@ static struct config_bool ConfigureNamesBool[] =
                        NULL,
                },
                &quote_all_identifiers,
-               false, NULL, NULL
+               false,
+               NULL, NULL, NULL
        },
 
        /* End-of-list marker */
        {
-               {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
+               {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
        }
 };
 
@@ -1354,7 +1446,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &XLogArchiveTimeout,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"post_auth_delay", PGC_BACKEND, DEVELOPER_OPTIONS,
@@ -1363,7 +1456,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_UNIT_S
                },
                &PostAuthDelay,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
@@ -1372,7 +1466,8 @@ static struct config_int ConfigureNamesInt[] =
                                "column-specific target set via ALTER TABLE SET STATISTICS.")
                },
                &default_statistics_target,
-               100, 1, 10000, NULL, NULL
+               100, 1, 10000,
+               NULL, NULL, NULL
        },
        {
                {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
@@ -1383,7 +1478,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "this many items.")
                },
                &from_collapse_limit,
-               8, 1, INT_MAX, NULL, NULL
+               8, 1, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
@@ -1394,7 +1490,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "list of no more than this many items would result.")
                },
                &join_collapse_limit,
-               8, 1, INT_MAX, NULL, NULL
+               8, 1, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -1402,7 +1499,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &geqo_threshold,
-               12, 2, INT_MAX, NULL, NULL
+               12, 2, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -1410,7 +1508,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &Geqo_effort,
-               DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT, NULL, NULL
+               DEFAULT_GEQO_EFFORT, MIN_GEQO_EFFORT, MAX_GEQO_EFFORT,
+               NULL, NULL, NULL
        },
        {
                {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -1418,7 +1517,8 @@ static struct config_int ConfigureNamesInt[] =
                        gettext_noop("Zero selects a suitable default value.")
                },
                &Geqo_pool_size,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -1426,7 +1526,8 @@ static struct config_int ConfigureNamesInt[] =
                        gettext_noop("Zero selects a suitable default value.")
                },
                &Geqo_generations,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1437,7 +1538,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &DeadlockTimeout,
-               1000, 1, INT_MAX, NULL, NULL
+               1000, 1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1447,7 +1549,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &max_standby_archive_delay,
-               30 * 1000, -1, INT_MAX, NULL, NULL
+               30 * 1000, -1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1457,7 +1560,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &max_standby_streaming_delay,
-               30 * 1000, -1, INT_MAX, NULL, NULL
+               30 * 1000, -1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1467,7 +1571,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &wal_receiver_status_interval,
-               10, 0, INT_MAX/1000, NULL, NULL
+               10, 0, INT_MAX/1000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1476,7 +1581,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &MaxConnections,
-               100, 1, MAX_BACKENDS, assign_maxconnections, NULL
+               100, 1, MAX_BACKENDS,
+               check_maxconnections, assign_maxconnections, NULL
        },
 
        {
@@ -1485,7 +1591,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &ReservedBackends,
-               3, 0, MAX_BACKENDS, NULL, NULL
+               3, 0, MAX_BACKENDS,
+               NULL, NULL, NULL
        },
 
        /*
@@ -1499,7 +1606,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_BLOCKS
                },
                &NBuffers,
-               1024, 16, INT_MAX / 2, NULL, NULL
+               1024, 16, INT_MAX / 2,
+               NULL, NULL, NULL
        },
 
        {
@@ -1509,7 +1617,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_BLOCKS
                },
                &num_temp_buffers,
-               1024, 100, INT_MAX / 2, NULL, show_num_temp_buffers
+               1024, 100, INT_MAX / 2,
+               check_temp_buffers, NULL, NULL
        },
 
        {
@@ -1518,7 +1627,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &PostPortNumber,
-               DEF_PGPORT, 1, 65535, NULL, NULL
+               DEF_PGPORT, 1, 65535,
+               NULL, NULL, NULL
        },
 
        {
@@ -1532,7 +1642,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "start with a 0 (zero).)")
                },
                &Unix_socket_permissions,
-               0777, 0000, 0777, NULL, show_unix_socket_permissions
+               0777, 0000, 0777,
+               NULL, NULL, show_unix_socket_permissions
        },
 
        {
@@ -1545,7 +1656,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "start with a 0 (zero).)")
                },
                &Log_file_mode,
-               0600, 0000, 0777, NULL, show_log_file_mode
+               0600, 0000, 0777,
+               NULL, NULL, show_log_file_mode
        },
 
        {
@@ -1557,7 +1669,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB
                },
                &work_mem,
-               1024, 64, MAX_KILOBYTES, NULL, NULL
+               1024, 64, MAX_KILOBYTES,
+               NULL, NULL, NULL
        },
 
        {
@@ -1567,7 +1680,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB
                },
                &maintenance_work_mem,
-               16384, 1024, MAX_KILOBYTES, NULL, NULL
+               16384, 1024, MAX_KILOBYTES,
+               NULL, NULL, NULL
        },
 
        /*
@@ -1582,7 +1696,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB
                },
                &max_stack_depth,
-               100, 100, MAX_KILOBYTES, assign_max_stack_depth, NULL
+               100, 100, MAX_KILOBYTES,
+               check_max_stack_depth, assign_max_stack_depth, NULL
        },
 
        {
@@ -1591,7 +1706,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &VacuumCostPageHit,
-               1, 0, 10000, NULL, NULL
+               1, 0, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1600,7 +1716,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &VacuumCostPageMiss,
-               10, 0, 10000, NULL, NULL
+               10, 0, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1609,7 +1726,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &VacuumCostPageDirty,
-               20, 0, 10000, NULL, NULL
+               20, 0, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1618,7 +1736,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &VacuumCostLimit,
-               200, 1, 10000, NULL, NULL
+               200, 1, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1628,7 +1747,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &VacuumCostDelay,
-               0, 0, 100, NULL, NULL
+               0, 0, 100,
+               NULL, NULL, NULL
        },
 
        {
@@ -1638,7 +1758,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &autovacuum_vac_cost_delay,
-               20, -1, 100, NULL, NULL
+               20, -1, 100,
+               NULL, NULL, NULL
        },
 
        {
@@ -1647,7 +1768,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_vac_cost_limit,
-               -1, -1, 10000, NULL, NULL
+               -1, -1, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1656,7 +1778,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &max_files_per_process,
-               1000, 25, INT_MAX, NULL, NULL
+               1000, 25, INT_MAX,
+               NULL, NULL, NULL
        },
 
        /*
@@ -1668,7 +1791,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &max_prepared_xacts,
-               0, 0, MAX_BACKENDS, NULL, NULL
+               0, 0, MAX_BACKENDS,
+               NULL, NULL, NULL
        },
 
 #ifdef LOCK_DEBUG
@@ -1679,7 +1803,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_lock_oidmin,
-               FirstNormalObjectId, 0, INT_MAX, NULL, NULL
+               FirstNormalObjectId, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"trace_lock_table", PGC_SUSET, DEVELOPER_OPTIONS,
@@ -1688,7 +1813,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE
                },
                &Trace_lock_table,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 #endif
 
@@ -1699,7 +1825,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &StatementTimeout,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1708,7 +1835,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &vacuum_freeze_min_age,
-               50000000, 0, 1000000000, NULL, NULL
+               50000000, 0, 1000000000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1717,7 +1845,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &vacuum_freeze_table_age,
-               150000000, 0, 2000000000, NULL, NULL
+               150000000, 0, 2000000000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1726,7 +1855,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &vacuum_defer_cleanup_age,
-               0, 0, 1000000, NULL, NULL
+               0, 0, 1000000,
+               NULL, NULL, NULL
        },
 
        /*
@@ -1740,7 +1870,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "objects will need to be locked at any one time.")
                },
                &max_locks_per_xact,
-               64, 10, INT_MAX, NULL, NULL
+               64, 10, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1751,7 +1882,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "objects will need to be locked at any one time.")
                },
                &max_predicate_locks_per_xact,
-               64, 10, INT_MAX, NULL, NULL
+               64, 10, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1761,7 +1893,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &AuthenticationTimeout,
-               60, 1, 600, NULL, NULL
+               60, 1, 600,
+               NULL, NULL, NULL
        },
 
        {
@@ -1772,7 +1905,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_UNIT_S
                },
                &PreAuthDelay,
-               0, 0, 60, NULL, NULL
+               0, 0, 60,
+               NULL, NULL, NULL
        },
 
        {
@@ -1781,7 +1915,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &wal_keep_segments,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1790,7 +1925,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &CheckPointSegments,
-               3, 1, INT_MAX, NULL, NULL
+               3, 1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1800,7 +1936,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &CheckPointTimeout,
-               300, 30, 3600, NULL, NULL
+               300, 30, 3600,
+               NULL, NULL, NULL
        },
 
        {
@@ -1813,7 +1950,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &CheckPointWarning,
-               30, 0, INT_MAX, NULL, NULL
+               30, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1823,7 +1961,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_XBLOCKS
                },
                &XLOGbuffers,
-               -1, -1, INT_MAX, NULL, NULL
+               -1, -1, INT_MAX,
+               check_wal_buffers, NULL, NULL
        },
 
        {
@@ -1833,7 +1972,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &WalWriterDelay,
-               200, 1, 10000, NULL, NULL
+               200, 1, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1843,7 +1983,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &max_wal_senders,
-               0, 0, MAX_BACKENDS, NULL, NULL
+               0, 0, MAX_BACKENDS,
+               NULL, NULL, NULL
        },
 
        {
@@ -1853,7 +1994,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &WalSndDelay,
-               1000, 1, 10000, NULL, NULL
+               1000, 1, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1863,7 +2005,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &replication_timeout,
-               60 * 1000, 0, INT_MAX, NULL, NULL
+               60 * 1000, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1873,7 +2016,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &CommitDelay,
-               0, 0, 100000, NULL, NULL
+               0, 0, 100000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1883,7 +2027,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &CommitSiblings,
-               5, 0, 1000, NULL, NULL
+               5, 0, 1000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1894,7 +2039,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "(FLT_DIG or DBL_DIG as appropriate).")
                },
                &extra_float_digits,
-               0, -15, 3, NULL, NULL
+               0, -15, 3,
+               NULL, NULL, NULL
        },
 
        {
@@ -1905,7 +2051,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &log_min_duration_statement,
-               -1, -1, INT_MAX, NULL, NULL
+               -1, -1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1916,7 +2063,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &Log_autovacuum_min_duration,
-               -1, -1, INT_MAX, NULL, NULL
+               -1, -1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -1926,7 +2074,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MS
                },
                &BgWriterDelay,
-               200, 10, 10000, NULL, NULL
+               200, 10, 10000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1935,7 +2084,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &bgwriter_lru_maxpages,
-               100, 0, 1000, NULL, NULL
+               100, 0, 1000,
+               NULL, NULL, NULL
        },
 
        {
@@ -1955,7 +2105,7 @@ static struct config_int ConfigureNamesInt[] =
 #else
                0, 0, 0,
 #endif
-               assign_effective_io_concurrency, NULL
+               check_effective_io_concurrency, assign_effective_io_concurrency, NULL
        },
 
        {
@@ -1965,7 +2115,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_MIN
                },
                &Log_RotationAge,
-               HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / MINS_PER_HOUR, NULL, NULL
+               HOURS_PER_DAY * MINS_PER_HOUR, 0, INT_MAX / MINS_PER_HOUR,
+               NULL, NULL, NULL
        },
 
        {
@@ -1975,7 +2126,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB
                },
                &Log_RotationSize,
-               10 * 1024, 0, INT_MAX / 1024, NULL, NULL
+               10 * 1024, 0, INT_MAX / 1024,
+               NULL, NULL, NULL
        },
 
        {
@@ -1985,7 +2137,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &max_function_args,
-               FUNC_MAX_ARGS, FUNC_MAX_ARGS, FUNC_MAX_ARGS, NULL, NULL
+               FUNC_MAX_ARGS, FUNC_MAX_ARGS, FUNC_MAX_ARGS,
+               NULL, NULL, NULL
        },
 
        {
@@ -1995,7 +2148,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &max_index_keys,
-               INDEX_MAX_KEYS, INDEX_MAX_KEYS, INDEX_MAX_KEYS, NULL, NULL
+               INDEX_MAX_KEYS, INDEX_MAX_KEYS, INDEX_MAX_KEYS,
+               NULL, NULL, NULL
        },
 
        {
@@ -2005,7 +2159,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &max_identifier_length,
-               NAMEDATALEN - 1, NAMEDATALEN - 1, NAMEDATALEN - 1, NULL, NULL
+               NAMEDATALEN - 1, NAMEDATALEN - 1, NAMEDATALEN - 1,
+               NULL, NULL, NULL
        },
 
        {
@@ -2015,7 +2170,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &block_size,
-               BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
+               BLCKSZ, BLCKSZ, BLCKSZ,
+               NULL, NULL, NULL
        },
 
        {
@@ -2025,7 +2181,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_BLOCKS | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &segment_size,
-               RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE, NULL, NULL
+               RELSEG_SIZE, RELSEG_SIZE, RELSEG_SIZE,
+               NULL, NULL, NULL
        },
 
        {
@@ -2035,7 +2192,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &wal_block_size,
-               XLOG_BLCKSZ, XLOG_BLCKSZ, XLOG_BLCKSZ, NULL, NULL
+               XLOG_BLCKSZ, XLOG_BLCKSZ, XLOG_BLCKSZ,
+               NULL, NULL, NULL
        },
 
        {
@@ -2048,7 +2206,7 @@ static struct config_int ConfigureNamesInt[] =
                (XLOG_SEG_SIZE / XLOG_BLCKSZ),
                (XLOG_SEG_SIZE / XLOG_BLCKSZ),
                (XLOG_SEG_SIZE / XLOG_BLCKSZ),
-               NULL, NULL
+               NULL, NULL, NULL
        },
 
        {
@@ -2058,7 +2216,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &autovacuum_naptime,
-               60, 1, INT_MAX / 1000, NULL, NULL
+               60, 1, INT_MAX / 1000,
+               NULL, NULL, NULL
        },
        {
                {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM,
@@ -2066,7 +2225,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_vac_thresh,
-               50, 0, INT_MAX, NULL, NULL
+               50, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                {"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM,
@@ -2074,7 +2234,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_anl_thresh,
-               50, 0, INT_MAX, NULL, NULL
+               50, 0, INT_MAX,
+               NULL, NULL, NULL
        },
        {
                /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
@@ -2084,7 +2245,8 @@ static struct config_int ConfigureNamesInt[] =
                },
                &autovacuum_freeze_max_age,
                /* see pg_resetxlog if you change the upper-limit value */
-               200000000, 100000000, 2000000000, NULL, NULL
+               200000000, 100000000, 2000000000,
+               NULL, NULL, NULL
        },
        {
                /* see max_connections */
@@ -2093,7 +2255,8 @@ static struct config_int ConfigureNamesInt[] =
                        NULL
                },
                &autovacuum_max_workers,
-               3, 1, MAX_BACKENDS, assign_autovacuum_max_workers, NULL
+               3, 1, MAX_BACKENDS,
+               check_autovacuum_max_workers, assign_autovacuum_max_workers, NULL
        },
 
        {
@@ -2103,7 +2266,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &tcp_keepalives_idle,
-               0, 0, INT_MAX, assign_tcp_keepalives_idle, show_tcp_keepalives_idle
+               0, 0, INT_MAX,
+               NULL, assign_tcp_keepalives_idle, show_tcp_keepalives_idle
        },
 
        {
@@ -2113,7 +2277,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_S
                },
                &tcp_keepalives_interval,
-               0, 0, INT_MAX, assign_tcp_keepalives_interval, show_tcp_keepalives_interval
+               0, 0, INT_MAX,
+               NULL, assign_tcp_keepalives_interval, show_tcp_keepalives_interval
        },
 
        {
@@ -2123,7 +2288,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB,
                },
                &ssl_renegotiation_limit,
-               512 * 1024, 0, MAX_KILOBYTES, NULL, NULL
+               512 * 1024, 0, MAX_KILOBYTES,
+               NULL, NULL, NULL
        },
 
        {
@@ -2134,7 +2300,8 @@ static struct config_int ConfigureNamesInt[] =
                                                 "system default."),
                },
                &tcp_keepalives_count,
-               0, 0, INT_MAX, assign_tcp_keepalives_count, show_tcp_keepalives_count
+               0, 0, INT_MAX,
+               NULL, assign_tcp_keepalives_count, show_tcp_keepalives_count
        },
 
        {
@@ -2144,7 +2311,8 @@ static struct config_int ConfigureNamesInt[] =
                        0
                },
                &GinFuzzySearchLimit,
-               0, 0, INT_MAX, NULL, NULL
+               0, 0, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -2156,7 +2324,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_BLOCKS,
                },
                &effective_cache_size,
-               DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX, NULL, NULL
+               DEFAULT_EFFECTIVE_CACHE_SIZE, 1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -2167,7 +2336,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &server_version_num,
-               PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM, NULL, NULL
+               PG_VERSION_NUM, PG_VERSION_NUM, PG_VERSION_NUM,
+               NULL, NULL, NULL
        },
 
        {
@@ -2177,7 +2347,8 @@ static struct config_int ConfigureNamesInt[] =
                        GUC_UNIT_KB
                },
                &log_temp_files,
-               -1, -1, INT_MAX, NULL, NULL
+               -1, -1, INT_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -2186,12 +2357,13 @@ static struct config_int ConfigureNamesInt[] =
                        NULL,
                },
                &pgstat_track_activity_query_size,
-               1024, 100, 102400, NULL, NULL
+               1024, 100, 102400,
+               NULL, NULL, NULL
        },
 
        /* End-of-list marker */
        {
-               {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
+               {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
        }
 };
 
@@ -2205,7 +2377,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &seq_page_cost,
-               DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX, NULL, NULL
+               DEFAULT_SEQ_PAGE_COST, 0, DBL_MAX,
+               NULL, NULL, NULL
        },
        {
                {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
@@ -2214,7 +2387,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &random_page_cost,
-               DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX, NULL, NULL
+               DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX,
+               NULL, NULL, NULL
        },
        {
                {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
@@ -2223,7 +2397,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &cpu_tuple_cost,
-               DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX, NULL, NULL
+               DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX,
+               NULL, NULL, NULL
        },
        {
                {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
@@ -2232,7 +2407,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &cpu_index_tuple_cost,
-               DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX, NULL, NULL
+               DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX,
+               NULL, NULL, NULL
        },
        {
                {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
@@ -2241,7 +2417,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &cpu_operator_cost,
-               DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, NULL, NULL
+               DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX,
+               NULL, NULL, NULL
        },
 
        {
@@ -2251,7 +2428,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &cursor_tuple_fraction,
-               DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0, NULL, NULL
+               DEFAULT_CURSOR_TUPLE_FRACTION, 0.0, 1.0,
+               NULL, NULL, NULL
        },
 
        {
@@ -2260,8 +2438,9 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &Geqo_selection_bias,
-               DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS,
-               MAX_GEQO_SELECTION_BIAS, NULL, NULL
+               DEFAULT_GEQO_SELECTION_BIAS,
+               MIN_GEQO_SELECTION_BIAS, MAX_GEQO_SELECTION_BIAS,
+               NULL, NULL, NULL
        },
        {
                {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
@@ -2269,7 +2448,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &Geqo_seed,
-               0.0, 0.0, 1.0, NULL, NULL
+               0.0, 0.0, 1.0,
+               NULL, NULL, NULL
        },
 
        {
@@ -2278,7 +2458,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &bgwriter_lru_multiplier,
-               2.0, 0.0, 10.0, NULL, NULL
+               2.0, 0.0, 10.0,
+               NULL, NULL, NULL
        },
 
        {
@@ -2288,7 +2469,8 @@ 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.0, -1.0, 1.0, assign_random_seed, show_random_seed
+               0.0, -1.0, 1.0,
+               check_random_seed, assign_random_seed, show_random_seed
        },
 
        {
@@ -2297,7 +2479,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &autovacuum_vac_scale,
-               0.2, 0.0, 100.0, NULL, NULL
+               0.2, 0.0, 100.0,
+               NULL, NULL, NULL
        },
        {
                {"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM,
@@ -2305,7 +2488,8 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &autovacuum_anl_scale,
-               0.1, 0.0, 100.0, NULL, NULL
+               0.1, 0.0, 100.0,
+               NULL, NULL, NULL
        },
 
        {
@@ -2314,12 +2498,13 @@ static struct config_real ConfigureNamesReal[] =
                        NULL
                },
                &CheckPointCompletionTarget,
-               0.5, 0.0, 1.0, NULL, NULL
+               0.5, 0.0, 1.0,
+               NULL, NULL, NULL
        },
 
        /* End-of-list marker */
        {
-               {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL
+               {NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL
        }
 };
 
@@ -2332,7 +2517,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &XLogArchiveCommand,
-               "", NULL, show_archive_command
+               "",
+               NULL, NULL, show_archive_command
        },
 
        {
@@ -2342,7 +2528,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME | GUC_REPORT
                },
                &client_encoding_string,
-               "SQL_ASCII", assign_client_encoding, NULL
+               "SQL_ASCII",
+               check_client_encoding, assign_client_encoding, NULL
        },
 
        {
@@ -2351,7 +2538,8 @@ static struct config_string ConfigureNamesString[] =
                        gettext_noop("If blank, no prefix is used.")
                },
                &Log_line_prefix,
-               "", NULL, NULL
+               "",
+               NULL, NULL, NULL
        },
 
        {
@@ -2360,7 +2548,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &log_timezone_string,
-               "UNKNOWN", assign_log_timezone, show_log_timezone
+               NULL,
+               check_log_timezone, assign_log_timezone, show_log_timezone
        },
 
        {
@@ -2371,7 +2560,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_REPORT
                },
                &datestyle_string,
-               "ISO, MDY", assign_datestyle, NULL
+               "ISO, MDY",
+               check_datestyle, assign_datestyle, NULL
        },
 
        {
@@ -2381,7 +2571,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME
                },
                &default_tablespace,
-               "", assign_default_tablespace, NULL
+               "",
+               check_default_tablespace, NULL, NULL
        },
 
        {
@@ -2391,7 +2582,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_LIST_QUOTE
                },
                &temp_tablespaces,
-               "", assign_temp_tablespaces, NULL
+               "",
+               check_temp_tablespaces, assign_temp_tablespaces, NULL
        },
 
        {
@@ -2404,7 +2596,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &Dynamic_library_path,
-               "$libdir", NULL, NULL
+               "$libdir",
+               NULL, NULL, NULL
        },
 
        {
@@ -2414,7 +2607,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &pg_krb_server_keyfile,
-               PG_KRB_SRVTAB, NULL, NULL
+               PG_KRB_SRVTAB,
+               NULL, NULL, NULL
        },
 
        {
@@ -2423,7 +2617,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &pg_krb_srvnam,
-               PG_KRB_SRVNAM, NULL, NULL
+               PG_KRB_SRVNAM,
+               NULL, NULL, NULL
        },
 
        {
@@ -2432,7 +2627,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &bonjour_name,
-               "", NULL, NULL
+               "",
+               NULL, NULL, NULL
        },
 
        /* See main.c about why defaults for LC_foo are not all alike */
@@ -2444,7 +2640,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &locale_collate,
-               "C", NULL, NULL
+               "C",
+               NULL, NULL, NULL
        },
 
        {
@@ -2454,7 +2651,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &locale_ctype,
-               "C", NULL, NULL
+               "C",
+               NULL, NULL, NULL
        },
 
        {
@@ -2463,7 +2661,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &locale_messages,
-               "", locale_messages_assign, NULL
+               "",
+               check_locale_messages, assign_locale_messages, NULL
        },
 
        {
@@ -2472,7 +2671,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &locale_monetary,
-               "C", locale_monetary_assign, NULL
+               "C",
+               check_locale_monetary, assign_locale_monetary, NULL
        },
 
        {
@@ -2481,7 +2681,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &locale_numeric,
-               "C", locale_numeric_assign, NULL
+               "C",
+               check_locale_numeric, assign_locale_numeric, NULL
        },
 
        {
@@ -2490,7 +2691,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &locale_time,
-               "C", locale_time_assign, NULL
+               "C",
+               check_locale_time, assign_locale_time, NULL
        },
 
        {
@@ -2500,7 +2702,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY
                },
                &shared_preload_libraries_string,
-               "", NULL, NULL
+               "",
+               NULL, NULL, NULL
        },
 
        {
@@ -2510,7 +2713,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_LIST_QUOTE
                },
                &local_preload_libraries_string,
-               "", NULL, NULL
+               "",
+               NULL, NULL, NULL
        },
 
        {
@@ -2520,7 +2724,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_LIST_QUOTE
                },
                &namespace_search_path,
-               "\"$user\",public", assign_search_path, NULL
+               "\"$user\",public",
+               check_search_path, assign_search_path, NULL
        },
 
        {
@@ -2531,7 +2736,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &server_encoding_string,
-               "SQL_ASCII", NULL, NULL
+               "SQL_ASCII",
+               NULL, NULL, NULL
        },
 
        {
@@ -2542,7 +2748,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &server_version_string,
-               PG_VERSION, NULL, NULL
+               PG_VERSION,
+               NULL, NULL, NULL
        },
 
        {
@@ -2553,7 +2760,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
                },
                &role_string,
-               "none", assign_role, show_role
+               "none",
+               check_role, assign_role, show_role
        },
 
        {
@@ -2564,7 +2772,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME | GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
                },
                &session_authorization_string,
-               NULL, assign_session_authorization, show_session_authorization
+               NULL,
+               check_session_authorization, assign_session_authorization, NULL
        },
 
        {
@@ -2576,7 +2785,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT
                },
                &log_destination_string,
-               "stderr", assign_log_destination, NULL
+               "stderr",
+               check_log_destination, assign_log_destination, NULL
        },
        {
                {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
@@ -2586,7 +2796,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &Log_directory,
-               "pg_log", assign_canonical_path, NULL
+               "pg_log",
+               check_canonical_path, NULL, NULL
        },
        {
                {"log_filename", PGC_SIGHUP, LOGGING_WHERE,
@@ -2595,7 +2806,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &Log_filename,
-               "postgresql-%Y-%m-%d_%H%M%S.log", NULL, NULL
+               "postgresql-%Y-%m-%d_%H%M%S.log",
+               NULL, NULL, NULL
        },
 
        {
@@ -2605,7 +2817,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &syslog_ident_str,
-               "postgres", assign_syslog_ident, NULL
+               "postgres",
+               NULL, assign_syslog_ident, NULL
        },
 
        {
@@ -2615,7 +2828,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_REPORT
                },
                &timezone_string,
-               "UNKNOWN", assign_timezone, show_timezone
+               NULL,
+               check_timezone, assign_timezone, show_timezone
        },
        {
                {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
@@ -2623,7 +2837,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &timezone_abbreviations_string,
-               "UNKNOWN", assign_timezone_abbreviations, NULL
+               NULL,
+               check_timezone_abbreviations, assign_timezone_abbreviations, NULL
        },
 
        {
@@ -2633,7 +2848,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
                },
                &XactIsoLevel_string,
-               NULL, assign_XactIsoLevel, show_XactIsoLevel
+               "default",
+               check_XactIsoLevel, assign_XactIsoLevel, show_XactIsoLevel
        },
 
        {
@@ -2643,7 +2859,8 @@ static struct config_string ConfigureNamesString[] =
                                                 "that starts the server.")
                },
                &Unix_socket_group,
-               "", NULL, NULL
+               "",
+               NULL, NULL, NULL
        },
 
        {
@@ -2653,7 +2870,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &UnixSocketDir,
-               "", assign_canonical_path, NULL
+               "",
+               check_canonical_path, NULL, NULL
        },
 
        {
@@ -2663,7 +2881,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT
                },
                &ListenAddresses,
-               "localhost", NULL, NULL
+               "localhost",
+               NULL, NULL, NULL
        },
 
        {
@@ -2673,7 +2892,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT | GUC_LIST_QUOTE
                },
                &custom_variable_classes,
-               NULL, assign_custom_variable_classes, NULL
+               NULL,
+               check_custom_variable_classes, NULL, NULL
        },
 
        {
@@ -2683,6 +2903,7 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &data_directory,
+               NULL,
                NULL, NULL, NULL
        },
 
@@ -2693,6 +2914,7 @@ static struct config_string ConfigureNamesString[] =
                        GUC_DISALLOW_IN_FILE | GUC_SUPERUSER_ONLY
                },
                &ConfigFileName,
+               NULL,
                NULL, NULL, NULL
        },
 
@@ -2703,6 +2925,7 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &HbaFileName,
+               NULL,
                NULL, NULL, NULL
        },
 
@@ -2713,6 +2936,7 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &IdentFileName,
+               NULL,
                NULL, NULL, NULL
        },
 
@@ -2723,7 +2947,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &external_pid_file,
-               NULL, assign_canonical_path, NULL
+               NULL,
+               check_canonical_path, NULL, NULL
        },
 
        {
@@ -2733,7 +2958,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_SUPERUSER_ONLY
                },
                &pgstat_temp_directory,
-               "pg_stat_tmp", assign_pgstat_temp_directory, NULL
+               "pg_stat_tmp",
+               check_canonical_path, assign_pgstat_temp_directory, NULL
        },
 
        {
@@ -2743,7 +2969,8 @@ static struct config_string ConfigureNamesString[] =
                        GUC_LIST_INPUT
                },
                &SyncRepStandbyNames,
-               "", assign_synchronous_standby_names, NULL
+               "",
+               check_synchronous_standby_names, NULL, NULL
        },
 
        {
@@ -2752,7 +2979,8 @@ static struct config_string ConfigureNamesString[] =
                        NULL
                },
                &TSCurrentConfig,
-               "pg_catalog.simple", assignTSCurrentConfig, NULL
+               "pg_catalog.simple",
+               check_TSCurrentConfig, assign_TSCurrentConfig, NULL
        },
 
        {
@@ -2767,7 +2995,7 @@ static struct config_string ConfigureNamesString[] =
 #else
                "none",
 #endif
-               NULL, NULL
+               NULL, NULL, NULL
        },
 
        {
@@ -2777,12 +3005,13 @@ static struct config_string ConfigureNamesString[] =
                        GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
                },
                &application_name,
-               "", assign_application_name, NULL
+               "",
+               check_application_name, assign_application_name, NULL
        },
 
        /* End-of-list marker */
        {
-               {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
+               {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL, NULL
        }
 };
 
@@ -2795,7 +3024,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &backslash_quote,
-               BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options, NULL, NULL
+               BACKSLASH_QUOTE_SAFE_ENCODING, backslash_quote_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2804,7 +3034,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &bytea_output,
-               BYTEA_OUTPUT_HEX, bytea_output_options, NULL, NULL
+               BYTEA_OUTPUT_HEX, bytea_output_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2814,7 +3045,8 @@ static struct config_enum ConfigureNamesEnum[] =
                                                 " the level, the fewer messages are sent.")
                },
                &client_min_messages,
-               NOTICE, client_message_level_options, NULL, NULL
+               NOTICE, client_message_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2825,7 +3057,7 @@ static struct config_enum ConfigureNamesEnum[] =
                },
                &constraint_exclusion,
                CONSTRAINT_EXCLUSION_PARTITION, constraint_exclusion_options,
-               NULL, NULL
+               NULL, NULL, NULL
        },
 
        {
@@ -2834,7 +3066,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &DefaultXactIsoLevel,
-               XACT_READ_COMMITTED, isolation_level_options, NULL, NULL
+               XACT_READ_COMMITTED, isolation_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2844,7 +3077,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        GUC_REPORT
                },
                &IntervalStyle,
-               INTSTYLE_POSTGRES, intervalstyle_options, NULL, NULL
+               INTSTYLE_POSTGRES, intervalstyle_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2853,7 +3087,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &Log_error_verbosity,
-               PGERROR_DEFAULT, log_error_verbosity_options, NULL, NULL
+               PGERROR_DEFAULT, log_error_verbosity_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2863,7 +3098,8 @@ static struct config_enum ConfigureNamesEnum[] =
                                                 " the level, the fewer messages are sent.")
                },
                &log_min_messages,
-               WARNING, server_message_level_options, NULL, NULL
+               WARNING, server_message_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2873,7 +3109,8 @@ static struct config_enum ConfigureNamesEnum[] =
                                                 " the level, the fewer messages are sent.")
                },
                &log_min_error_statement,
-               ERROR, server_message_level_options, NULL, NULL
+               ERROR, server_message_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2882,7 +3119,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &log_statement,
-               LOGSTMT_NONE, log_statement_options, NULL, NULL
+               LOGSTMT_NONE, log_statement_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2896,7 +3134,8 @@ static struct config_enum ConfigureNamesEnum[] =
 #else
                0,
 #endif
-               syslog_facility_options, assign_syslog_facility, NULL
+               syslog_facility_options,
+               NULL, assign_syslog_facility, NULL
        },
 
        {
@@ -2906,7 +3145,7 @@ static struct config_enum ConfigureNamesEnum[] =
                },
                &SessionReplicationRole,
                SESSION_REPLICATION_ROLE_ORIGIN, session_replication_role_options,
-               assign_session_replication_role, NULL
+               NULL, assign_session_replication_role, NULL
        },
 
        {
@@ -2916,7 +3155,7 @@ static struct config_enum ConfigureNamesEnum[] =
                },
                &synchronous_commit,
                SYNCHRONOUS_COMMIT_ON, synchronous_commit_options,
-               NULL, NULL
+               NULL, NULL, NULL
        },
 
        {
@@ -2930,7 +3169,8 @@ static struct config_enum ConfigureNamesEnum[] =
                 * client_message_level_options allows too many values, really,
                 * but it's not worth having a separate options array for this.
                 */
-               LOG, client_message_level_options, NULL, NULL
+               LOG, client_message_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2939,7 +3179,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &pgstat_track_functions,
-               TRACK_FUNC_OFF, track_function_options, NULL, NULL
+               TRACK_FUNC_OFF, track_function_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2948,7 +3189,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &wal_level,
-               WAL_LEVEL_MINIMAL, wal_level_options, NULL
+               WAL_LEVEL_MINIMAL, wal_level_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2958,7 +3200,7 @@ static struct config_enum ConfigureNamesEnum[] =
                },
                &sync_method,
                DEFAULT_SYNC_METHOD, sync_method_options,
-               assign_xlog_sync_method, NULL
+               NULL, assign_xlog_sync_method, NULL
        },
 
        {
@@ -2967,7 +3209,8 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &xmlbinary,
-               XMLBINARY_BASE64, xmlbinary_options, NULL, NULL
+               XMLBINARY_BASE64, xmlbinary_options,
+               NULL, NULL, NULL
        },
 
        {
@@ -2977,13 +3220,14 @@ static struct config_enum ConfigureNamesEnum[] =
                        NULL
                },
                &xmloption,
-               XMLOPTION_CONTENT, xmloption_options, NULL, NULL
+               XMLOPTION_CONTENT, xmloption_options,
+               NULL, NULL, NULL
        },
 
 
        /* End-of-list marker */
        {
-               {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL
+               {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
        }
 };
 
@@ -3030,7 +3274,6 @@ 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 validate_option_array_item(const char *name, const char *value,
                                                   bool skipIfNoPermissions);
 
@@ -3078,6 +3321,27 @@ guc_strdup(int elevel, const char *src)
 }
 
 
+/*
+ * Detect whether strval is referenced anywhere in a GUC string item
+ */
+static bool
+string_field_used(struct config_string * conf, char *strval)
+{
+       GucStack   *stack;
+
+       if (strval == *(conf->variable) ||
+               strval == conf->reset_val ||
+               strval == conf->boot_val)
+               return true;
+       for (stack = conf->gen.stack; stack; stack = stack->prev)
+       {
+               if (strval == stack->prior.val.stringval ||
+                       strval == stack->masked.val.stringval)
+                       return true;
+       }
+       return false;
+}
+
 /*
  * Support for assigning to a field of a string GUC item.  Free the prior
  * value if it's not referenced anywhere else in the item (including stacked
@@ -3087,87 +3351,119 @@ static void
 set_string_field(struct config_string * conf, char **field, char *newval)
 {
        char       *oldval = *field;
-       GucStack   *stack;
 
        /* Do the assignment */
        *field = newval;
 
-       /* Exit if any duplicate references, or if old value was NULL anyway */
-       if (oldval == NULL ||
-               oldval == *(conf->variable) ||
-               oldval == conf->reset_val ||
-               oldval == conf->boot_val)
-               return;
-       for (stack = conf->gen.stack; stack; stack = stack->prev)
-       {
-               if (oldval == stack->prior.stringval ||
-                       oldval == stack->masked.stringval)
-                       return;
-       }
-
-       /* Not used anymore, so free it */
-       free(oldval);
+       /* Free old value if it's not NULL and isn't referenced anymore */
+       if (oldval && !string_field_used(conf, oldval))
+               free(oldval);
 }
 
 /*
- * Detect whether strval is referenced anywhere in a GUC string item
+ * Detect whether an "extra" struct is referenced anywhere in a GUC item
  */
 static bool
-string_field_used(struct config_string * conf, char *strval)
+extra_field_used(struct config_generic * gconf, void *extra)
 {
        GucStack   *stack;
 
-       if (strval == *(conf->variable) ||
-               strval == conf->reset_val ||
-               strval == conf->boot_val)
+       if (extra == gconf->extra)
                return true;
-       for (stack = conf->gen.stack; stack; stack = stack->prev)
+       switch (gconf->vartype)
        {
-               if (strval == stack->prior.stringval ||
-                       strval == stack->masked.stringval)
+               case PGC_BOOL:
+                       if (extra == ((struct config_bool *) gconf)->reset_extra)
+                               return true;
+                       break;
+               case PGC_INT:
+                       if (extra == ((struct config_int *) gconf)->reset_extra)
+                               return true;
+                       break;
+               case PGC_REAL:
+                       if (extra == ((struct config_real *) gconf)->reset_extra)
+                               return true;
+                       break;
+               case PGC_STRING:
+                       if (extra == ((struct config_string *) gconf)->reset_extra)
+                               return true;
+                       break;
+               case PGC_ENUM:
+                       if (extra == ((struct config_enum *) gconf)->reset_extra)
+                               return true;
+                       break;
+       }
+       for (stack = gconf->stack; stack; stack = stack->prev)
+       {
+               if (extra == stack->prior.extra ||
+                       extra == stack->masked.extra)
                        return true;
        }
+
        return false;
 }
 
 /*
- * Support for copying a variable's active value into a stack entry
+ * Support for assigning to an "extra" field of a GUC item.  Free the prior
+ * value if it's not referenced anywhere else in the item (including stacked
+ * states).
+ */
+static void
+set_extra_field(struct config_generic * gconf, void **field, void *newval)
+{
+       void       *oldval = *field;
+
+       /* Do the assignment */
+       *field = newval;
+
+       /* Free old value if it's not NULL and isn't referenced anymore */
+       if (oldval && !extra_field_used(gconf, oldval))
+               free(oldval);
+}
+
+/*
+ * Support for copying a variable's active value into a stack entry.
+ * The "extra" field associated with the active value is copied, too.
+ *
+ * NB: be sure stringval and extra fields of a new stack entry are
+ * initialized to NULL before this is used, else we'll try to free() them.
  */
 static void
-set_stack_value(struct config_generic * gconf, union config_var_value * val)
+set_stack_value(struct config_generic * gconf, config_var_value *val)
 {
        switch (gconf->vartype)
        {
                case PGC_BOOL:
-                       val->boolval =
+                       val->val.boolval =
                                *((struct config_bool *) gconf)->variable;
                        break;
                case PGC_INT:
-                       val->intval =
+                       val->val.intval =
                                *((struct config_int *) gconf)->variable;
                        break;
                case PGC_REAL:
-                       val->realval =
+                       val->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),
+                                                        &(val->val.stringval),
                                                         *((struct config_string *) gconf)->variable);
                        break;
                case PGC_ENUM:
-                       val->enumval =
+                       val->val.enumval =
                                *((struct config_enum *) gconf)->variable;
                        break;
        }
+       set_extra_field(gconf, &(val->extra), gconf->extra);
 }
 
 /*
- * Support for discarding a no-longer-needed value in a stack entry
+ * Support for discarding a no-longer-needed value in a stack entry.
+ * The "extra" field associated with the stack entry is cleared, too.
  */
 static void
-discard_stack_value(struct config_generic * gconf, union config_var_value * val)
+discard_stack_value(struct config_generic * gconf, config_var_value *val)
 {
        switch (gconf->vartype)
        {
@@ -3179,10 +3475,11 @@ discard_stack_value(struct config_generic * gconf, union config_var_value * val)
                        break;
                case PGC_STRING:
                        set_string_field((struct config_string *) gconf,
-                                                        &(val->stringval),
+                                                        &(val->val.stringval),
                                                         NULL);
                        break;
        }
+       set_extra_field(gconf, &(val->extra), NULL);
 }
 
 
@@ -3594,6 +3891,9 @@ InitializeGUCOptions(void)
 
 /*
  * Initialize one GUC option variable to its compiled-in default.
+ *
+ * Note: the reason for calling check_hooks is not that we think the boot_val
+ * might fail, but that the hooks might wish to compute an "extra" struct.
  */
 static void
 InitializeOneGUCOption(struct config_generic * gconf)
@@ -3602,6 +3902,7 @@ InitializeOneGUCOption(struct config_generic * gconf)
        gconf->reset_source = PGC_S_DEFAULT;
        gconf->source = PGC_S_DEFAULT;
        gconf->stack = NULL;
+       gconf->extra = NULL;
        gconf->sourcefile = NULL;
        gconf->sourceline = 0;
 
@@ -3610,96 +3911,91 @@ InitializeOneGUCOption(struct config_generic * gconf)
                case PGC_BOOL:
                        {
                                struct config_bool *conf = (struct config_bool *) gconf;
+                               bool            newval = conf->boot_val;
+                               void       *extra = NULL;
 
+                               if (!call_bool_check_hook(conf, &newval, &extra,
+                                                                                 PGC_S_DEFAULT, LOG))
+                                       elog(FATAL, "failed to initialize %s to %d",
+                                                conf->gen.name, (int) newval);
                                if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (conf->boot_val, true,
-                                                                                          PGC_S_DEFAULT))
-                                               elog(FATAL, "failed to initialize %s to %d",
-                                                        conf->gen.name, (int) conf->boot_val);
-                               *conf->variable = conf->reset_val = conf->boot_val;
+                                       (*conf->assign_hook) (newval, extra);
+                               *conf->variable = conf->reset_val = newval;
+                               conf->gen.extra = conf->reset_extra = extra;
                                break;
                        }
                case PGC_INT:
                        {
                                struct config_int *conf = (struct config_int *) gconf;
-
-                               Assert(conf->boot_val >= conf->min);
-                               Assert(conf->boot_val <= conf->max);
+                               int                     newval = conf->boot_val;
+                               void       *extra = NULL;
+
+                               Assert(newval >= conf->min);
+                               Assert(newval <= conf->max);
+                               if (!call_int_check_hook(conf, &newval, &extra,
+                                                                                PGC_S_DEFAULT, LOG))
+                                       elog(FATAL, "failed to initialize %s to %d",
+                                                conf->gen.name, newval);
                                if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (conf->boot_val, true,
-                                                                                          PGC_S_DEFAULT))
-                                               elog(FATAL, "failed to initialize %s to %d",
-                                                        conf->gen.name, conf->boot_val);
-                               *conf->variable = conf->reset_val = conf->boot_val;
+                                       (*conf->assign_hook) (newval, extra);
+                               *conf->variable = conf->reset_val = newval;
+                               conf->gen.extra = conf->reset_extra = extra;
                                break;
                        }
                case PGC_REAL:
                        {
                                struct config_real *conf = (struct config_real *) gconf;
-
-                               Assert(conf->boot_val >= conf->min);
-                               Assert(conf->boot_val <= conf->max);
+                               double          newval = conf->boot_val;
+                               void       *extra = NULL;
+
+                               Assert(newval >= conf->min);
+                               Assert(newval <= conf->max);
+                               if (!call_real_check_hook(conf, &newval, &extra,
+                                                                                 PGC_S_DEFAULT, LOG))
+                                       elog(FATAL, "failed to initialize %s to %g",
+                                                conf->gen.name, newval);
                                if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (conf->boot_val, true,
-                                                                                          PGC_S_DEFAULT))
-                                               elog(FATAL, "failed to initialize %s to %g",
-                                                        conf->gen.name, conf->boot_val);
-                               *conf->variable = conf->reset_val = conf->boot_val;
+                                       (*conf->assign_hook) (newval, extra);
+                               *conf->variable = conf->reset_val = newval;
+                               conf->gen.extra = conf->reset_extra = extra;
                                break;
                        }
                case PGC_STRING:
                        {
                                struct config_string *conf = (struct config_string *) gconf;
-                               char       *str;
-
-                               *conf->variable = NULL;
-                               conf->reset_val = NULL;
-
-                               if (conf->boot_val == NULL)
-                               {
-                                       /* leave the value NULL, do not call assign hook */
-                                       break;
-                               }
+                               char       *newval;
+                               void       *extra = NULL;
 
-                               str = guc_strdup(FATAL, conf->boot_val);
-                               conf->reset_val = str;
+                               /* non-NULL boot_val must always get strdup'd */
+                               if (conf->boot_val != NULL)
+                                       newval = guc_strdup(FATAL, conf->boot_val);
+                               else
+                                       newval = NULL;
 
+                               if (!call_string_check_hook(conf, &newval, &extra,
+                                                                                       PGC_S_DEFAULT, LOG))
+                                       elog(FATAL, "failed to initialize %s to \"%s\"",
+                                                conf->gen.name, newval ? newval : "");
                                if (conf->assign_hook)
-                               {
-                                       const char *newstr;
-
-                                       newstr = (*conf->assign_hook) (str, true,
-                                                                                                  PGC_S_DEFAULT);
-                                       if (newstr == NULL)
-                                       {
-                                               elog(FATAL, "failed to initialize %s to \"%s\"",
-                                                        conf->gen.name, str);
-                                       }
-                                       else if (newstr != str)
-                                       {
-                                               free(str);
-
-                                               /*
-                                                * See notes in set_config_option about casting
-                                                */
-                                               str = (char *) newstr;
-                                               conf->reset_val = str;
-                                       }
-                               }
-                               *conf->variable = str;
+                                       (*conf->assign_hook) (newval, extra);
+                               *conf->variable = conf->reset_val = newval;
+                               conf->gen.extra = conf->reset_extra = extra;
                                break;
                        }
                case PGC_ENUM:
                        {
                                struct config_enum *conf = (struct config_enum *) gconf;
+                               int                     newval = conf->boot_val;
+                               void       *extra = NULL;
 
+                               if (!call_enum_check_hook(conf, &newval, &extra,
+                                                                                 PGC_S_DEFAULT, LOG))
+                                       elog(FATAL, "failed to initialize %s to %d",
+                                                conf->gen.name, newval);
                                if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (conf->boot_val, true,
-                                                                                          PGC_S_DEFAULT))
-                                               elog(FATAL, "failed to initialize %s to %s",
-                                                        conf->gen.name,
-                                                 config_enum_lookup_by_value(conf, conf->boot_val));
-                               *conf->variable = conf->reset_val = conf->boot_val;
+                                       (*conf->assign_hook) (newval, extra);
+                               *conf->variable = conf->reset_val = newval;
+                               conf->gen.extra = conf->reset_extra = extra;
                                break;
                        }
        }
@@ -3888,11 +4184,11 @@ ResetAllOptions(void)
                                        struct config_bool *conf = (struct config_bool *) gconf;
 
                                        if (conf->assign_hook)
-                                               if (!(*conf->assign_hook) (conf->reset_val, true,
-                                                                                                  PGC_S_SESSION))
-                                                       elog(ERROR, "failed to reset %s to %d",
-                                                                conf->gen.name, (int) conf->reset_val);
+                                               (*conf->assign_hook) (conf->reset_val,
+                                                                                         conf->reset_extra);
                                        *conf->variable = conf->reset_val;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       conf->reset_extra);
                                        break;
                                }
                        case PGC_INT:
@@ -3900,11 +4196,11 @@ ResetAllOptions(void)
                                        struct config_int *conf = (struct config_int *) gconf;
 
                                        if (conf->assign_hook)
-                                               if (!(*conf->assign_hook) (conf->reset_val, true,
-                                                                                                  PGC_S_SESSION))
-                                                       elog(ERROR, "failed to reset %s to %d",
-                                                                conf->gen.name, conf->reset_val);
+                                               (*conf->assign_hook) (conf->reset_val,
+                                                                                         conf->reset_extra);
                                        *conf->variable = conf->reset_val;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       conf->reset_extra);
                                        break;
                                }
                        case PGC_REAL:
@@ -3912,40 +4208,23 @@ ResetAllOptions(void)
                                        struct config_real *conf = (struct config_real *) gconf;
 
                                        if (conf->assign_hook)
-                                               if (!(*conf->assign_hook) (conf->reset_val, true,
-                                                                                                  PGC_S_SESSION))
-                                                       elog(ERROR, "failed to reset %s to %g",
-                                                                conf->gen.name, conf->reset_val);
+                                               (*conf->assign_hook) (conf->reset_val,
+                                                                                         conf->reset_extra);
                                        *conf->variable = conf->reset_val;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       conf->reset_extra);
                                        break;
                                }
                        case PGC_STRING:
                                {
                                        struct config_string *conf = (struct config_string *) gconf;
-                                       char       *str;
-
-                                       /* We need not strdup here */
-                                       str = conf->reset_val;
-
-                                       if (conf->assign_hook && str)
-                                       {
-                                               const char *newstr;
-
-                                               newstr = (*conf->assign_hook) (str, true,
-                                                                                                          PGC_S_SESSION);
-                                               if (newstr == NULL)
-                                                       elog(ERROR, "failed to reset %s to \"%s\"",
-                                                                conf->gen.name, str);
-                                               else if (newstr != str)
-                                               {
-                                                       /*
-                                                        * See notes in set_config_option about casting
-                                                        */
-                                                       str = (char *) newstr;
-                                               }
-                                       }
 
-                                       set_string_field(conf, conf->variable, str);
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (conf->reset_val,
+                                                                                         conf->reset_extra);
+                                       set_string_field(conf, conf->variable, conf->reset_val);
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       conf->reset_extra);
                                        break;
                                }
                        case PGC_ENUM:
@@ -3953,12 +4232,11 @@ ResetAllOptions(void)
                                        struct config_enum *conf = (struct config_enum *) gconf;
 
                                        if (conf->assign_hook)
-                                               if (!(*conf->assign_hook) (conf->reset_val, true,
-                                                                                                  PGC_S_SESSION))
-                                                       elog(ERROR, "failed to reset %s to %s",
-                                                                conf->gen.name,
-                                                                config_enum_lookup_by_value(conf, conf->reset_val));
+                                               (*conf->assign_hook) (conf->reset_val,
+                                                                                         conf->reset_extra);
                                        *conf->variable = conf->reset_val;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       conf->reset_extra);
                                        break;
                                }
                }
@@ -4209,7 +4487,7 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                        if (restorePrior || restoreMasked)
                        {
                                /* Perform appropriate restoration of the stacked value */
-                               union config_var_value newvalue;
+                               config_var_value newvalue;
                                GucSource       newsource;
 
                                if (restoreMasked)
@@ -4228,16 +4506,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                                        case PGC_BOOL:
                                                {
                                                        struct config_bool *conf = (struct config_bool *) gconf;
-                                                       bool            newval = newvalue.boolval;
+                                                       bool            newval = newvalue.val.boolval;
+                                                       void       *newextra = newvalue.extra;
 
-                                                       if (*conf->variable != newval)
+                                                       if (*conf->variable != newval ||
+                                                               conf->gen.extra != newextra)
                                                        {
                                                                if (conf->assign_hook)
-                                                                       if (!(*conf->assign_hook) (newval,
-                                                                                                          true, PGC_S_OVERRIDE))
-                                                                               elog(LOG, "failed to commit %s as %d",
-                                                                                        conf->gen.name, (int) newval);
+                                                                       (*conf->assign_hook) (newval, newextra);
                                                                *conf->variable = newval;
+                                                               set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                                               newextra);
                                                                changed = true;
                                                        }
                                                        break;
@@ -4245,16 +4524,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                                        case PGC_INT:
                                                {
                                                        struct config_int *conf = (struct config_int *) gconf;
-                                                       int                     newval = newvalue.intval;
+                                                       int                     newval = newvalue.val.intval;
+                                                       void       *newextra = newvalue.extra;
 
-                                                       if (*conf->variable != newval)
+                                                       if (*conf->variable != newval ||
+                                                               conf->gen.extra != newextra)
                                                        {
                                                                if (conf->assign_hook)
-                                                                       if (!(*conf->assign_hook) (newval,
-                                                                                                          true, PGC_S_OVERRIDE))
-                                                                               elog(LOG, "failed to commit %s as %d",
-                                                                                        conf->gen.name, newval);
+                                                                       (*conf->assign_hook) (newval, newextra);
                                                                *conf->variable = newval;
+                                                               set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                                               newextra);
                                                                changed = true;
                                                        }
                                                        break;
@@ -4262,16 +4542,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                                        case PGC_REAL:
                                                {
                                                        struct config_real *conf = (struct config_real *) gconf;
-                                                       double          newval = newvalue.realval;
+                                                       double          newval = newvalue.val.realval;
+                                                       void       *newextra = newvalue.extra;
 
-                                                       if (*conf->variable != newval)
+                                                       if (*conf->variable != newval ||
+                                                               conf->gen.extra != newextra)
                                                        {
                                                                if (conf->assign_hook)
-                                                                       if (!(*conf->assign_hook) (newval,
-                                                                                                          true, PGC_S_OVERRIDE))
-                                                                               elog(LOG, "failed to commit %s as %g",
-                                                                                        conf->gen.name, newval);
+                                                                       (*conf->assign_hook) (newval, newextra);
                                                                *conf->variable = newval;
+                                                               set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                                               newextra);
                                                                changed = true;
                                                        }
                                                        break;
@@ -4279,33 +4560,17 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                                        case PGC_STRING:
                                                {
                                                        struct config_string *conf = (struct config_string *) gconf;
-                                                       char       *newval = newvalue.stringval;
+                                                       char       *newval = newvalue.val.stringval;
+                                                       void       *newextra = newvalue.extra;
 
-                                                       if (*conf->variable != newval)
+                                                       if (*conf->variable != newval ||
+                                                               conf->gen.extra != newextra)
                                                        {
-                                                               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 as \"%s\"",
-                                                                                        conf->gen.name, newval);
-                                                                       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;
-                                                                       }
-                                                               }
-
+                                                               if (conf->assign_hook)
+                                                                       (*conf->assign_hook) (newval, newextra);
                                                                set_string_field(conf, conf->variable, newval);
+                                                               set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                                               newextra);
                                                                changed = true;
                                                        }
 
@@ -4315,30 +4580,36 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
                                                         * 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);
+                                                       set_string_field(conf, &stack->prior.val.stringval, NULL);
+                                                       set_string_field(conf, &stack->masked.val.stringval, NULL);
                                                        break;
                                                }
                                        case PGC_ENUM:
                                                {
                                                        struct config_enum *conf = (struct config_enum *) gconf;
-                                                       int                     newval = newvalue.enumval;
+                                                       int                     newval = newvalue.val.enumval;
+                                                       void       *newextra = newvalue.extra;
 
-                                                       if (*conf->variable != newval)
+                                                       if (*conf->variable != newval ||
+                                                               conf->gen.extra != newextra)
                                                        {
                                                                if (conf->assign_hook)
-                                                                       if (!(*conf->assign_hook) (newval,
-                                                                                                          true, PGC_S_OVERRIDE))
-                                                                               elog(LOG, "failed to commit %s as %s",
-                                                                                        conf->gen.name,
-                                                                                        config_enum_lookup_by_value(conf, newval));
+                                                                       (*conf->assign_hook) (newval, newextra);
                                                                *conf->variable = newval;
+                                                               set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                                               newextra);
                                                                changed = true;
                                                        }
                                                        break;
                                                }
                                }
 
+                               /*
+                                * Release stacked extra values if not used anymore.
+                                */
+                               set_extra_field(gconf, &(stack->prior.extra), NULL);
+                               set_extra_field(gconf, &(stack->masked.extra), NULL);
+
                                gconf->source = newsource;
                        }
 
@@ -4748,33 +5019,6 @@ config_enum_get_options(struct config_enum * record, const char *prefix,
        return retstr.data;
 }
 
-/*
- * Call a GucStringAssignHook function, being careful to free the
- * "newval" string if the hook ereports.
- *
- * This is split out of set_config_option just to avoid the "volatile"
- * qualifiers that would otherwise have to be plastered all over.
- */
-static const char *
-call_string_assign_hook(GucStringAssignHook assign_hook,
-                                               char *newval, bool doit, GucSource source)
-{
-       const char *result;
-
-       PG_TRY();
-       {
-               result = (*assign_hook) (newval, doit, source);
-       }
-       PG_CATCH();
-       {
-               free(newval);
-               PG_RE_THROW();
-       }
-       PG_END_TRY();
-
-       return result;
-}
-
 
 /*
  * Sets option `name' to given value. The value should be a string
@@ -4810,6 +5054,7 @@ set_config_option(const char *name, const char *value,
 {
        struct config_generic *record;
        int                     elevel;
+       bool            prohibitValueChange = false;
        bool            makeDefault;
 
        if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
@@ -4846,15 +5091,21 @@ set_config_option(const char *name, const char *value,
 
        /*
         * Check if the option can be set at this time. See guc.h for the precise
-        * rules. Note that we don't want to throw errors if we're in the SIGHUP
-        * context. In that case we just ignore the attempt and return true.
+        * rules.
         */
        switch (record->context)
        {
                case PGC_INTERNAL:
                        if (context == PGC_SIGHUP)
+                       {
+                               /*
+                                * Historically we've just silently ignored attempts to set
+                                * PGC_INTERNAL variables from the config file.  Maybe it'd
+                                * be better to use the prohibitValueChange logic for this?
+                                */
                                return true;
-                       if (context != PGC_INTERNAL)
+                       }
+                       else if (context != PGC_INTERNAL)
                        {
                                ereport(elevel,
                                                (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
@@ -4867,19 +5118,23 @@ set_config_option(const char *name, const char *value,
                        if (context == PGC_SIGHUP)
                        {
                                /*
-                                * We are reading a PGC_POSTMASTER var from postgresql.conf.
-                                * We can't change the setting, so give a warning if the DBA
-                                * tries to change it.  (Throwing an error would be more
-                                * consistent, but seems overly rigid.)
+                                * We are re-reading a PGC_POSTMASTER variable from
+                                * postgresql.conf.  We can't change the setting, so we should
+                                * give a warning if the DBA tries to change it.  However,
+                                * because of variant formats, canonicalization by check
+                                * hooks, etc, we can't just compare the given string directly
+                                * to what's stored.  Set a flag to check below after we have
+                                * the final storable value.
+                                *
+                                * During the "checking" pass we just do nothing, to avoid
+                                * printing the warning twice.
                                 */
-                               if (changeVal && !is_newvalue_equal(record, value))
-                                       ereport(elevel,
-                                                       (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
-                                                        errmsg("parameter \"%s\" cannot be changed without restarting the server",
-                                                                       name)));
-                               return true;
+                               if (!changeVal)
+                                       return true;
+
+                               prohibitValueChange = true;
                        }
-                       if (context != PGC_POSTMASTER)
+                       else if (context != PGC_POSTMASTER)
                        {
                                ereport(elevel,
                                                (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
@@ -5022,6 +5277,7 @@ set_config_option(const char *name, const char *value,
                        {
                                struct config_bool *conf = (struct config_bool *) record;
                                bool            newval;
+                               void       *newextra = NULL;
 
                                if (value)
                                {
@@ -5033,32 +5289,45 @@ set_config_option(const char *name, const char *value,
                                                                 name)));
                                                return false;
                                        }
+                                       if (!call_bool_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
                                }
                                else if (source == PGC_S_DEFAULT)
+                               {
                                        newval = conf->boot_val;
+                                       if (!call_bool_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
+                               }
                                else
                                {
                                        newval = conf->reset_val;
+                                       newextra = conf->reset_extra;
                                        source = conf->gen.reset_source;
                                }
 
-                               /* Save old value to support transaction abort */
-                               if (changeVal && !makeDefault)
-                                       push_old_value(&conf->gen, action);
-
-                               if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (newval, changeVal, source))
-                                       {
+                               if (prohibitValueChange)
+                               {
+                                       if (*conf->variable != newval)
                                                ereport(elevel,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                        errmsg("invalid value for parameter \"%s\": %d",
-                                                                       name, (int) newval)));
-                                               return false;
-                                       }
+                                                               (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                                                                errmsg("parameter \"%s\" cannot be changed without restarting the server",
+                                                                               name)));
+                                       return false;
+                               }
 
                                if (changeVal)
                                {
+                                       /* Save old value to support transaction abort */
+                                       if (!makeDefault)
+                                               push_old_value(&conf->gen, action);
+
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (newval, newextra);
                                        *conf->variable = newval;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       newextra);
                                        conf->gen.source = source;
                                }
                                if (makeDefault)
@@ -5068,17 +5337,25 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.reset_source <= source)
                                        {
                                                conf->reset_val = newval;
+                                               set_extra_field(&conf->gen, &conf->reset_extra,
+                                                                               newextra);
                                                conf->gen.reset_source = source;
                                        }
                                        for (stack = conf->gen.stack; stack; stack = stack->prev)
                                        {
                                                if (stack->source <= source)
                                                {
-                                                       stack->prior.boolval = newval;
+                                                       stack->prior.val.boolval = newval;
+                                                       set_extra_field(&conf->gen, &stack->prior.extra,
+                                                                                       newextra);
                                                        stack->source = source;
                                                }
                                        }
                                }
+
+                               /* Perhaps we didn't install newextra anywhere */
+                               if (newextra && !extra_field_used(&conf->gen, newextra))
+                                       free(newextra);
                                break;
                        }
 
@@ -5086,6 +5363,7 @@ set_config_option(const char *name, const char *value,
                        {
                                struct config_int *conf = (struct config_int *) record;
                                int                     newval;
+                               void       *newextra = NULL;
 
                                if (value)
                                {
@@ -5108,32 +5386,45 @@ set_config_option(const char *name, const char *value,
                                                                                newval, name, conf->min, conf->max)));
                                                return false;
                                        }
+                                       if (!call_int_check_hook(conf, &newval, &newextra,
+                                                                                        source, elevel))
+                                               return false;
                                }
                                else if (source == PGC_S_DEFAULT)
+                               {
                                        newval = conf->boot_val;
+                                       if (!call_int_check_hook(conf, &newval, &newextra,
+                                                                                        source, elevel))
+                                               return false;
+                               }
                                else
                                {
                                        newval = conf->reset_val;
+                                       newextra = conf->reset_extra;
                                        source = conf->gen.reset_source;
                                }
 
-                               /* Save old value to support transaction abort */
-                               if (changeVal && !makeDefault)
-                                       push_old_value(&conf->gen, action);
-
-                               if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (newval, changeVal, source))
-                                       {
+                               if (prohibitValueChange)
+                               {
+                                       if (*conf->variable != newval)
                                                ereport(elevel,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                        errmsg("invalid value for parameter \"%s\": %d",
-                                                                       name, newval)));
-                                               return false;
-                                       }
+                                                               (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                                                                errmsg("parameter \"%s\" cannot be changed without restarting the server",
+                                                                               name)));
+                                       return false;
+                               }
 
                                if (changeVal)
                                {
+                                       /* Save old value to support transaction abort */
+                                       if (!makeDefault)
+                                               push_old_value(&conf->gen, action);
+
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (newval, newextra);
                                        *conf->variable = newval;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       newextra);
                                        conf->gen.source = source;
                                }
                                if (makeDefault)
@@ -5143,17 +5434,25 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.reset_source <= source)
                                        {
                                                conf->reset_val = newval;
+                                               set_extra_field(&conf->gen, &conf->reset_extra,
+                                                                               newextra);
                                                conf->gen.reset_source = source;
                                        }
                                        for (stack = conf->gen.stack; stack; stack = stack->prev)
                                        {
                                                if (stack->source <= source)
                                                {
-                                                       stack->prior.intval = newval;
+                                                       stack->prior.val.intval = newval;
+                                                       set_extra_field(&conf->gen, &stack->prior.extra,
+                                                                                       newextra);
                                                        stack->source = source;
                                                }
                                        }
                                }
+
+                               /* Perhaps we didn't install newextra anywhere */
+                               if (newextra && !extra_field_used(&conf->gen, newextra))
+                                       free(newextra);
                                break;
                        }
 
@@ -5161,6 +5460,7 @@ set_config_option(const char *name, const char *value,
                        {
                                struct config_real *conf = (struct config_real *) record;
                                double          newval;
+                               void       *newextra = NULL;
 
                                if (value)
                                {
@@ -5180,32 +5480,45 @@ set_config_option(const char *name, const char *value,
                                                                                newval, name, conf->min, conf->max)));
                                                return false;
                                        }
+                                       if (!call_real_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
                                }
                                else if (source == PGC_S_DEFAULT)
+                               {
                                        newval = conf->boot_val;
+                                       if (!call_real_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
+                               }
                                else
                                {
                                        newval = conf->reset_val;
+                                       newextra = conf->reset_extra;
                                        source = conf->gen.reset_source;
                                }
 
-                               /* Save old value to support transaction abort */
-                               if (changeVal && !makeDefault)
-                                       push_old_value(&conf->gen, action);
-
-                               if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (newval, changeVal, source))
-                                       {
+                               if (prohibitValueChange)
+                               {
+                                       if (*conf->variable != newval)
                                                ereport(elevel,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                        errmsg("invalid value for parameter \"%s\": %g",
-                                                                       name, newval)));
-                                               return false;
-                                       }
+                                                               (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                                                                errmsg("parameter \"%s\" cannot be changed without restarting the server",
+                                                                               name)));
+                                       return false;
+                               }
 
                                if (changeVal)
                                {
+                                       /* Save old value to support transaction abort */
+                                       if (!makeDefault)
+                                               push_old_value(&conf->gen, action);
+
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (newval, newextra);
                                        *conf->variable = newval;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       newextra);
                                        conf->gen.source = source;
                                }
                                if (makeDefault)
@@ -5215,17 +5528,25 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.reset_source <= source)
                                        {
                                                conf->reset_val = newval;
+                                               set_extra_field(&conf->gen, &conf->reset_extra,
+                                                                               newextra);
                                                conf->gen.reset_source = source;
                                        }
                                        for (stack = conf->gen.stack; stack; stack = stack->prev)
                                        {
                                                if (stack->source <= source)
                                                {
-                                                       stack->prior.realval = newval;
+                                                       stack->prior.val.realval = newval;
+                                                       set_extra_field(&conf->gen, &stack->prior.extra,
+                                                                                       newextra);
                                                        stack->source = source;
                                                }
                                        }
                                }
+
+                               /* Perhaps we didn't install newextra anywhere */
+                               if (newextra && !extra_field_used(&conf->gen, newextra))
+                                       free(newextra);
                                break;
                        }
 
@@ -5233,95 +5554,88 @@ set_config_option(const char *name, const char *value,
                        {
                                struct config_string *conf = (struct config_string *) record;
                                char       *newval;
+                               void       *newextra = NULL;
 
                                if (value)
                                {
+                                       /*
+                                        * The value passed by the caller could be transient,
+                                        * so we always strdup it.
+                                        */
                                        newval = guc_strdup(elevel, value);
                                        if (newval == NULL)
                                                return false;
 
                                        /*
-                                        * The only sort of "parsing" check we need to do is apply
+                                        * The only built-in "parsing" check we have is to apply
                                         * truncation if GUC_IS_NAME.
                                         */
                                        if (conf->gen.flags & GUC_IS_NAME)
                                                truncate_identifier(newval, strlen(newval), true);
+
+                                       if (!call_string_check_hook(conf, &newval, &newextra,
+                                                                                               source, elevel))
+                                       {
+                                               free(newval);
+                                               return false;
+                                       }
                                }
                                else if (source == PGC_S_DEFAULT)
                                {
-                                       if (conf->boot_val == NULL)
-                                               newval = NULL;
-                                       else
+                                       /* non-NULL boot_val must always get strdup'd */
+                                       if (conf->boot_val != NULL)
                                        {
                                                newval = guc_strdup(elevel, conf->boot_val);
                                                if (newval == NULL)
                                                        return false;
                                        }
+                                       else
+                                               newval = NULL;
+
+                                       if (!call_string_check_hook(conf, &newval, &newextra,
+                                                                                               source, elevel))
+                                       {
+                                               free(newval);
+                                               return false;
+                                       }
                                }
                                else
                                {
                                        /*
-                                        * We could possibly avoid strdup here, but easier to make
-                                        * this case work the same as the normal assignment case;
-                                        * note the possible free of newval below.
+                                        * strdup not needed, since reset_val is already under
+                                        * guc.c's control
                                         */
-                                       if (conf->reset_val == NULL)
-                                               newval = NULL;
-                                       else
-                                       {
-                                               newval = guc_strdup(elevel, conf->reset_val);
-                                               if (newval == NULL)
-                                                       return false;
-                                       }
+                                       newval = conf->reset_val;
+                                       newextra = conf->reset_extra;
                                        source = conf->gen.reset_source;
                                }
 
-                               /* Save old value to support transaction abort */
-                               if (changeVal && !makeDefault)
-                                       push_old_value(&conf->gen, action);
-
-                               if (conf->assign_hook && newval)
+                               if (prohibitValueChange)
                                {
-                                       const char *hookresult;
-
-                                       /*
-                                        * If the hook ereports, we have to make sure we free
-                                        * newval, else it will be a permanent memory leak.
-                                        */
-                                       hookresult = call_string_assign_hook(conf->assign_hook,
-                                                                                                                newval,
-                                                                                                                changeVal,
-                                                                                                                source);
-                                       if (hookresult == NULL)
-                                       {
-                                               free(newval);
+                                       /* newval shouldn't be NULL, so we're a bit sloppy here */
+                                       if (*conf->variable == NULL || newval == NULL ||
+                                               strcmp(*conf->variable, newval) != 0)
                                                ereport(elevel,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                                               name, value ? value : "")));
-                                               return false;
-                                       }
-                                       else if (hookresult != newval)
-                                       {
-                                               free(newval);
-
-                                               /*
-                                                * Having to cast away const here is annoying, but the
-                                                * alternative is to declare assign_hooks as returning
-                                                * char*, which would mean they'd have to cast away
-                                                * const, or as both taking and returning char*, which
-                                                * doesn't seem attractive either --- we don't want
-                                                * them to scribble on the passed str.
-                                                */
-                                               newval = (char *) hookresult;
-                                       }
+                                                               (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                                                                errmsg("parameter \"%s\" cannot be changed without restarting the server",
+                                                                               name)));
+                                       return false;
                                }
 
                                if (changeVal)
                                {
+                                       /* Save old value to support transaction abort */
+                                       if (!makeDefault)
+                                               push_old_value(&conf->gen, action);
+
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (newval, newextra);
                                        set_string_field(conf, conf->variable, newval);
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       newextra);
                                        conf->gen.source = source;
                                }
+
                                if (makeDefault)
                                {
                                        GucStack   *stack;
@@ -5329,27 +5643,37 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.reset_source <= source)
                                        {
                                                set_string_field(conf, &conf->reset_val, newval);
+                                               set_extra_field(&conf->gen, &conf->reset_extra,
+                                                                               newextra);
                                                conf->gen.reset_source = source;
                                        }
                                        for (stack = conf->gen.stack; stack; stack = stack->prev)
                                        {
                                                if (stack->source <= source)
                                                {
-                                                       set_string_field(conf, &stack->prior.stringval,
+                                                       set_string_field(conf, &stack->prior.val.stringval,
                                                                                         newval);
+                                                       set_extra_field(&conf->gen, &stack->prior.extra,
+                                                                                       newextra);
                                                        stack->source = source;
                                                }
                                        }
                                }
+
                                /* Perhaps we didn't install newval anywhere */
                                if (newval && !string_field_used(conf, newval))
                                        free(newval);
+                               /* Perhaps we didn't install newextra anywhere */
+                               if (newextra && !extra_field_used(&conf->gen, newextra))
+                                       free(newextra);
                                break;
                        }
+
                case PGC_ENUM:
                        {
                                struct config_enum *conf = (struct config_enum *) record;
                                int                     newval;
+                               void       *newextra = NULL;
 
                                if (value)
                                {
@@ -5371,33 +5695,45 @@ set_config_option(const char *name, const char *value,
                                                        pfree(hintmsg);
                                                return false;
                                        }
+                                       if (!call_enum_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
                                }
                                else if (source == PGC_S_DEFAULT)
+                               {
                                        newval = conf->boot_val;
+                                       if (!call_enum_check_hook(conf, &newval, &newextra,
+                                                                                         source, elevel))
+                                               return false;
+                               }
                                else
                                {
                                        newval = conf->reset_val;
+                                       newextra = conf->reset_extra;
                                        source = conf->gen.reset_source;
                                }
 
-                               /* Save old value to support transaction abort */
-                               if (changeVal && !makeDefault)
-                                       push_old_value(&conf->gen, action);
-
-                               if (conf->assign_hook)
-                                       if (!(*conf->assign_hook) (newval, changeVal, source))
-                                       {
+                               if (prohibitValueChange)
+                               {
+                                       if (*conf->variable != newval)
                                                ereport(elevel,
-                                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("invalid value for parameter \"%s\": \"%s\"",
-                                                               name,
-                                                               config_enum_lookup_by_value(conf, newval))));
-                                               return false;
-                                       }
+                                                               (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+                                                                errmsg("parameter \"%s\" cannot be changed without restarting the server",
+                                                                               name)));
+                                       return false;
+                               }
 
                                if (changeVal)
                                {
+                                       /* Save old value to support transaction abort */
+                                       if (!makeDefault)
+                                               push_old_value(&conf->gen, action);
+
+                                       if (conf->assign_hook)
+                                               (*conf->assign_hook) (newval, newextra);
                                        *conf->variable = newval;
+                                       set_extra_field(&conf->gen, &conf->gen.extra,
+                                                                       newextra);
                                        conf->gen.source = source;
                                }
                                if (makeDefault)
@@ -5407,17 +5743,25 @@ set_config_option(const char *name, const char *value,
                                        if (conf->gen.reset_source <= source)
                                        {
                                                conf->reset_val = newval;
+                                               set_extra_field(&conf->gen, &conf->reset_extra,
+                                                                               newextra);
                                                conf->gen.reset_source = source;
                                        }
                                        for (stack = conf->gen.stack; stack; stack = stack->prev)
                                        {
                                                if (stack->source <= source)
                                                {
-                                                       stack->prior.enumval = newval;
+                                                       stack->prior.val.enumval = newval;
+                                                       set_extra_field(&conf->gen, &stack->prior.extra,
+                                                                                       newextra);
                                                        stack->source = source;
                                                }
                                        }
                                }
+
+                               /* Perhaps we didn't install newextra anywhere */
+                               if (newextra && !extra_field_used(&conf->gen, newextra))
+                                       free(newextra);
                                break;
                        }
        }
@@ -5577,51 +5921,6 @@ GetConfigOptionResetString(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,
@@ -6115,6 +6414,7 @@ DefineCustomBoolVariable(const char *name,
                                                 bool bootValue,
                                                 GucContext context,
                                                 int flags,
+                                                GucBoolCheckHook check_hook,
                                                 GucBoolAssignHook assign_hook,
                                                 GucShowHook show_hook)
 {
@@ -6126,6 +6426,7 @@ DefineCustomBoolVariable(const char *name,
        var->variable = valueAddr;
        var->boot_val = bootValue;
        var->reset_val = bootValue;
+       var->check_hook = check_hook;
        var->assign_hook = assign_hook;
        var->show_hook = show_hook;
        define_custom_variable(&var->gen);
@@ -6141,6 +6442,7 @@ DefineCustomIntVariable(const char *name,
                                                int maxValue,
                                                GucContext context,
                                                int flags,
+                                               GucIntCheckHook check_hook,
                                                GucIntAssignHook assign_hook,
                                                GucShowHook show_hook)
 {
@@ -6154,6 +6456,7 @@ DefineCustomIntVariable(const char *name,
        var->reset_val = bootValue;
        var->min = minValue;
        var->max = maxValue;
+       var->check_hook = check_hook;
        var->assign_hook = assign_hook;
        var->show_hook = show_hook;
        define_custom_variable(&var->gen);
@@ -6169,6 +6472,7 @@ DefineCustomRealVariable(const char *name,
                                                 double maxValue,
                                                 GucContext context,
                                                 int flags,
+                                                GucRealCheckHook check_hook,
                                                 GucRealAssignHook assign_hook,
                                                 GucShowHook show_hook)
 {
@@ -6182,6 +6486,7 @@ DefineCustomRealVariable(const char *name,
        var->reset_val = bootValue;
        var->min = minValue;
        var->max = maxValue;
+       var->check_hook = check_hook;
        var->assign_hook = assign_hook;
        var->show_hook = show_hook;
        define_custom_variable(&var->gen);
@@ -6195,6 +6500,7 @@ DefineCustomStringVariable(const char *name,
                                                   const char *bootValue,
                                                   GucContext context,
                                                   int flags,
+                                                  GucStringCheckHook check_hook,
                                                   GucStringAssignHook assign_hook,
                                                   GucShowHook show_hook)
 {
@@ -6205,9 +6511,7 @@ DefineCustomStringVariable(const char *name,
                                                         PGC_STRING, sizeof(struct config_string));
        var->variable = valueAddr;
        var->boot_val = bootValue;
-       /* we could probably do without strdup, but keep it like normal case */
-       if (var->boot_val)
-               var->reset_val = guc_strdup(ERROR, var->boot_val);
+       var->check_hook = check_hook;
        var->assign_hook = assign_hook;
        var->show_hook = show_hook;
        define_custom_variable(&var->gen);
@@ -6222,6 +6526,7 @@ DefineCustomEnumVariable(const char *name,
                                                 const struct config_enum_entry * options,
                                                 GucContext context,
                                                 int flags,
+                                                GucEnumCheckHook check_hook,
                                                 GucEnumAssignHook assign_hook,
                                                 GucShowHook show_hook)
 {
@@ -6234,6 +6539,7 @@ DefineCustomEnumVariable(const char *name,
        var->boot_val = bootValue;
        var->reset_val = bootValue;
        var->options = options;
+       var->check_hook = check_hook;
        var->assign_hook = assign_hook;
        var->show_hook = show_hook;
        define_custom_variable(&var->gen);
@@ -6995,67 +7301,6 @@ _ShowOption(struct config_generic * record, bool use_units)
 }
 
 
-/*
- * Attempt (badly) to detect if a proposed new GUC setting is the same
- * as the current value.
- *
- * XXX this does not really work because it doesn't account for the
- * effects of canonicalization of string values by assign_hooks.
- */
-static bool
-is_newvalue_equal(struct config_generic * record, const char *newvalue)
-{
-       /* newvalue == NULL isn't supported */
-       Assert(newvalue != NULL);
-
-       switch (record->vartype)
-       {
-               case PGC_BOOL:
-                       {
-                               struct config_bool *conf = (struct config_bool *) record;
-                               bool            newval;
-
-                               return parse_bool(newvalue, &newval)
-                                       && *conf->variable == newval;
-                       }
-               case PGC_INT:
-                       {
-                               struct config_int *conf = (struct config_int *) record;
-                               int                     newval;
-
-                               return parse_int(newvalue, &newval, record->flags, NULL)
-                                       && *conf->variable == newval;
-                       }
-               case PGC_REAL:
-                       {
-                               struct config_real *conf = (struct config_real *) record;
-                               double          newval;
-
-                               return parse_real(newvalue, &newval)
-                                       && *conf->variable == newval;
-                       }
-               case PGC_STRING:
-                       {
-                               struct config_string *conf = (struct config_string *) record;
-
-                               return *conf->variable != NULL &&
-                                       strcmp(*conf->variable, newvalue) == 0;
-                       }
-
-               case PGC_ENUM:
-                       {
-                               struct config_enum *conf = (struct config_enum *) record;
-                               int                     newval;
-
-                               return config_enum_lookup_by_name(conf, newvalue, &newval) &&
-                                       *conf->variable == newval;
-                       }
-       }
-
-       return false;
-}
-
-
 #ifdef EXEC_BACKEND
 
 /*
@@ -7673,30 +7918,222 @@ validate_option_array_item(const char *name, const char *value,
 
 
 /*
- * assign_hook and show_hook subroutines
+ * Called by check_hooks that want to override the normal
+ * ERRCODE_INVALID_PARAMETER_VALUE SQLSTATE for check hook failures.
+ *
+ * Note that GUC_check_errmsg() etc are just macros that result in a direct
+ * assignment to the associated variables.  That is ugly, but forced by the
+ * limitations of C's macro mechanisms.
  */
+void
+GUC_check_errcode(int sqlerrcode)
+{
+       GUC_check_errcode_value = sqlerrcode;
+}
 
-static const char *
-assign_log_destination(const char *value, bool doit, GucSource source)
+
+/*
+ * Convenience functions to manage calling a variable's check_hook.
+ * These mostly take care of the protocol for letting check hooks supply
+ * portions of the error report on failure.
+ */
+
+static bool
+call_bool_check_hook(struct config_bool *conf, bool *newval, void **extra,
+                                        GucSource source, int elevel)
+{
+       /* Quick success if no hook */
+       if (!conf->check_hook)
+               return true;
+
+       /* Reset variables that might be set by hook */
+       GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+       GUC_check_errmsg_string = NULL;
+       GUC_check_errdetail_string = NULL;
+       GUC_check_errhint_string = NULL;
+
+       if (!(*conf->check_hook) (newval, extra, source))
+       {
+               ereport(elevel,
+                               (errcode(GUC_check_errcode_value),
+                                GUC_check_errmsg_string ?
+                                errmsg("%s", GUC_check_errmsg_string) :
+                                errmsg("invalid value for parameter \"%s\": %d",
+                                               conf->gen.name, (int) *newval),
+                                GUC_check_errdetail_string ?
+                                errdetail("%s", GUC_check_errdetail_string) : 0,
+                                GUC_check_errhint_string ?
+                                errhint("%s", GUC_check_errhint_string) : 0));
+               /* Flush any strings created in ErrorContext */
+               FlushErrorState();
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+call_int_check_hook(struct config_int *conf, int *newval, void **extra,
+                                       GucSource source, int elevel)
+{
+       /* Quick success if no hook */
+       if (!conf->check_hook)
+               return true;
+
+       /* Reset variables that might be set by hook */
+       GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+       GUC_check_errmsg_string = NULL;
+       GUC_check_errdetail_string = NULL;
+       GUC_check_errhint_string = NULL;
+
+       if (!(*conf->check_hook) (newval, extra, source))
+       {
+               ereport(elevel,
+                               (errcode(GUC_check_errcode_value),
+                                GUC_check_errmsg_string ?
+                                errmsg("%s", GUC_check_errmsg_string) :
+                                errmsg("invalid value for parameter \"%s\": %d",
+                                               conf->gen.name, *newval),
+                                GUC_check_errdetail_string ?
+                                errdetail("%s", GUC_check_errdetail_string) : 0,
+                                GUC_check_errhint_string ?
+                                errhint("%s", GUC_check_errhint_string) : 0));
+               /* Flush any strings created in ErrorContext */
+               FlushErrorState();
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+call_real_check_hook(struct config_real *conf, double *newval, void **extra,
+                                        GucSource source, int elevel)
+{
+       /* Quick success if no hook */
+       if (!conf->check_hook)
+               return true;
+
+       /* Reset variables that might be set by hook */
+       GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+       GUC_check_errmsg_string = NULL;
+       GUC_check_errdetail_string = NULL;
+       GUC_check_errhint_string = NULL;
+
+       if (!(*conf->check_hook) (newval, extra, source))
+       {
+               ereport(elevel,
+                               (errcode(GUC_check_errcode_value),
+                                GUC_check_errmsg_string ?
+                                errmsg("%s", GUC_check_errmsg_string) :
+                                errmsg("invalid value for parameter \"%s\": %g",
+                                               conf->gen.name, *newval),
+                                GUC_check_errdetail_string ?
+                                errdetail("%s", GUC_check_errdetail_string) : 0,
+                                GUC_check_errhint_string ?
+                                errhint("%s", GUC_check_errhint_string) : 0));
+               /* Flush any strings created in ErrorContext */
+               FlushErrorState();
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+call_string_check_hook(struct config_string *conf, char **newval, void **extra,
+                                          GucSource source, int elevel)
+{
+       /* Quick success if no hook */
+       if (!conf->check_hook)
+               return true;
+
+       /* Reset variables that might be set by hook */
+       GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+       GUC_check_errmsg_string = NULL;
+       GUC_check_errdetail_string = NULL;
+       GUC_check_errhint_string = NULL;
+
+       if (!(*conf->check_hook) (newval, extra, source))
+       {
+               ereport(elevel,
+                               (errcode(GUC_check_errcode_value),
+                                GUC_check_errmsg_string ?
+                                errmsg("%s", GUC_check_errmsg_string) :
+                                errmsg("invalid value for parameter \"%s\": \"%s\"",
+                                               conf->gen.name, *newval ? *newval : ""),
+                                GUC_check_errdetail_string ?
+                                errdetail("%s", GUC_check_errdetail_string) : 0,
+                                GUC_check_errhint_string ?
+                                errhint("%s", GUC_check_errhint_string) : 0));
+               /* Flush any strings created in ErrorContext */
+               FlushErrorState();
+               return false;
+       }
+
+       return true;
+}
+
+static bool
+call_enum_check_hook(struct config_enum *conf, int *newval, void **extra,
+                                        GucSource source, int elevel)
+{
+       /* Quick success if no hook */
+       if (!conf->check_hook)
+               return true;
+
+       /* Reset variables that might be set by hook */
+       GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+       GUC_check_errmsg_string = NULL;
+       GUC_check_errdetail_string = NULL;
+       GUC_check_errhint_string = NULL;
+
+       if (!(*conf->check_hook) (newval, extra, source))
+       {
+               ereport(elevel,
+                               (errcode(GUC_check_errcode_value),
+                                GUC_check_errmsg_string ?
+                                errmsg("%s", GUC_check_errmsg_string) :
+                                errmsg("invalid value for parameter \"%s\": \"%s\"",
+                                               conf->gen.name,
+                                               config_enum_lookup_by_value(conf, *newval)),
+                                GUC_check_errdetail_string ?
+                                errdetail("%s", GUC_check_errdetail_string) : 0,
+                                GUC_check_errhint_string ?
+                                errhint("%s", GUC_check_errhint_string) : 0));
+               /* Flush any strings created in ErrorContext */
+               FlushErrorState();
+               return false;
+       }
+
+       return true;
+}
+
+
+/*
+ * check_hook, assign_hook and show_hook subroutines
+ */
+
+static bool
+check_log_destination(char **newval, void **extra, GucSource source)
 {
        char       *rawstring;
        List       *elemlist;
        ListCell   *l;
        int                     newlogdest = 0;
+       int                *myextra;
 
        /* Need a modifiable copy of string */
-       rawstring = pstrdup(value);
+       rawstring = pstrdup(*newval);
 
        /* Parse string into list of identifiers */
        if (!SplitIdentifierString(rawstring, ',', &elemlist))
        {
                /* syntax error in list */
+               GUC_check_errdetail("List syntax is invalid.");
                pfree(rawstring);
                list_free(elemlist);
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                  errmsg("invalid list syntax for parameter \"log_destination\"")));
-               return NULL;
+               return false;
        }
 
        foreach(l, elemlist)
@@ -7717,105 +8154,103 @@ assign_log_destination(const char *value, bool doit, GucSource source)
 #endif
                else
                {
-                       ereport(GUC_complaint_elevel(source),
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                 errmsg("unrecognized \"log_destination\" key word: \"%s\"",
-                                                tok)));
+                       GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
                        pfree(rawstring);
                        list_free(elemlist);
-                       return NULL;
+                       return false;
                }
        }
 
-       if (doit)
-               Log_destination = newlogdest;
-
        pfree(rawstring);
        list_free(elemlist);
 
-       return value;
+       myextra = (int *) guc_malloc(ERROR, sizeof(int));
+       *myextra = newlogdest;
+       *extra = (void *) myextra;
+
+       return true;
 }
 
-static bool
-assign_syslog_facility(int newval, bool doit, GucSource source)
+static void
+assign_log_destination(const char *newval, void *extra)
+{
+       Log_destination = *((int *) extra);
+}
+
+static void
+assign_syslog_facility(int newval, void *extra)
 {
 #ifdef HAVE_SYSLOG
-       if (doit)
-               set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres",
-                                                         newval);
+       set_syslog_parameters(syslog_ident_str ? syslog_ident_str : "postgres",
+                                                 newval);
 #endif
        /* Without syslog support, just ignore it */
-
-       return true;
 }
 
-static const char *
-assign_syslog_ident(const char *ident, bool doit, GucSource source)
+static void
+assign_syslog_ident(const char *newval, void *extra)
 {
 #ifdef HAVE_SYSLOG
-       if (doit)
-               set_syslog_parameters(ident, syslog_facility);
+       set_syslog_parameters(newval, syslog_facility);
 #endif
        /* Without syslog support, it will always be set to "none", so ignore */
-
-       return ident;
 }
 
 
-static bool
-assign_session_replication_role(int newval, bool doit, GucSource source)
+static void
+assign_session_replication_role(int newval, void *extra)
 {
        /*
         * Must flush the plan cache when changing replication role; but don't
         * flush unnecessarily.
         */
-       if (doit && SessionReplicationRole != newval)
-       {
+       if (SessionReplicationRole != newval)
                ResetPlanCache();
-       }
-
-       return true;
 }
 
-static const char *
-show_num_temp_buffers(void)
+static bool
+check_temp_buffers(int *newval, void **extra, GucSource source)
 {
        /*
-        * We show the GUC var until local buffers have been initialized, and
-        * NLocBuffer afterwards.
+        * Once local buffers have been initialized, it's too late to change this.
         */
-       static char nbuf[32];
-
-       sprintf(nbuf, "%d", NLocBuffer ? NLocBuffer : num_temp_buffers);
-       return nbuf;
+       if (NLocBuffer && NLocBuffer != *newval)
+       {
+               GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temp tables have been accessed in the session.");
+               return false;
+       }
+       return true;
 }
 
 static bool
-assign_phony_autocommit(bool newval, bool doit, GucSource source)
+check_phony_autocommit(bool *newval, void **extra, GucSource source)
 {
-       if (!newval)
+       if (!*newval)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("SET AUTOCOMMIT TO OFF is no longer supported")));
+               GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
+               GUC_check_errmsg("SET AUTOCOMMIT TO OFF is no longer supported");
                return false;
        }
        return true;
 }
 
-static const char *
-assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
+static bool
+check_custom_variable_classes(char **newval, void **extra, GucSource source)
 {
        /*
         * Check syntax. newval must be a comma separated list of identifiers.
         * Whitespace is allowed but removed from the result.
         */
        bool            hasSpaceAfterToken = false;
-       const char *cp = newval;
+       const char *cp = *newval;
        int                     symLen = 0;
        char            c;
        StringInfoData buf;
 
+       /* Default NULL is OK */
+       if (cp == NULL)
+               return true;
+
        initStringInfo(&buf);
        while ((c = *cp++) != '\0')
        {
@@ -7844,7 +8279,7 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
                         * non-identifier character
                         */
                        pfree(buf.data);
-                       return NULL;
+                       return false;
                }
                appendStringInfoChar(&buf, c);
                symLen++;
@@ -7855,21 +8290,20 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
                buf.data[--buf.len] = '\0';
 
        /* GUC wants the result malloc'd */
-       newval = guc_strdup(LOG, buf.data);
+       free(*newval);
+       *newval = guc_strdup(LOG, buf.data);
 
        pfree(buf.data);
-       return newval;
+       return true;
 }
 
 static bool
-assign_debug_assertions(bool newval, bool doit, GucSource source)
+check_debug_assertions(bool *newval, void **extra, GucSource source)
 {
 #ifndef USE_ASSERT_CHECKING
-       if (newval)
+       if (*newval)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                          errmsg("assertion checking is not supported by this build")));
+               GUC_check_errmsg("assertion checking is not supported by this build");
                return false;
        }
 #endif
@@ -7877,14 +8311,12 @@ assign_debug_assertions(bool newval, bool doit, GucSource source)
 }
 
 static bool
-assign_bonjour(bool newval, bool doit, GucSource source)
+check_bonjour(bool *newval, void **extra, GucSource source)
 {
 #ifndef USE_BONJOUR
-       if (newval)
+       if (*newval)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("Bonjour is not supported by this build")));
+               GUC_check_errmsg("Bonjour is not supported by this build");
                return false;
        }
 #endif
@@ -7892,14 +8324,12 @@ assign_bonjour(bool newval, bool doit, GucSource source)
 }
 
 static bool
-assign_ssl(bool newval, bool doit, GucSource source)
+check_ssl(bool *newval, void **extra, GucSource source)
 {
 #ifndef USE_SSL
-       if (newval)
+       if (*newval)
        {
-               ereport(GUC_complaint_elevel(source),
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("SSL is not supported by this build")));
+               GUC_check_errmsg("SSL is not supported by this build");
                return false;
        }
 #endif
@@ -7907,57 +8337,48 @@ assign_ssl(bool newval, bool doit, GucSource source)
 }
 
 static bool
-assign_stage_log_stats(bool newval, bool doit, GucSource source)
+check_stage_log_stats(bool *newval, void **extra, GucSource source)
 {
-       if (newval && log_statement_stats)
+       if (*newval && log_statement_stats)
        {
-               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 */
-               if (source != PGC_S_OVERRIDE)
-                       return false;
+               GUC_check_errdetail("Cannot enable parameter when \"log_statement_stats\" is true.");
+               return false;
        }
        return true;
 }
 
 static bool
-assign_log_stats(bool newval, bool doit, GucSource source)
+check_log_stats(bool *newval, void **extra, GucSource source)
 {
-       if (newval &&
+       if (*newval &&
                (log_parser_stats || log_planner_stats || log_executor_stats))
        {
-               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 */
-               if (source != PGC_S_OVERRIDE)
-                       return false;
+               GUC_check_errdetail("Cannot enable \"log_statement_stats\" when "
+                                                       "\"log_parser_stats\", \"log_planner_stats\", "
+                                                       "or \"log_executor_stats\" is true.");
+               return false;
        }
        return true;
 }
 
-static const char *
-assign_canonical_path(const char *newval, bool doit, GucSource source)
+static bool
+check_canonical_path(char **newval, void **extra, GucSource source)
 {
-       if (doit)
-       {
-               char       *canon_val = guc_strdup(ERROR, newval);
-
-               canonicalize_path(canon_val);
-               return canon_val;
-       }
-       else
-               return newval;
+       /*
+        * Since canonicalize_path never enlarges the string, we can just
+        * modify newval in-place.  But watch out for NULL, which is the
+        * default value for external_pid_file.
+        */
+       if (*newval)
+               canonicalize_path(*newval);
+       return true;
 }
 
-static const char *
-assign_timezone_abbreviations(const char *newval, bool doit, GucSource source)
+static bool
+check_timezone_abbreviations(char **newval, void **extra, GucSource source)
 {
        /*
-        * The powerup value shown above for timezone_abbreviations is "UNKNOWN".
+        * The boot_val given above for timezone_abbreviations is NULL.
         * When we see this we just do nothing.  If this value isn't overridden
         * from the config file then pg_timezone_abbrev_initialize() will
         * eventually replace it with "Default".  This hack has two purposes: to
@@ -7965,34 +8386,32 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source)
         * the config file, and to avoid trying to read the timezone abbrev files
         * during InitializeGUCOptions().  The latter doesn't work in an
         * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet and so
-        * we can't locate PGSHAREDIR.  (Essentially the same hack is used to
-        * delay initializing TimeZone ... if we have any more, we should try to
-        * clean up and centralize this mechanism ...)
+        * we can't locate PGSHAREDIR.
         */
-       if (strcmp(newval, "UNKNOWN") == 0)
+       if (*newval == NULL)
        {
-               return newval;
+               Assert(source == PGC_S_DEFAULT);
+               return true;
        }
 
-       /* Loading abbrev file is expensive, so only do it when value changes */
-       if (timezone_abbreviations_string == NULL ||
-               strcmp(timezone_abbreviations_string, newval) != 0)
-       {
-               int                     elevel;
+       /* OK, load the file and produce a malloc'd TimeZoneAbbrevTable */
+       *extra = load_tzoffsets(*newval);
 
-               /*
-                * If reading config file, only the postmaster should bleat loudly
-                * about problems.      Otherwise, it's just this one process doing it,
-                * and we use WARNING message level.
-                */
-               if (source == PGC_S_FILE)
-                       elevel = IsUnderPostmaster ? DEBUG3 : LOG;
-               else
-                       elevel = WARNING;
-               if (!load_tzoffsets(newval, doit, elevel))
-                       return NULL;
-       }
-       return newval;
+       /* tzparser.c returns NULL on failure, reporting via GUC_check_errmsg */
+       if (!*extra)
+               return false;
+
+       return true;
+}
+
+static void
+assign_timezone_abbreviations(const char *newval, void *extra)
+{
+       /* Do nothing for the boot_val default of NULL */
+       if (!extra)
+               return;
+
+       InstallTimeZoneAbbrevs((TimeZoneAbbrevTable *) extra);
 }
 
 /*
@@ -8004,10 +8423,10 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source)
 void
 pg_timezone_abbrev_initialize(void)
 {
-       if (strcmp(timezone_abbreviations_string, "UNKNOWN") == 0)
+       if (timezone_abbreviations_string == NULL)
        {
                SetConfigOption("timezone_abbreviations", "Default",
-                                               PGC_POSTMASTER, PGC_S_ARGV);
+                                               PGC_POSTMASTER, PGC_S_DEFAULT);
        }
 }
 
@@ -8020,54 +8439,61 @@ show_archive_command(void)
                return "(disabled)";
 }
 
-static bool
-assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
+static void
+assign_tcp_keepalives_idle(int newval, void *extra)
 {
-       if (doit)
-               return (pq_setkeepalivesidle(newval, MyProcPort) == STATUS_OK);
-
-       return true;
+       /*
+        * The kernel API provides no way to test a value without setting it;
+        * and once we set it we might fail to unset it.  So there seems little
+        * point in fully implementing the check-then-assign GUC API for these
+        * variables.  Instead we just do the assignment on demand.  pqcomm.c
+        * reports any problems via elog(LOG).
+        *
+        * This approach means that the GUC value might have little to do with
+        * the actual kernel value, so we use a show_hook that retrieves the
+        * kernel value rather than trusting GUC's copy.
+        */
+       (void) pq_setkeepalivesidle(newval, MyProcPort);
 }
 
 static const char *
 show_tcp_keepalives_idle(void)
 {
+       /* See comments in assign_tcp_keepalives_idle */
        static char nbuf[16];
 
        snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle(MyProcPort));
        return nbuf;
 }
 
-static bool
-assign_tcp_keepalives_interval(int newval, bool doit, GucSource source)
+static void
+assign_tcp_keepalives_interval(int newval, void *extra)
 {
-       if (doit)
-               return (pq_setkeepalivesinterval(newval, MyProcPort) == STATUS_OK);
-
-       return true;
+       /* See comments in assign_tcp_keepalives_idle */
+       (void) pq_setkeepalivesinterval(newval, MyProcPort);
 }
 
 static const char *
 show_tcp_keepalives_interval(void)
 {
+       /* See comments in assign_tcp_keepalives_idle */
        static char nbuf[16];
 
        snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval(MyProcPort));
        return nbuf;
 }
 
-static bool
-assign_tcp_keepalives_count(int newval, bool doit, GucSource source)
+static void
+assign_tcp_keepalives_count(int newval, void *extra)
 {
-       if (doit)
-               return (pq_setkeepalivescount(newval, MyProcPort) == STATUS_OK);
-
-       return true;
+       /* See comments in assign_tcp_keepalives_idle */
+       (void) pq_setkeepalivescount(newval, MyProcPort);
 }
 
 static const char *
 show_tcp_keepalives_count(void)
 {
+       /* See comments in assign_tcp_keepalives_idle */
        static char nbuf[16];
 
        snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount(MyProcPort));
@@ -8075,31 +8501,35 @@ show_tcp_keepalives_count(void)
 }
 
 static bool
-assign_maxconnections(int newval, bool doit, GucSource source)
+check_maxconnections(int *newval, void **extra, GucSource source)
 {
-       if (newval + autovacuum_max_workers + 1 > MAX_BACKENDS)
+       if (*newval + autovacuum_max_workers + 1 > MAX_BACKENDS)
                return false;
-
-       if (doit)
-               MaxBackends = newval + autovacuum_max_workers + 1;
-
        return true;
 }
 
+static void
+assign_maxconnections(int newval, void *extra)
+{
+       MaxBackends = newval + autovacuum_max_workers + 1;
+}
+
 static bool
-assign_autovacuum_max_workers(int newval, bool doit, GucSource source)
+check_autovacuum_max_workers(int *newval, void **extra, GucSource source)
 {
-       if (MaxConnections + newval + 1 > MAX_BACKENDS)
+       if (MaxConnections + *newval + 1 > MAX_BACKENDS)
                return false;
-
-       if (doit)
-               MaxBackends = MaxConnections + newval + 1;
-
        return true;
 }
 
+static void
+assign_autovacuum_max_workers(int newval, void *extra)
+{
+       MaxBackends = MaxConnections + newval + 1;
+}
+
 static bool
-assign_effective_io_concurrency(int newval, bool doit, GucSource source)
+check_effective_io_concurrency(int *newval, void **extra, GucSource source)
 {
 #ifdef USE_PREFETCH
        double          new_prefetch_pages = 0.0;
@@ -8108,6 +8538,8 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source)
        /*----------
         * The user-visible GUC parameter is the number of drives (spindles),
         * which we need to translate to a number-of-pages-to-prefetch target.
+        * The target value is stashed in *extra and then assigned to the actual
+        * variable by assign_effective_io_concurrency.
         *
         * The expected number of prefetch pages needed to keep N drives busy is:
         *
@@ -8132,18 +8564,21 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source)
         * Experimental results show that both of these formulas aren't aggressive
         * enough, but we don't really have any better proposals.
         *
-        * Note that if newval = 0 (disabled), we must set target = 0.
+        * Note that if *newval = 0 (disabled), we must set target = 0.
         *----------
         */
 
-       for (i = 1; i <= newval; i++)
-               new_prefetch_pages += (double) newval / (double) i;
+       for (i = 1; i <= *newval; i++)
+               new_prefetch_pages += (double) *newval / (double) i;
 
        /* This range check shouldn't fail, but let's be paranoid */
        if (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX)
        {
-               if (doit)
-                       target_prefetch_pages = (int) rint(new_prefetch_pages);
+               int        *myextra = (int *) guc_malloc(ERROR, sizeof(int));
+
+               *myextra = (int) rint(new_prefetch_pages);
+               *extra = (void *) myextra;
+
                return true;
        }
        else
@@ -8153,57 +8588,54 @@ assign_effective_io_concurrency(int newval, bool doit, GucSource source)
 #endif   /* USE_PREFETCH */
 }
 
-static const char *
-assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source)
+static void
+assign_effective_io_concurrency(int newval, void *extra)
 {
-       if (doit)
-       {
-               char       *canon_val = guc_strdup(ERROR, newval);
-               char       *tname;
-               char       *fname;
-
-               canonicalize_path(canon_val);
-
-               tname = guc_malloc(ERROR, strlen(canon_val) + 12);              /* /pgstat.tmp */
-               sprintf(tname, "%s/pgstat.tmp", canon_val);
-               fname = guc_malloc(ERROR, strlen(canon_val) + 13);              /* /pgstat.stat */
-               sprintf(fname, "%s/pgstat.stat", canon_val);
+#ifdef USE_PREFETCH
+       target_prefetch_pages = *((int *) extra);
+#endif   /* USE_PREFETCH */
+}
 
-               if (pgstat_stat_tmpname)
-                       free(pgstat_stat_tmpname);
-               pgstat_stat_tmpname = tname;
-               if (pgstat_stat_filename)
-                       free(pgstat_stat_filename);
-               pgstat_stat_filename = fname;
+static void
+assign_pgstat_temp_directory(const char *newval, void *extra)
+{
+       /* check_canonical_path already canonicalized newval for us */
+       char       *tname;
+       char       *fname;
 
-               return canon_val;
-       }
-       else
-               return newval;
+       tname = guc_malloc(ERROR, strlen(newval) + 12);         /* /pgstat.tmp */
+       sprintf(tname, "%s/pgstat.tmp", newval);
+       fname = guc_malloc(ERROR, strlen(newval) + 13);         /* /pgstat.stat */
+       sprintf(fname, "%s/pgstat.stat", newval);
+
+       if (pgstat_stat_tmpname)
+               free(pgstat_stat_tmpname);
+       pgstat_stat_tmpname = tname;
+       if (pgstat_stat_filename)
+               free(pgstat_stat_filename);
+       pgstat_stat_filename = fname;
 }
 
-static const char *
-assign_application_name(const char *newval, bool doit, GucSource source)
+static bool
+check_application_name(char **newval, void **extra, GucSource source)
 {
-       if (doit)
-       {
-               /* Only allow clean ASCII chars in the application name */
-               char       *repval = guc_strdup(ERROR, newval);
-               char       *p;
+       /* Only allow clean ASCII chars in the application name */
+       char       *p;
 
-               for (p = repval; *p; p++)
-               {
-                       if (*p < 32 || *p > 126)
-                               *p = '?';
-               }
+       for (p = *newval; *p; p++)
+       {
+               if (*p < 32 || *p > 126)
+                       *p = '?';
+       }
 
-               /* Update the pg_stat_activity view */
-               pgstat_report_appname(repval);
+       return true;
+}
 
-               return repval;
-       }
-       else
-               return newval;
+static void
+assign_application_name(const char *newval, void *extra)
+{
+       /* Update the pg_stat_activity view */
+       pgstat_report_appname(newval);
 }
 
 static const char *
index e63ce58e69aecf2605c4c101ed93be01e49e42fa..b2f6dd3a2bbdaf189b61e40e93e14227cde01061 100644 (file)
@@ -3,10 +3,12 @@
  * tzparser.c
  *       Functions for parsing timezone offset files
  *
- * Note: we generally should not throw any errors in this file, but instead
- * try to return an error code.  This is not completely bulletproof at
- * present --- in particular out-of-memory will throw an error.  Could
- * probably fix with PG_TRY if necessary.
+ * Note: this code is invoked from the check_hook for the GUC variable
+ * timezone_abbreviations.  Therefore, it should report problems using
+ * GUC_check_errmsg() and related functions, and try to avoid throwing
+ * elog(ERROR).  This is not completely bulletproof at present --- in
+ * particular out-of-memory will throw an error.  Could probably fix with
+ * PG_TRY if necessary.
  *
  *
  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
 
 #include "miscadmin.h"
 #include "storage/fd.h"
-#include "utils/datetime.h"
+#include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/tzparser.h"
 
 
 #define WHITESPACE " \t\n\r"
 
-static int     tz_elevel;                      /* to avoid passing this around a lot */
-
 static bool validateTzEntry(tzEntry *tzentry);
 static bool splitTzLine(const char *filename, int lineno,
                        char *line, tzEntry *tzentry);
@@ -58,20 +58,16 @@ validateTzEntry(tzEntry *tzentry)
         */
        if (strlen(tzentry->abbrev) > TOKMAXLEN)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d",
-                                               tzentry->abbrev, TOKMAXLEN,
-                                               tzentry->filename, tzentry->lineno)));
+               GUC_check_errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d",
+                                                tzentry->abbrev, TOKMAXLEN,
+                                                tzentry->filename, tzentry->lineno);
                return false;
        }
        if (tzentry->offset % 900 != 0)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d",
-                                               tzentry->offset,
-                                               tzentry->filename, tzentry->lineno)));
+               GUC_check_errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d",
+                                                tzentry->offset,
+                                                tzentry->filename, tzentry->lineno);
                return false;
        }
 
@@ -81,11 +77,9 @@ validateTzEntry(tzEntry *tzentry)
        if (tzentry->offset > 14 * 60 * 60 ||
                tzentry->offset < -14 * 60 * 60)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d",
-                                               tzentry->offset,
-                                               tzentry->filename, tzentry->lineno)));
+               GUC_check_errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d",
+                                                tzentry->offset,
+                                                tzentry->filename, tzentry->lineno);
                return false;
        }
 
@@ -118,10 +112,8 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
        abbrev = strtok(line, WHITESPACE);
        if (!abbrev)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("missing time zone abbreviation in time zone file \"%s\", line %d",
-                                               filename, lineno)));
+               GUC_check_errmsg("missing time zone abbreviation in time zone file \"%s\", line %d",
+                                                filename, lineno);
                return false;
        }
        tzentry->abbrev = abbrev;
@@ -129,19 +121,15 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
        offset = strtok(NULL, WHITESPACE);
        if (!offset)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                errmsg("missing time zone offset in time zone file \"%s\", line %d",
-                               filename, lineno)));
+               GUC_check_errmsg("missing time zone offset in time zone file \"%s\", line %d",
+                                                filename, lineno);
                return false;
        }
        tzentry->offset = strtol(offset, &offset_endptr, 10);
        if (offset_endptr == offset || *offset_endptr != '\0')
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("invalid number for time zone offset in time zone file \"%s\", line %d",
-                                               filename, lineno)));
+               GUC_check_errmsg("invalid number for time zone offset in time zone file \"%s\", line %d",
+                                                filename, lineno);
                return false;
        }
 
@@ -163,10 +151,8 @@ splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
 
        if (remain[0] != '#')           /* must be a comment */
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("invalid syntax in time zone file \"%s\", line %d",
-                                               filename, lineno)));
+               GUC_check_errmsg("invalid syntax in time zone file \"%s\", line %d",
+                                                filename, lineno);
                return false;
        }
        return true;
@@ -229,13 +215,11 @@ addToArray(tzEntry **base, int *arraysize, int n,
                                return n;
                        }
                        /* same abbrev but something is different, complain */
-                       ereport(tz_elevel,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                 errmsg("time zone abbreviation \"%s\" is multiply defined",
-                                                entry->abbrev),
-                                        errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.",
-                                                          midptr->filename, midptr->lineno,
-                                                          entry->filename, entry->lineno)));
+                       GUC_check_errmsg("time zone abbreviation \"%s\" is multiply defined",
+                                                        entry->abbrev);
+                       GUC_check_errdetail("Entry in time zone file \"%s\", line %d, conflicts with entry in file \"%s\", line %d.",
+                                                               midptr->filename, midptr->lineno,
+                                                               entry->filename, entry->lineno);
                        return -1;
                }
        }
@@ -296,12 +280,10 @@ ParseTzFile(const char *filename, int depth,
        {
                if (!isalpha((unsigned char) *p))
                {
-                       /* at level 0, we need no ereport since guc.c will say enough */
+                       /* at level 0, just use guc.c's regular "invalid value" message */
                        if (depth > 0)
-                               ereport(tz_elevel,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("invalid time zone file name \"%s\"",
-                                                               filename)));
+                               GUC_check_errmsg("invalid time zone file name \"%s\"",
+                                                                filename);
                        return -1;
                }
        }
@@ -313,10 +295,8 @@ ParseTzFile(const char *filename, int depth,
         */
        if (depth > 3)
        {
-               ereport(tz_elevel,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("time zone file recursion limit exceeded in file \"%s\"",
-                                       filename)));
+               GUC_check_errmsg("time zone file recursion limit exceeded in file \"%s\"",
+                                                filename);
                return -1;
        }
 
@@ -340,12 +320,10 @@ ParseTzFile(const char *filename, int depth,
                tzdir = AllocateDir(file_path);
                if (tzdir == NULL)
                {
-                       ereport(tz_elevel,
-                                       (errcode_for_file_access(),
-                                        errmsg("could not open directory \"%s\": %m",
-                                                       file_path),
-                                        errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.",
-                                                        my_exec_path)));
+                       GUC_check_errmsg("could not open directory \"%s\": %m",
+                                                        file_path);
+                       GUC_check_errhint("This may indicate an incomplete PostgreSQL installation, or that the file \"%s\" has been moved away from its proper location.",
+                                                         my_exec_path);
                        return -1;
                }
                FreeDir(tzdir);
@@ -356,10 +334,8 @@ ParseTzFile(const char *filename, int depth,
                 * complaint is enough
                 */
                if (errno != ENOENT || depth > 0)
-                       ereport(tz_elevel,
-                                       (errcode_for_file_access(),
-                                        errmsg("could not read time zone file \"%s\": %m",
-                                                       filename)));
+                       GUC_check_errmsg("could not read time zone file \"%s\": %m",
+                                                        filename);
 
                return -1;
        }
@@ -371,10 +347,8 @@ ParseTzFile(const char *filename, int depth,
                {
                        if (ferror(tzFile))
                        {
-                               ereport(tz_elevel,
-                                               (errcode_for_file_access(),
-                                                errmsg("could not read time zone file \"%s\": %m",
-                                                               filename)));
+                               GUC_check_errmsg("could not read time zone file \"%s\": %m",
+                                                                filename);
                                return -1;
                        }
                        /* else we're at EOF after all */
@@ -383,10 +357,8 @@ ParseTzFile(const char *filename, int depth,
                if (strlen(tzbuf) == sizeof(tzbuf) - 1)
                {
                        /* the line is too long for tzbuf */
-                       ereport(tz_elevel,
-                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                                errmsg("line is too long in time zone file \"%s\", line %d",
-                                               filename, lineno)));
+                       GUC_check_errmsg("line is too long in time zone file \"%s\", line %d",
+                                                        filename, lineno);
                        return -1;
                }
 
@@ -408,10 +380,8 @@ ParseTzFile(const char *filename, int depth,
                        includeFile = strtok(includeFile, WHITESPACE);
                        if (!includeFile || !*includeFile)
                        {
-                               ereport(tz_elevel,
-                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                errmsg("@INCLUDE without file name in time zone file \"%s\", line %d",
-                                                               filename, lineno)));
+                               GUC_check_errmsg("@INCLUDE without file name in time zone file \"%s\", line %d",
+                                                                filename, lineno);
                                return -1;
                        }
                        n = ParseTzFile(includeFile, depth + 1,
@@ -444,23 +414,20 @@ ParseTzFile(const char *filename, int depth,
 /*
  * load_tzoffsets --- read and parse the specified timezone offset file
  *
- * filename: name specified by user
- * doit: whether to actually apply the new values, or just check
- * elevel: elog reporting level (will be less than ERROR)
- *
- * Returns TRUE if OK, FALSE if not; should avoid erroring out
+ * On success, return a filled-in TimeZoneAbbrevTable, which must have been
+ * malloc'd not palloc'd.  On failure, return NULL, using GUC_check_errmsg
+ * and friends to give details of the problem.
  */
-bool
-load_tzoffsets(const char *filename, bool doit, int elevel)
+TimeZoneAbbrevTable *
+load_tzoffsets(const char *filename)
 {
+       TimeZoneAbbrevTable *result = NULL;
        MemoryContext tmpContext;
        MemoryContext oldContext;
        tzEntry    *array;
        int                     arraysize;
        int                     n;
 
-       tz_elevel = elevel;
-
        /*
         * Create a temp memory context to work in.  This makes it easy to clean
         * up afterwards.
@@ -479,13 +446,20 @@ load_tzoffsets(const char *filename, bool doit, int elevel)
        /* Parse the file(s) */
        n = ParseTzFile(filename, 0, &array, &arraysize, 0);
 
-       /* If no errors and we should apply the result, pass it to datetime.c */
-       if (n >= 0 && doit)
-               InstallTimeZoneAbbrevs(array, n);
+       /* If no errors so far, allocate result and let datetime.c convert data */
+       if (n >= 0)
+       {
+               result = malloc(offsetof(TimeZoneAbbrevTable, abbrevs) +
+                                               n * sizeof(datetkn));
+               if (!result)
+                       GUC_check_errmsg("out of memory");
+               else
+                       ConvertTimeZoneAbbrevs(result, array, n);
+       }
 
        /* Clean up */
        MemoryContextSwitchTo(oldContext);
        MemoryContextDelete(tmpContext);
 
-       return (n >= 0);
+       return result;
 }
index 39bccbd5bf616c1b273ee32fe04009a0f5a7853d..1d904ff984bf7e96e5b2e621dd65808489939429 100644 (file)
 #include "utils/guc.h"
 
 
-extern const char *assign_datestyle(const char *value,
-                                bool doit, GucSource source);
-extern const char *assign_timezone(const char *value,
-                               bool doit, GucSource source);
+extern bool check_datestyle(char **newval, void **extra, GucSource source);
+extern void assign_datestyle(const char *newval, void *extra);
+extern bool check_timezone(char **newval, void **extra, GucSource source);
+extern void assign_timezone(const char *newval, void *extra);
 extern const char *show_timezone(void);
-extern const char *assign_log_timezone(const char *value,
-                                       bool doit, GucSource source);
+extern bool check_log_timezone(char **newval, void **extra, GucSource source);
+extern void assign_log_timezone(const char *newval, void *extra);
 extern const char *show_log_timezone(void);
-extern bool assign_transaction_read_only(bool value,
-                                       bool doit, GucSource source);
-extern const char *assign_XactIsoLevel(const char *value,
-                                       bool doit, GucSource source);
+extern bool check_transaction_read_only(bool *newval, void **extra, GucSource source);
+extern bool check_XactIsoLevel(char **newval, void **extra, GucSource source);
+extern void assign_XactIsoLevel(const char *newval, void *extra);
 extern const char *show_XactIsoLevel(void);
-extern bool assign_transaction_deferrable(bool newval, bool doit,
-                                       GucSource source);
-extern bool assign_random_seed(double value,
-                                  bool doit, GucSource source);
+extern bool check_transaction_deferrable(bool *newval, void **extra, GucSource source);
+extern bool check_random_seed(double *newval, void **extra, GucSource source);
+extern void assign_random_seed(double newval, void *extra);
 extern const char *show_random_seed(void);
-extern const char *assign_client_encoding(const char *value,
-                                          bool doit, GucSource source);
-extern const char *assign_role(const char *value,
-                       bool doit, GucSource source);
+extern bool check_client_encoding(char **newval, void **extra, GucSource source);
+extern void assign_client_encoding(const char *newval, void *extra);
+extern bool check_session_authorization(char **newval, void **extra, GucSource source);
+extern void assign_session_authorization(const char *newval, void *extra);
+extern bool check_role(char **newval, void **extra, GucSource source);
+extern void assign_role(const char *newval, void *extra);
 extern const char *show_role(void);
-extern const char *assign_session_authorization(const char *value,
-                                                        bool doit, GucSource source);
-extern const char *show_session_authorization(void);
 
 #endif   /* VARIABLE_H */
index 85a7b2f87dd257ec5bd4e553299017b630811a55..8efc6d30464bfc4c71bed1b1bc6dbc03376f7b0f 100644 (file)
@@ -397,7 +397,8 @@ extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collat
 extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation);
 #endif
 
-extern int     SetClientEncoding(int encoding, bool doit);
+extern int     PrepareClientEncoding(int encoding);
+extern int     SetClientEncoding(int encoding);
 extern void InitializeClientEncoding(void);
 extern int     pg_get_client_encoding(void);
 extern const char *pg_get_client_encoding_name(void);
index 9022b96585536410f86d1eb657b5128974929a0a..728e2c8f2d2772b6d5704228fdc3abbe14483f0c 100644 (file)
@@ -45,6 +45,6 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
 
 /* called by various procs */
 extern int SyncRepWakeQueue(bool all);
-extern const char *assign_synchronous_standby_names(const char *newval, bool doit, GucSource source);
+extern bool check_synchronous_standby_names(char **newval, void **extra, GucSource source);
 
 #endif   /* _SYNCREP_H */
index bf21cd9d06f110b9b6f56597c3f8f9bd0c7d73dd..d5192d98558de6f79a4e1015e3d79cbf68de25b4 100644 (file)
@@ -57,7 +57,8 @@ extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
 extern List *pg_plan_queries(List *querytrees, int cursorOptions,
                                ParamListInfo boundParams);
 
-extern bool assign_max_stack_depth(int newval, bool doit, GucSource source);
+extern bool check_max_stack_depth(int *newval, void **extra, GucSource source);
+extern void assign_max_stack_depth(int newval, void *extra);
 
 extern void die(SIGNAL_ARGS);
 extern void quickdie(SIGNAL_ARGS);
index 5ae38b2a43ff4b91ff0fda4fe1b2c1a8b5aad7b5..01550eddfe3c412d9fc9076d3659193a0edd386c 100644 (file)
@@ -93,6 +93,7 @@ extern TSDictionaryCacheEntry *lookup_ts_dictionary_cache(Oid dictId);
 extern TSConfigCacheEntry *lookup_ts_config_cache(Oid cfgId);
 
 extern Oid     getTSCurrentConfig(bool emitError);
-extern const char *assignTSCurrentConfig(const char *newval, bool doit, GucSource source);
+extern bool check_TSCurrentConfig(char **newval, void **extra, GucSource source);
+extern void assign_TSCurrentConfig(const char *newval, void *extra);
 
 #endif   /* TS_CACHE_H */
index 11672458f4c2da498c4a2cff744b5681e36090fd..9911d2079364f98fdf6b2e077d59def8c3498e69 100644 (file)
@@ -20,7 +20,9 @@
 #include <math.h>
 
 #include "utils/timestamp.h"
-#include "utils/tzparser.h"
+
+/* this struct is declared in utils/tzparser.h: */
+struct tzEntry;
 
 
 /* ----------------------------------------------------------------
@@ -203,6 +205,13 @@ typedef struct
        char            value;                  /* this may be unsigned, alas */
 } datetkn;
 
+/* one of its uses is in tables of time zone abbreviations */
+typedef struct TimeZoneAbbrevTable
+{
+       int                     numabbrevs;
+       datetkn         abbrevs[1];             /* VARIABLE LENGTH ARRAY */
+} TimeZoneAbbrevTable;
+
 
 /* FMODULO()
  * Macro to replace modf(), which is broken on some platforms.
@@ -317,7 +326,10 @@ extern int DecodeUnits(int field, char *lowtoken, int *val);
 extern int     j2day(int jd);
 
 extern bool CheckDateTokenTables(void);
-extern void InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n);
+
+extern void ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl,
+                                                                  struct tzEntry *abbrevs, int n);
+extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
 
 extern Datum pg_timezone_abbrevs(PG_FUNCTION_ARGS);
 extern Datum pg_timezone_names(PG_FUNCTION_ARGS);
index 71a1cdbd782fe5006a979862c2a5ef69f6d890ab..1f2a9f53c14934e901d3ea6fbc23b7b8de095801 100644 (file)
@@ -200,6 +200,15 @@ elog_finish(int elevel, const char *fmt,...)
 __attribute__((format(printf, 2, 3)));
 
 
+/* Support for constructing error strings separately from ereport() calls */
+
+extern void pre_format_elog_string(int errnumber, const char *domain);
+extern char *format_elog_string(const char *fmt,...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+
 /* Support for attaching context information to error reports */
 
 typedef struct ErrorContextCallback
index c07f263ea073b0dab52c0f3960904e0767cceb45..452310f2bdfe8d6d0f0b6e17470bbb99a4a95b7a 100644 (file)
@@ -97,7 +97,8 @@ typedef enum
 } GucSource;
 
 /*
- * Parsing the configuation file will return a list of name-value pairs
+ * Parsing the configuration file will return a list of name-value pairs
+ * with source location info.
  */
 typedef struct ConfigVariable
 {
@@ -117,7 +118,9 @@ extern bool ParseConfigFp(FILE *fp, const char *config_file,
 extern void FreeConfigVariables(ConfigVariable *list);
 
 /*
- * Enum values are made up of an array of name-value pairs
+ * The possible values of an enum variable are specified by an array of
+ * name-value pairs.  The "hidden" flag means the value is accepted but
+ * won't be displayed when guc.c is asked for a list of acceptable values.
  */
 struct config_enum_entry
 {
@@ -126,15 +129,26 @@ struct config_enum_entry
        bool            hidden;
 };
 
-
-typedef const char *(*GucStringAssignHook) (const char *newval, bool doit, GucSource source);
-typedef bool (*GucBoolAssignHook) (bool newval, bool doit, GucSource source);
-typedef bool (*GucIntAssignHook) (int newval, bool doit, GucSource source);
-typedef bool (*GucRealAssignHook) (double newval, bool doit, GucSource source);
-typedef bool (*GucEnumAssignHook) (int newval, bool doit, GucSource source);
+/*
+ * Signatures for per-variable check/assign/show hook functions
+ */
+typedef bool (*GucBoolCheckHook) (bool *newval, void **extra, GucSource source);
+typedef bool (*GucIntCheckHook) (int *newval, void **extra, GucSource source);
+typedef bool (*GucRealCheckHook) (double *newval, void **extra, GucSource source);
+typedef bool (*GucStringCheckHook) (char **newval, void **extra, GucSource source);
+typedef bool (*GucEnumCheckHook) (int *newval, void **extra, GucSource source);
+
+typedef void (*GucBoolAssignHook) (bool newval, void *extra);
+typedef void (*GucIntAssignHook) (int newval, void *extra);
+typedef void (*GucRealAssignHook) (double newval, void *extra);
+typedef void (*GucStringAssignHook) (const char *newval, void *extra);
+typedef void (*GucEnumAssignHook) (int newval, void *extra);
 
 typedef const char *(*GucShowHook) (void);
 
+/*
+ * Miscellaneous
+ */
 typedef enum
 {
        /* Types of set_config_option actions */
@@ -201,7 +215,6 @@ extern char *ConfigFileName;
 extern char *HbaFileName;
 extern char *IdentFileName;
 extern char *external_pid_file;
-extern char *XactIsoLevel_string;
 
 extern char *application_name;
 
@@ -209,6 +222,9 @@ extern int  tcp_keepalives_idle;
 extern int     tcp_keepalives_interval;
 extern int     tcp_keepalives_count;
 
+/*
+ * Functions exported by guc.c
+ */
 extern void SetConfigOption(const char *name, const char *value,
                                GucContext context, GucSource source);
 
@@ -220,6 +236,7 @@ extern void DefineCustomBoolVariable(
                                                 bool bootValue,
                                                 GucContext context,
                                                 int flags,
+                                                GucBoolCheckHook check_hook,
                                                 GucBoolAssignHook assign_hook,
                                                 GucShowHook show_hook);
 
@@ -233,6 +250,7 @@ extern void DefineCustomIntVariable(
                                                int maxValue,
                                                GucContext context,
                                                int flags,
+                                               GucIntCheckHook check_hook,
                                                GucIntAssignHook assign_hook,
                                                GucShowHook show_hook);
 
@@ -246,6 +264,7 @@ extern void DefineCustomRealVariable(
                                                 double maxValue,
                                                 GucContext context,
                                                 int flags,
+                                                GucRealCheckHook check_hook,
                                                 GucRealAssignHook assign_hook,
                                                 GucShowHook show_hook);
 
@@ -257,6 +276,7 @@ extern void DefineCustomStringVariable(
                                                   const char *bootValue,
                                                   GucContext context,
                                                   int flags,
+                                                  GucStringCheckHook check_hook,
                                                   GucStringAssignHook assign_hook,
                                                   GucShowHook show_hook);
 
@@ -269,6 +289,7 @@ extern void DefineCustomEnumVariable(
                                                 const struct config_enum_entry * options,
                                                 GucContext context,
                                                 int flags,
+                                                GucEnumCheckHook check_hook,
                                                 GucEnumAssignHook assign_hook,
                                                 GucShowHook show_hook);
 
@@ -308,8 +329,6 @@ extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *va
 extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
 extern ArrayType *GUCArrayReset(ArrayType *array);
 
-extern int     GUC_complaint_elevel(GucSource source);
-
 extern void pg_timezone_abbrev_initialize(void);
 
 #ifdef EXEC_BACKEND
@@ -317,6 +336,27 @@ extern void write_nondefault_variables(GucContext context);
 extern void read_nondefault_variables(void);
 #endif
 
+/* Support for messages reported from GUC check hooks */
+
+extern PGDLLIMPORT char *GUC_check_errmsg_string;
+extern PGDLLIMPORT char *GUC_check_errdetail_string;
+extern PGDLLIMPORT char *GUC_check_errhint_string;
+
+extern void GUC_check_errcode(int sqlerrcode);
+
+#define GUC_check_errmsg \
+       pre_format_elog_string(errno, TEXTDOMAIN), \
+       GUC_check_errmsg_string = format_elog_string
+
+#define GUC_check_errdetail \
+       pre_format_elog_string(errno, TEXTDOMAIN), \
+       GUC_check_errdetail_string = format_elog_string
+
+#define GUC_check_errhint \
+       pre_format_elog_string(errno, TEXTDOMAIN), \
+       GUC_check_errhint_string = format_elog_string
+
+
 /*
  * The following functions are not in guc.c, but are declared here to avoid
  * having to include guc.h in some widely used headers that it really doesn't
@@ -324,17 +364,16 @@ extern void read_nondefault_variables(void);
  */
 
 /* in commands/tablespace.c */
-extern const char *assign_default_tablespace(const char *newval,
-                                                 bool doit, GucSource source);
-extern const char *assign_temp_tablespaces(const char *newval,
-                                               bool doit, GucSource source);
+extern bool check_default_tablespace(char **newval, void **extra, GucSource source);
+extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source);
+extern void assign_temp_tablespaces(const char *newval, void *extra);
 
 /* in catalog/namespace.c */
-extern const char *assign_search_path(const char *newval,
-                                  bool doit, GucSource source);
+extern bool check_search_path(char **newval, void **extra, GucSource source);
+extern void assign_search_path(const char *newval, void *extra);
 
 /* in access/transam/xlog.c */
-extern bool assign_xlog_sync_method(int newval,
-                                               bool doit, GucSource source);
+extern bool check_wal_buffers(int *newval, void **extra, GucSource source);
+extern void assign_xlog_sync_method(int new_sync_method, void *extra);
 
 #endif   /* GUC_H */
index 073b77d28ce60c11d45c4d0a8651e8a506997245..a1ca012ef9a7b8aade5bf50106f3d6d6bf6c95b1 100644 (file)
@@ -28,7 +28,7 @@ enum config_type
        PGC_ENUM
 };
 
-union config_var_value
+union config_var_val
 {
        bool            boolval;
        int                     intval;
@@ -37,6 +37,16 @@ union config_var_value
        int                     enumval;
 };
 
+/*
+ * The actual value of a GUC variable can include a malloc'd opaque struct
+ * "extra", which is created by its check_hook and used by its assign_hook.
+ */
+typedef struct config_var_value
+{
+       union config_var_val val;
+       void       *extra;
+} config_var_value;
+
 /*
  * Groupings to help organize all the run-time options for display
  */
@@ -105,8 +115,8 @@ typedef struct guc_stack
        int                     nest_level;             /* nesting depth at which we made entry */
        GucStackState state;            /* see enum above */
        GucSource       source;                 /* source of the prior value */
-       union config_var_value prior;           /* previous value of variable */
-       union config_var_value masked;          /* SET value in a GUC_SET_LOCAL entry */
+       config_var_value prior;         /* previous value of variable */
+       config_var_value masked;        /* SET value in a GUC_SET_LOCAL entry */
        /* masked value's source must be PGC_S_SESSION, so no need to store it */
 } GucStack;
 
@@ -116,6 +126,11 @@ typedef struct guc_stack
  * The short description should be less than 80 chars in length. Some
  * applications may use the long description as well, and will append
  * it to the short description. (separated by a newline or '. ')
+ *
+ * Note that sourcefile/sourceline are kept here, and not pushed into stacked
+ * values, although in principle they belong with some stacked value if the
+ * active value is session- or transaction-local.  This is to avoid bloating
+ * stack entries.  We know they are only relevant when source == PGC_S_FILE.
  */
 struct config_generic
 {
@@ -132,7 +147,8 @@ struct config_generic
        GucSource       reset_source;   /* source of the reset_value */
        GucSource       source;                 /* source of the current actual value */
        GucStack   *stack;                      /* stacked prior values */
-       char       *sourcefile;         /* file this settings is from (NULL if not
+       void       *extra;                      /* "extra" pointer for current actual value */
+       char       *sourcefile;         /* file current setting is from (NULL if not
                                                                 * file) */
        int                     sourceline;             /* line in source file */
 };
@@ -155,10 +171,12 @@ struct config_bool
        /* constant fields, must be set correctly in initial value: */
        bool       *variable;
        bool            boot_val;
+       GucBoolCheckHook check_hook;
        GucBoolAssignHook assign_hook;
        GucShowHook show_hook;
        /* variable fields, initialized at runtime: */
        bool            reset_val;
+       void       *reset_extra;
 };
 
 struct config_int
@@ -169,10 +187,12 @@ struct config_int
        int                     boot_val;
        int                     min;
        int                     max;
+       GucIntCheckHook check_hook;
        GucIntAssignHook assign_hook;
        GucShowHook show_hook;
        /* variable fields, initialized at runtime: */
        int                     reset_val;
+       void       *reset_extra;
 };
 
 struct config_real
@@ -183,10 +203,12 @@ struct config_real
        double          boot_val;
        double          min;
        double          max;
+       GucRealCheckHook check_hook;
        GucRealAssignHook assign_hook;
        GucShowHook show_hook;
        /* variable fields, initialized at runtime: */
        double          reset_val;
+       void       *reset_extra;
 };
 
 struct config_string
@@ -195,10 +217,12 @@ struct config_string
        /* constant fields, must be set correctly in initial value: */
        char      **variable;
        const char *boot_val;
+       GucStringCheckHook check_hook;
        GucStringAssignHook assign_hook;
        GucShowHook show_hook;
        /* variable fields, initialized at runtime: */
        char       *reset_val;
+       void       *reset_extra;
 };
 
 struct config_enum
@@ -208,10 +232,12 @@ struct config_enum
        int                *variable;
        int                     boot_val;
        const struct config_enum_entry *options;
+       GucEnumCheckHook check_hook;
        GucEnumAssignHook assign_hook;
        GucShowHook show_hook;
        /* variable fields, initialized at runtime: */
        int                     reset_val;
+       void       *reset_extra;
 };
 
 /* constant tables corresponding to enums above and in guc.h */
index 4c72fd0dc6c3137757175713fc5169c22e1aeed6..25b9d5091505c6b17e8cba2b1b0a8d4bf572aa33 100644 (file)
@@ -33,14 +33,14 @@ extern char *localized_abbrev_months[];
 extern char *localized_full_months[];
 
 
-extern const char *locale_messages_assign(const char *value,
-                                          bool doit, GucSource source);
-extern const char *locale_monetary_assign(const char *value,
-                                          bool doit, GucSource source);
-extern const char *locale_numeric_assign(const char *value,
-                                         bool doit, GucSource source);
-extern const char *locale_time_assign(const char *value,
-                                  bool doit, GucSource source);
+extern bool check_locale_messages(char **newval, void **extra, GucSource source);
+extern void assign_locale_messages(const char *newval, void *extra);
+extern bool check_locale_monetary(char **newval, void **extra, GucSource source);
+extern void assign_locale_monetary(const char *newval, void *extra);
+extern bool check_locale_numeric(char **newval, void **extra, GucSource source);
+extern void assign_locale_numeric(const char *newval, void *extra);
+extern bool check_locale_time(char **newval, void **extra, GucSource source);
+extern void assign_locale_time(const char *newval, void *extra);
 
 extern bool check_locale(int category, const char *locale);
 extern char *pg_perm_setlocale(int category, const char *locale);
index ba18819ef5928a4101843fb300c464682ebdb22f..2065078116a3e11bdc2b94f5cba5b58a6867cb20 100644 (file)
@@ -13,6 +13,8 @@
 #ifndef TZPARSER_H
 #define TZPARSER_H
 
+#include "utils/datetime.h"
+
 /*
  * The result of parsing a timezone configuration file is an array of
  * these structs, in order by abbrev.  We export this because datetime.c
@@ -30,6 +32,6 @@ typedef struct tzEntry
 } tzEntry;
 
 
-extern bool load_tzoffsets(const char *filename, bool doit, int elevel);
+extern TimeZoneAbbrevTable *load_tzoffsets(const char *filename);
 
 #endif   /* TZPARSER_H */
index 9a94b3f085236d4333eb1a80a8e1d19dcb891681..35bf72ca30ffd0388a3df8ffec61aa786dcc2c9e 100644 (file)
@@ -363,7 +363,7 @@ _PG_init(void)
                                                         &plperl_use_strict,
                                                         false,
                                                         PGC_USERSET, 0,
-                                                        NULL, NULL);
+                                                        NULL, NULL, NULL);
 
        /*
         * plperl.on_init is marked PGC_SIGHUP to support the idea that it might
@@ -377,7 +377,7 @@ _PG_init(void)
                                                           &plperl_on_init,
                                                           NULL,
                                                           PGC_SIGHUP, 0,
-                                                          NULL, NULL);
+                                                          NULL, NULL, NULL);
 
        /*
         * plperl.on_plperl_init is marked PGC_SUSET to avoid issues whereby a
@@ -399,7 +399,7 @@ _PG_init(void)
                                                           &plperl_on_plperl_init,
                                                           NULL,
                                                           PGC_SUSET, 0,
-                                                          NULL, NULL);
+                                                          NULL, NULL, NULL);
 
        DefineCustomStringVariable("plperl.on_plperlu_init",
                                                           gettext_noop("Perl initialization code to execute once when plperlu is first used."),
@@ -407,7 +407,7 @@ _PG_init(void)
                                                           &plperl_on_plperlu_init,
                                                           NULL,
                                                           PGC_SUSET, 0,
-                                                          NULL, NULL);
+                                                          NULL, NULL, NULL);
 
        EmitWarningsOnPlaceholders("plperl");
 
index 2389849a223195fb3e78e884d07e17b85f08fb00..e6e71432ff9ae72e43c402a4873446234dc7d7da 100644 (file)
@@ -63,7 +63,7 @@ _PG_init(void)
                                                         PLPGSQL_RESOLVE_ERROR,
                                                         variable_conflict_options,
                                                         PGC_SUSET, 0,
-                                                        NULL, NULL);
+                                                        NULL, NULL, NULL);
 
        EmitWarningsOnPlaceholders("plpgsql");
 
index a0e24e84cbf68ced1a3aca0ecb4e824883215790..98671a2bcdf892cd0573fd1e8ed7e2d0bcf2d3af 100644 (file)
@@ -622,7 +622,8 @@ alter function report_guc(text) set search_path = no_such_schema;
 NOTICE:  schema "no_such_schema" does not exist
 -- with error occurring here
 select report_guc('work_mem'), current_setting('work_mem');
-ERROR:  schema "no_such_schema" does not exist
+ERROR:  invalid value for parameter "search_path": "no_such_schema"
+DETAIL:  schema "no_such_schema" does not exist
 alter function report_guc(text) reset search_path set work_mem = '2MB';
 select report_guc('work_mem'), current_setting('work_mem');
  report_guc | current_setting 
index d09d4ad736f54f04ed82b67a12ef4f27321d8f57..382a4e04f506b4e2b1cfb70fbb561292df214ab0 100644 (file)
@@ -1444,27 +1444,39 @@ pg_timezone_initialize(void)
 {
        pg_tz      *def_tz = NULL;
 
-       /* Do we need to try to figure the session timezone? */
-       if (pg_strcasecmp(GetConfigOption("timezone", false), "UNKNOWN") == 0)
+       /*
+        * Make sure that session_timezone and log_timezone are set.
+        * (session_timezone could still be NULL even if a timezone value was set
+        * in postgresql.conf, if that setting was interval-based rather than
+        * timezone-based.)
+        */
+       if (!session_timezone)
        {
-               /* Select setting */
                def_tz = select_default_timezone();
                session_timezone = def_tz;
-               /* Tell GUC about the value. Will redundantly call pg_tzset() */
-               SetConfigOption("timezone", pg_get_timezone_name(def_tz),
-                                               PGC_POSTMASTER, PGC_S_ARGV);
        }
-
-       /* What about the log timezone? */
-       if (pg_strcasecmp(GetConfigOption("log_timezone", false), "UNKNOWN") == 0)
+       if (!log_timezone)
        {
-               /* Select setting, but don't duplicate work */
+               /* Don't duplicate work */
                if (!def_tz)
                        def_tz = select_default_timezone();
                log_timezone = def_tz;
+       }
+
+       /* Now, set the timezone GUC if it's not already set */
+       if (GetConfigOption("timezone", false) == NULL)
+       {
+               /* Tell GUC about the value. Will redundantly call pg_tzset() */
+               SetConfigOption("timezone", pg_get_timezone_name(session_timezone),
+                                               PGC_POSTMASTER, PGC_S_ENV_VAR);
+       }
+
+       /* Likewise for log timezone */
+       if (GetConfigOption("log_timezone", false) == NULL)
+       {
                /* Tell GUC about the value. Will redundantly call pg_tzset() */
-               SetConfigOption("log_timezone", pg_get_timezone_name(def_tz),
-                                               PGC_POSTMASTER, PGC_S_ARGV);
+               SetConfigOption("log_timezone", pg_get_timezone_name(log_timezone),
+                                               PGC_POSTMASTER, PGC_S_ENV_VAR);
        }
 }