Similar in purpose to cc3f281ffb0a5d9b187e7a7b7de4a045809ff683, but
taking into account that a dblink caller may choose to cause arbitrary
changes to DateStyle and IntervalStyle.  To handle this, it is
necessary to use PQparameterStatus before parsing any input, every
time.  This is avoided in cases that do not include the two
problematic types treated -- interval and timestamptz -- by scanning
the TupleDesc's types first.

Although it has been suggested that extra_float_digits would need
similar treatment as IntervalStyle and DateStyle (as it's seen in
pg_dump), extra_float_digits is not an GUC_REPORT-ed GUC and is not
able to be checked in PQparameterStatus, and furthermore, the float4
and float8 parsers are not sensitive to the GUC anyway: both accept as
much precision as is provided in an unambiguous way.
---
 contrib/dblink/dblink.c            |  225 +++++++++++++++++++++++++++++++++++-
 contrib/dblink/expected/dblink.out |  162 ++++++++++++++++++++++++++
 contrib/dblink/sql/dblink.sql      |   91 +++++++++++++++
 3 files changed, 472 insertions(+), 6 deletions(-)
*** a/contrib/dblink/dblink.c
--- b/contrib/dblink/dblink.c
***************
*** 53,58 ****
--- 53,59 ----
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
+ #include "utils/guc.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
***************
*** 69,74 **** typedef struct remoteConn
--- 70,120 ----
  	bool		newXactForCursor;		/* Opened a transaction for a cursor */
  } remoteConn;
  
+ /*
+  * Used to index into an array to remember GUC info associated with
+  * types where input and output formatting may change the
+  * interpretation of the data.
+  */
+ typedef enum
+ {
+ 	GST_TIMESTAMPTZ = 0,
+ 	GST_INTERVAL,
+ 
+ 	/* GST_MAX must be the last element, for array sizing. */
+ 	GST_MAX
+ } gucSensitiveTypes;
+ 
+ /*
+  * remoteGucInfo saves the value of one GUC in connection with one
+  * type so that the GUC environment locally can match for correct type
+  * parsing.
+  */
+ typedef struct remoteGucInfo
+ {
+ 	bool		 isInitialized;
+ 	const char	*gucName;
+ 	const char	*remoteVal;
+ } remoteGucInfo;
+ 
+ /*
+  * Contains information to save and restore GUCs for types with
+  * GUC-sensitive parsing.
+  */
+ typedef struct remoteGucs
+ {
+ 	/*
+ 	 * GUC nesting level.  Set to -1 if no GUC nesting level has been
+ 	 * introduced.
+ 	 */
+ 	int localGUCNestLevel;
+ 
+ 	/* Kept around for PQparameterStatus to interrogate remote GUCs */
+ 	PGconn *conn;
+ 
+ 	/* Memory enough for each affected data type */
+ 	remoteGucInfo rgis[GST_MAX];
+ } remoteGucs;
+ 
  typedef struct storeInfo
  {
  	FunctionCallInfo fcinfo;
***************
*** 79,84 **** typedef struct storeInfo
--- 125,131 ----
  	/* temp storage for results to avoid leaks on exception */
  	PGresult   *last_res;
  	PGresult   *cur_res;
+ 	remoteGucs *rgs;
  } storeInfo;
  
  /*
***************
*** 86,92 **** typedef struct storeInfo
   */
  static Datum dblink_record_internal(FunctionCallInfo fcinfo, bool is_async);
  static void prepTuplestoreResult(FunctionCallInfo fcinfo);
! static void materializeResult(FunctionCallInfo fcinfo, PGresult *res);
  static void materializeQueryResult(FunctionCallInfo fcinfo,
  					   PGconn *conn,
  					   const char *conname,
--- 133,140 ----
   */
  static Datum dblink_record_internal(FunctionCallInfo fcinfo, bool is_async);
  static void prepTuplestoreResult(FunctionCallInfo fcinfo);
! static void materializeResult(FunctionCallInfo fcinfo, remoteGucs *rgs,
! 							  PGresult *res);
  static void materializeQueryResult(FunctionCallInfo fcinfo,
  					   PGconn *conn,
  					   const char *conname,
***************
*** 118,123 **** static void validate_pkattnums(Relation rel,
--- 166,174 ----
  				   int **pkattnums, int *pknumatts);
  static bool is_valid_dblink_option(const PQconninfoOption *options,
  					   const char *option, Oid context);
+ static void initRemoteGucs(remoteGucs *rgs, PGconn *conn);
+ static void applyRemoteGucs(remoteGucs *rgs, TupleDesc tupdesc);
+ static void restoreLocalGucs(remoteGucs *rgs);
  
  /* Global */
  static remoteConn *pconn = NULL;
***************
*** 531,536 **** dblink_fetch(PG_FUNCTION_ARGS)
--- 582,588 ----
  	char	   *curname = NULL;
  	int			howmany = 0;
  	bool		fail = true;	/* default to backward compatible */
+ 	remoteGucs	rgs;
  
  	prepTuplestoreResult(fcinfo);
  
***************
*** 605,611 **** dblink_fetch(PG_FUNCTION_ARGS)
  				 errmsg("cursor \"%s\" does not exist", curname)));
  	}
  
