diff --git a/contrib/auth_delay/auth_delay.c b/contrib/auth_delay/auth_delay.c
index 199de9bb39130de2d0ae0862571a464515f107c3..ca388c4498414d86cb0fbd889ef1f851e9f93ddf 100644
--- a/contrib/auth_delay/auth_delay.c
+++ b/contrib/auth_delay/auth_delay.c
@@ -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;
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 78e604e7bc4f0f6203bacd4047d55541afd9b9e9..b3206987484e57a91850033fef01bc9c507172e9 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -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");
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 0390ec4c8ed6d3e9d6c09cd85b3cadbc2dd3a6fb..87cf8c55cf491831bbf5a9db79ca1eb3e9b81cc9 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -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");
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 27e85d25b1d33222a4c935b13af9c8263a720bac..5dc8a3ecaa8b704a158409d6be46d8cd755253c5 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -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);
 
 	/*
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b22e498f8b50a8d4c212f0637862e81cc92cd7e2..b31c79ebbdcb03660c81e5f83c55e107ee2b6fb2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -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;
 }
 
 
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 77c9805aed1c6d70214c01938b957d88377f969d..734581e48557dcd72e8e7796729f344d00afd9ce 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -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;
 }
 
 /*
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index b5a2d9d005e223f49913ca64068e5955ff3da403..42a704beb164f39316862f4bdcdcc6c6b6edd1ba 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -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);
 }
 
 /*
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 2a61ea3bc7cc970efffaede20d4da4d414faa121..2cec713089668825d49e6cf9e95c2dbc5ee6ea42 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -33,10 +33,10 @@
  */
 
 /*
- * 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";
 }
diff --git a/src/backend/nls.mk b/src/backend/nls.mk
index eec8a5e2b0dd409945b284c9a2e296385494e890..cf09335a01cd358141b1e86d04f2f610d72ac9f6 100644
--- a/src/backend/nls.mk
+++ b/src/backend/nls.mk
@@ -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 >$@
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 0d3381465108919d5c086e8ae01223c32afb2742..192aac9aea3fd6582710af5c3786dd3baf505889 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -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;
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 79c0f68966ea33e0ad7f76625b1e582eda6da4d5..cc7945aeb669cc9d558854dcf51cf69e12c3f801 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index f0fe2e31a283293570a343f2ba4b215c647b28c0..0410b8384e9d18dfb62f8bc6e59239f2da17aa86 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -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++)
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 163856d5b18d24ce2a6e5667bd75a71d2c153a39..cbf74a07f2dcde385578562987d6ce486501505b 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -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
 }
 
 
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 6a99e78c6a42c7fac68671948850d7a851bb9ad6..fc93551d069d2de1bce3de51dacd8385fab6dea9 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -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;
 }
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e7bc046d87007d40431d262a9a0e80aa6edac12f..9e58735aeec2fdc48e9838a5d1997e52db51afb9 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -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
  *
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index a60188df1099a944f6b0b6fec157052ce31f4381..b1281778036a398cf4f31fafcf8a728ac742affb 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -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
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 881862a30b182a94cd71c6ed1a98dd6e6d5e51e0..70244ced18d869c5e46e8f1a6a455f9704c27ae0 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -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.
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index e660395898274a94ca182641dacdbdc8fefa8bce..10ef12eb24be0e0b4d463b27e111962dd4a1fcb0 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -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);
 	}
 
 	/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2151fde36185612f43c3299e8e5db4c1f54a2e57..5e4904aeb7fc8a34828358b68c41661365f573bd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -104,7 +104,7 @@
  * 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 *
diff --git a/src/backend/utils/misc/tzparser.c b/src/backend/utils/misc/tzparser.c
index e63ce58e69aecf2605c4c101ed93be01e49e42fa..b2f6dd3a2bbdaf189b61e40e93e14227cde01061 100644
--- a/src/backend/utils/misc/tzparser.c
+++ b/src/backend/utils/misc/tzparser.c
@@ -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
@@ -24,15 +26,13 @@
 
 #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;
 }
diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h
index 39bccbd5bf616c1b273ee32fe04009a0f5a7853d..1d904ff984bf7e96e5b2e621dd65808489939429 100644
--- a/src/include/commands/variable.h
+++ b/src/include/commands/variable.h
@@ -13,31 +13,28 @@
 #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 */
diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h
index 85a7b2f87dd257ec5bd4e553299017b630811a55..8efc6d30464bfc4c71bed1b1bc6dbc03376f7b0f 100644
--- a/src/include/mb/pg_wchar.h
+++ b/src/include/mb/pg_wchar.h
@@ -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);
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index 9022b96585536410f86d1eb657b5128974929a0a..728e2c8f2d2772b6d5704228fdc3abbe14483f0c 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -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 */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index bf21cd9d06f110b9b6f56597c3f8f9bd0c7d73dd..d5192d98558de6f79a4e1015e3d79cbf68de25b4 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -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);
diff --git a/src/include/tsearch/ts_cache.h b/src/include/tsearch/ts_cache.h
index 5ae38b2a43ff4b91ff0fda4fe1b2c1a8b5aad7b5..01550eddfe3c412d9fc9076d3659193a0edd386c 100644
--- a/src/include/tsearch/ts_cache.h
+++ b/src/include/tsearch/ts_cache.h
@@ -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 */
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 11672458f4c2da498c4a2cff744b5681e36090fd..9911d2079364f98fdf6b2e077d59def8c3498e69 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -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);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 71a1cdbd782fe5006a979862c2a5ef69f6d890ab..1f2a9f53c14934e901d3ea6fbc23b7b8de095801 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -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
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07f263ea073b0dab52c0f3960904e0767cceb45..452310f2bdfe8d6d0f0b6e17470bbb99a4a95b7a 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -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 */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 073b77d28ce60c11d45c4d0a8651e8a506997245..a1ca012ef9a7b8aade5bf50106f3d6d6bf6c95b1 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -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 */
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 4c72fd0dc6c3137757175713fc5169c22e1aeed6..25b9d5091505c6b17e8cba2b1b0a8d4bf572aa33 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -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);
diff --git a/src/include/utils/tzparser.h b/src/include/utils/tzparser.h
index ba18819ef5928a4101843fb300c464682ebdb22f..2065078116a3e11bdc2b94f5cba5b58a6867cb20 100644
--- a/src/include/utils/tzparser.h
+++ b/src/include/utils/tzparser.h
@@ -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 */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 9a94b3f085236d4333eb1a80a8e1d19dcb891681..35bf72ca30ffd0388a3df8ffec61aa786dcc2c9e 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -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");
 
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 2389849a223195fb3e78e884d07e17b85f08fb00..e6e71432ff9ae72e43c402a4873446234dc7d7da 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -63,7 +63,7 @@ _PG_init(void)
 							 PLPGSQL_RESOLVE_ERROR,
 							 variable_conflict_options,
 							 PGC_SUSET, 0,
-							 NULL, NULL);
+							 NULL, NULL, NULL);
 
 	EmitWarningsOnPlaceholders("plpgsql");
 
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index a0e24e84cbf68ced1a3aca0ecb4e824883215790..98671a2bcdf892cd0573fd1e8ed7e2d0bcf2d3af 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -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 
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index d09d4ad736f54f04ed82b67a12ef4f27321d8f57..382a4e04f506b4e2b1cfb70fbb561292df214ab0 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -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);
 	}
 }