! 	materializeResult(fcinfo, res);
  	return (Datum) 0;
  }
  
--- 657,665 ----
  				 errmsg("cursor \"%s\" does not exist", curname)));
  	}
  
! 	initRemoteGucs(&rgs, conn);
! 	materializeResult(fcinfo, &rgs, res);
! 
  	return (Datum) 0;
  }
  
***************
*** 656,663 **** dblink_get_result(PG_FUNCTION_ARGS)
  static Datum
  dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
  {
! 	PGconn	   *volatile conn = NULL;
! 	volatile bool freeconn = false;
  
  	prepTuplestoreResult(fcinfo);
  
--- 710,718 ----
  static Datum
  dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
  {
! 	PGconn	   *volatile		conn	 = NULL;
! 	volatile bool				freeconn = false;
! 	remoteGucs					rgs;
  
  	prepTuplestoreResult(fcinfo);
  
***************
*** 750,756 **** dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
  				}
  				else
  				{
! 					materializeResult(fcinfo, res);
  				}
  			}
  		}
--- 805,812 ----
  				}
  				else
  				{
! 					initRemoteGucs(&rgs, conn);
! 					materializeResult(fcinfo, &rgs, res);
  				}
  			}
  		}
***************
*** 803,812 **** prepTuplestoreResult(FunctionCallInfo fcinfo)
  /*
   * Copy the contents of the PGresult into a tuplestore to be returned
   * as the result of the current function.
   * The PGresult will be released in this function.
   */
  static void
! materializeResult(FunctionCallInfo fcinfo, PGresult *res)
  {
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
  
--- 859,869 ----
  /*
   * Copy the contents of the PGresult into a tuplestore to be returned
   * as the result of the current function.
+  *
   * The PGresult will be released in this function.
   */
  static void
! materializeResult(FunctionCallInfo fcinfo, remoteGucs *rgs, PGresult *res)
  {
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
  
***************
*** 891,896 **** materializeResult(FunctionCallInfo fcinfo, PGresult *res)
--- 948,956 ----
  			rsinfo->setDesc = tupdesc;
  			MemoryContextSwitchTo(oldcontext);
  
+ 			/* Check types in return tupdesc for GUC sensitivity */
+ 			applyRemoteGucs(rgs, tupdesc);
+ 
  			values = (char **) palloc(nfields * sizeof(char *));
  
  			/* put all tuples into the tuplestore */
***************
*** 930,938 **** materializeResult(FunctionCallInfo fcinfo, PGresult *res)
--- 990,1004 ----
  	{
  		/* be sure to release the libpq result */
  		PQclear(res);
+ 
+ 		/* Pop any set GUCs, if necessary */
+ 		restoreLocalGucs(rgs);
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
+ 
+ 	/* Pop any set GUCs, if necessary */
+ 	restoreLocalGucs(rgs);
  }
  
  /*
***************
*** 953,958 **** materializeQueryResult(FunctionCallInfo fcinfo,
--- 1019,1027 ----
  	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
  	PGresult   *volatile res = NULL;
  	storeInfo	sinfo;
+ 	remoteGucs	rgs;
+ 
+ 	initRemoteGucs(&rgs, conn);
  
  	/* prepTuplestoreResult must have been called previously */
  	Assert(rsinfo->returnMode == SFRM_Materialize);
***************
*** 960,965 **** materializeQueryResult(FunctionCallInfo fcinfo,
--- 1029,1035 ----
  	/* initialize storeInfo to empty */
  	memset(&sinfo, 0, sizeof(sinfo));
  	sinfo.fcinfo = fcinfo;
+ 	sinfo.rgs = &rgs;
  
  	PG_TRY();
  	{
***************
*** 1041,1049 **** materializeQueryResult(FunctionCallInfo fcinfo,
--- 1111,1126 ----
  		/* and clear out any pending data in libpq */
  		while ((res = PQgetResult(conn)) != NULL)
  			PQclear(res);
+ 
+ 		/* Pop any set GUCs, if necessary */
+ 		restoreLocalGucs(&rgs);
+ 
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
+ 
+ 	/* Pop any set GUCs, if necessary */
+ 	restoreLocalGucs(&rgs);
  }
  
  /*
***************
*** 1186,1191 **** storeRow(storeInfo *sinfo, PGresult *res, bool first)
--- 1263,1277 ----
  									  ALLOCSET_DEFAULT_MINSIZE,
  									  ALLOCSET_DEFAULT_INITSIZE,
  									  ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 
+ 		/*
+ 		 * Apply any necessary GUCs to parse input properly.
+ 		 *
+ 		 * NB: This application of GUCs is un-done in callers, which in
+ 		 * particular have PG_TRY/PG_CATCH set up.
+ 		 */
+ 		applyRemoteGucs(sinfo->rgs, tupdesc);
  	}
  
  	/* Should have a single-row result if we get here */
***************
*** 2898,2900 **** is_valid_dblink_option(const PQconninfoOption *options, const char *option,
--- 2984,3113 ----
  
  	return true;
  }
+ 
+ /* Initializer for a "remoteGucs" struct value. */
+ static void
+ initRemoteGucs(remoteGucs *rgs, PGconn *conn)
+ {
+ 	rgs->localGUCNestLevel = -1;
+ 	rgs->conn			   = conn;
+ 
+ 	MemSet(&rgs->rgis, 0, sizeof rgs->rgis);
+ }
+ 
+ /*
+  * Scan a TupleDesc and, should it contain types that are sensitive to
+  * GUCs, acquire remote GUCs and set them in a new GUC nesting level.
+  * This is undone with restoreLocalGucs.
+  */
+ static void
+ applyRemoteGucs(remoteGucs *rgs, TupleDesc tupdesc)
+ {
+ 	int			field;
+ 	int			gstCur;
+ 	bool		anyInitialized = false;
+ 
+ 	for (field = 0; field < tupdesc->natts; field += 1)
+ 	{
+ 		const Oid typ = tupdesc->attrs[field]->atttypid;
+ 		remoteGucInfo *gi;
+ 
+ 		switch (typ)
+ 		{
+ 			case TIMESTAMPTZOID:
+ 				gi = &rgs->rgis[GST_TIMESTAMPTZ];
+ 				gi->gucName = "DateStyle";
+ 				break;
+ 
+ 			case INTERVALOID:
+ 				gi = &rgs->rgis[GST_INTERVAL];
+ 				gi->gucName = "IntervalStyle";
+ 				break;
+ 
+ 			default:
+ 				/*
+ 				 * Not an interesting type: keep looking for another.
+ 				 */
+ 				continue;
+ 		}
+ 
+ 		if (gi->isInitialized)
+ 		{
+ 			/*
+ 			 * If the affected type has been seen twice in the tupdesc
+ 			 * (e.g. two timestamptz attributes), the necessary work
+ 			 * has already been done in a previous iteration.
+ 			 */
+ 			continue;
+ 		}
+ 
+ 		/*
+ 		 * Looks like this is a type that needs GUC saving.  Assert some
+ 		 * invariants before going through with it.
+ 		 */
+ 		Assert(gi != NULL);
+ 		Assert(!gi->isInitialized);
+ 		Assert(gi->gucName != NULL);
+ 
+ 		/* Save the remote GUC */
+ 		gi->remoteVal = PQparameterStatus(rgs->conn, gi->gucName);
+ 
+ 		gi->isInitialized = true;
+ 		anyInitialized = true;
+ 	}
+ 
+ 	/*
+ 	 * Exit immediately without applying any GUCs if no types seen in
+ 	 * the tupdesc are affected.
+ 	 */
+ 	if (!anyInitialized)
+ 		return;
+ 
+ 	/*
+ 	 * Affected types require local GUC manipulations.  Create a new
+ 	 * GUC NestLevel to overlay the remote settings.
+ 	 *
+ 	 * Also, this nesting is done exactly once per remoteGucInfo
+ 	 * structure, so expect it to come with an invalid NestLevel.
+ 	 */
+ 	Assert(rgs->localGUCNestLevel == -1);
+ 	rgs->localGUCNestLevel = NewGUCNestLevel();
+ 
+ 	for (gstCur = 0; gstCur < GST_MAX; gstCur += 1)
+ 	{
+ 		const remoteGucInfo *rgi = &rgs->rgis[gstCur];
+ 		int gucApplyStatus;
+ 
+ 		if (!rgi->isInitialized)
+ 			continue;
+ 
+ 		gucApplyStatus = set_config_option(rgi->gucName, rgi->remoteVal,
+ 										   PGC_USERSET, PGC_S_SESSION,
+ 										   GUC_ACTION_SAVE, true, 0);
+ 		if (gucApplyStatus != 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("cannot load remote configuration %s "
+ 							"for type parsing",
+ 							rgi->gucName)));
+ 	}
+ }
+ 
+ /*
+  * Restore local GUCs after they have been overlaid with remote
+  * settings for type parsing, destroying the GUC nesting level.
+  */
+ static void
+ restoreLocalGucs(remoteGucs *rgs)
+ {
+ 	/*
+ 	 * A new GUCNestLevel was not introduced, so don't bother
+ 	 * restoring, either.
+ 	 */
+ 	if (rgs->localGUCNestLevel == -1)
+ 	{
+ 		return;
+ 	}
+ 
+ 	AtEOXact_GUC(false, rgs->localGUCNestLevel);
+ }
*** a/contrib/dblink/expected/dblink.out
--- b/contrib/dblink/expected/dblink.out
***************
*** 913,915 **** SELECT dblink_build_sql_delete('test_dropped', '1', 1,
--- 913,1077 ----
   DELETE FROM test_dropped WHERE id = '2'
  (1 row)
  
+ -- test the local mimicry of remote GUC values in parsing for affected
+ -- types
+ SET datestyle = ISO, MDY;
+ SET intervalstyle = postgres;
+ SET timezone = UTC;
+ SELECT dblink_connect('myconn','dbname=contrib_regression');
+  dblink_connect 
+ ----------------
+  OK
+ (1 row)
+ 
+ SELECT dblink_exec('myconn', 'SET datestyle = GERMAN, DMY;');
+  dblink_exec 
+ -------------
+  SET
+ (1 row)
+ 
+ -- The following attempt test various paths at which TupleDescs are
+ -- formed and inspected for containment of types requiring local GUC
+ -- setting.
+ -- single row synchronous case
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00'')) t')
+ 	 AS t(a timestamptz);
+            a            
+ ------------------------
+  2013-03-12 00:00:00+00
+ (1 row)
+ 
+ -- multi-row synchronous case
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00''),
+ 		   (''12.03.2013 00:00:00+00'')) t')
+ 	 AS t(a timestamptz);
+            a            
+ ------------------------
+  2013-03-12 00:00:00+00
+  2013-03-12 00:00:00+00
+ (2 rows)
+ 
+ -- single-row asynchronous case
+ SELECT *
+ FROM dblink_send_query('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00'')) t');
+  dblink_send_query 
+ -------------------
+                  1
+ (1 row)
+ 
+ CREATE TEMPORARY TABLE result AS
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz));
+ SELECT * FROM result;
+            t            
+ ------------------------
+  2013-03-12 00:00:00+00
+ (1 row)
+ 
+ DROP TABLE result;
+ -- multi-row asynchronous case
+ SELECT *
+ FROM dblink_send_query('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00''),
+ 		   (''12.03.2013 00:00:00+00'')) t');
+  dblink_send_query 
+ -------------------
+                  1
+ (1 row)
+ 
+ CREATE TEMPORARY TABLE result AS
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz));
+ SELECT * FROM result;
+            t            
+ ------------------------
+  2013-03-12 00:00:00+00
+  2013-03-12 00:00:00+00
+ (2 rows)
+ 
+ DROP TABLE result;
+ -- Try an ambiguous interval
+ SELECT dblink_exec('myconn', 'SET intervalstyle = sql_standard;');
+  dblink_exec 
+ -------------
+  SET
+ (1 row)
+ 
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''-1 2:03:04'')) i')
+ 	 AS i(i interval);
+          i         
+ -------------------
+  -1 days -02:03:04
+ (1 row)
+ 
+ -- Try swapping to another format to ensure the GUCs are tracked
+ -- properly through a change.
+ SELECT dblink_exec('myconn', 'SET datestyle = GERMAN, DMY;');
+  dblink_exec 
+ -------------
+  SET
+ (1 row)
+ 
+ CREATE TEMPORARY TABLE result (t timestamptz);
+ INSERT INTO result (SELECT *
+ 	   FROM dblink('myconn',
+ 			'SELECT * FROM
+ 		(VALUES (''12.03.2013 00:00:00+00'')) t')
+ 		AS t(a timestamptz));
+ SELECT dblink_exec('myconn', 'SET datestyle = ISO, MDY;');
+  dblink_exec 
+ -------------
+  SET
+ (1 row)
+ 
+ INSERT INTO result (SELECT *
+ 	   FROM dblink('myconn',
+ 			'SELECT * FROM
+ 		(VALUES (''03.12.2013 00:00:00+00'')) t')
+ 		AS t(a timestamptz));
+ SELECT DISTINCT * FROM result;
+            t            
+ ------------------------
+  2013-03-12 00:00:00+00
+ (1 row)
+ 
+ DROP TABLE result;
+ SELECT dblink_disconnect('myconn');
+  dblink_disconnect 
+ -------------------
+  OK
+ (1 row)
+ 
+ -- Make sure that the local values have retained their value in spite
+ -- of shenanigans on the connection.
+ SHOW datestyle;
+  DateStyle 
+ -----------
+  ISO, MDY
+ (1 row)
+ 
+ SHOW intervalstyle;
+  IntervalStyle 
+ ---------------
+  postgres
+ (1 row)
+ 
+ RESET datestyle;
+ RESET intervalstyle;
+ RESET timezone;
*** a/contrib/dblink/sql/dblink.sql
--- b/contrib/dblink/sql/dblink.sql
***************
*** 426,428 **** SELECT dblink_build_sql_update('test_dropped', '1', 1,
--- 426,519 ----
  
  SELECT dblink_build_sql_delete('test_dropped', '1', 1,
                                 ARRAY['2'::TEXT]);
+ 
+ -- test the local mimicry of remote GUC values in parsing for affected
+ -- types
+ SET datestyle = ISO, MDY;
+ SET intervalstyle = postgres;
+ SET timezone = UTC;
+ SELECT dblink_connect('myconn','dbname=contrib_regression');
+ SELECT dblink_exec('myconn', 'SET datestyle = GERMAN, DMY;');
+ 
+ -- The following attempt test various paths at which TupleDescs are
+ -- formed and inspected for containment of types requiring local GUC
+ -- setting.
+ 
+ -- single row synchronous case
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00'')) t')
+ 	 AS t(a timestamptz);
+ 
+ -- multi-row synchronous case
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00''),
+ 		   (''12.03.2013 00:00:00+00'')) t')
+ 	 AS t(a timestamptz);
+ 
+ -- single-row asynchronous case
+ SELECT *
+ FROM dblink_send_query('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00'')) t');
+ CREATE TEMPORARY TABLE result AS
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz));
+ SELECT * FROM result;
+ DROP TABLE result;
+ 
+ -- multi-row asynchronous case
+ SELECT *
+ FROM dblink_send_query('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''12.03.2013 00:00:00+00''),
+ 		   (''12.03.2013 00:00:00+00'')) t');
+ CREATE TEMPORARY TABLE result AS
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz))
+ UNION ALL
+ (SELECT * from dblink_get_result('myconn') as t(t timestamptz));
+ SELECT * FROM result;
+ DROP TABLE result;
+ 
+ -- Try an ambiguous interval
+ SELECT dblink_exec('myconn', 'SET intervalstyle = sql_standard;');
+ SELECT *
+ FROM dblink('myconn',
+ 	   'SELECT * FROM
+ 	   (VALUES (''-1 2:03:04'')) i')
+ 	 AS i(i interval);
+ 
+ -- Try swapping to another format to ensure the GUCs are tracked
+ -- properly through a change.
+ SELECT dblink_exec('myconn', 'SET datestyle = GERMAN, DMY;');
+ CREATE TEMPORARY TABLE result (t timestamptz);
+ INSERT INTO result (SELECT *
+ 	   FROM dblink('myconn',
+ 			'SELECT * FROM
+ 		(VALUES (''12.03.2013 00:00:00+00'')) t')
+ 		AS t(a timestamptz));
+ SELECT dblink_exec('myconn', 'SET datestyle = ISO, MDY;');
+ INSERT INTO result (SELECT *
+ 	   FROM dblink('myconn',
+ 			'SELECT * FROM
+ 		(VALUES (''03.12.2013 00:00:00+00'')) t')
+ 		AS t(a timestamptz));
+ SELECT DISTINCT * FROM result;
+ DROP TABLE result;
+ 
+ SELECT dblink_disconnect('myconn');
+ 
+ -- Make sure that the local values have retained their value in spite
+ -- of shenanigans on the connection.
+ SHOW datestyle;
+ SHOW intervalstyle;
+ 
+ RESET datestyle;
+ RESET intervalstyle;
+ RESET timezone;
