aboutsummaryrefslogtreecommitdiff
path: root/contrib/dblink/dblink.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/dblink/dblink.c')
-rw-r--r--contrib/dblink/dblink.c105
1 files changed, 100 insertions, 5 deletions
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index bba0d2adc3c..e8ad94ba841 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -53,6 +53,7 @@
#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"
@@ -86,7 +87,8 @@ 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 materializeResult(FunctionCallInfo fcinfo, PGconn *conn,
+ PGresult *res);
static void materializeQueryResult(FunctionCallInfo fcinfo,
PGconn *conn,
const char *conname,
@@ -118,6 +120,8 @@ static void validate_pkattnums(Relation rel,
int **pkattnums, int *pknumatts);
static bool is_valid_dblink_option(const PQconninfoOption *options,
const char *option, Oid context);
+static int applyRemoteGucs(PGconn *conn);
+static void restoreLocalGucs(int nestlevel);
/* Global */
static remoteConn *pconn = NULL;
@@ -605,7 +609,7 @@ dblink_fetch(PG_FUNCTION_ARGS)
errmsg("cursor \"%s\" does not exist", curname)));
}
- materializeResult(fcinfo, res);
+ materializeResult(fcinfo, conn, res);
return (Datum) 0;
}
@@ -750,7 +754,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
}
else
{
- materializeResult(fcinfo, res);
+ materializeResult(fcinfo, conn, res);
}
}
}
@@ -806,7 +810,7 @@ prepTuplestoreResult(FunctionCallInfo fcinfo)
* The PGresult will be released in this function.
*/
static void
-materializeResult(FunctionCallInfo fcinfo, PGresult *res)
+materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
@@ -816,7 +820,7 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
PG_TRY();
{
TupleDesc tupdesc;
- bool is_sql_cmd = false;
+ bool is_sql_cmd;
int ntuples;
int nfields;
@@ -877,6 +881,7 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
if (ntuples > 0)
{
AttInMetadata *attinmeta;
+ int nestlevel = -1;
Tuplestorestate *tupstore;
MemoryContext oldcontext;
int row;
@@ -884,6 +889,10 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ /* Set GUCs to ensure we read GUC-sensitive data types correctly */
+ if (!is_sql_cmd)
+ nestlevel = applyRemoteGucs(conn);
+
oldcontext = MemoryContextSwitchTo(
rsinfo->econtext->ecxt_per_query_memory);
tupstore = tuplestore_begin_heap(true, false, work_mem);
@@ -920,6 +929,9 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
tuplestore_puttuple(tupstore, tuple);
}
+ /* clean up GUC settings, if we changed any */
+ restoreLocalGucs(nestlevel);
+
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
}
@@ -1053,6 +1065,7 @@ static PGresult *
storeQueryResult(storeInfo *sinfo, PGconn *conn, const char *sql)
{
bool first = true;
+ int nestlevel = -1;
PGresult *res;
if (!PQsendQuery(conn, sql))
@@ -1072,6 +1085,15 @@ storeQueryResult(storeInfo *sinfo, PGconn *conn, const char *sql)
if (PQresultStatus(sinfo->cur_res) == PGRES_SINGLE_TUPLE)
{
/* got one row from possibly-bigger resultset */
+
+ /*
+ * Set GUCs to ensure we read GUC-sensitive data types correctly.
+ * We shouldn't do this until we have a row in hand, to ensure
+ * libpq has seen any earlier ParameterStatus protocol messages.
+ */
+ if (first && nestlevel < 0)
+ nestlevel = applyRemoteGucs(conn);
+
storeRow(sinfo, sinfo->cur_res, first);
PQclear(sinfo->cur_res);
@@ -1092,6 +1114,9 @@ storeQueryResult(storeInfo *sinfo, PGconn *conn, const char *sql)
}
}
+ /* clean up GUC settings, if we changed any */
+ restoreLocalGucs(nestlevel);
+
/* return last_res */
res = sinfo->last_res;
sinfo->last_res = NULL;
@@ -2898,3 +2923,73 @@ is_valid_dblink_option(const PQconninfoOption *options, const char *option,
return true;
}
+
+/*
+ * Copy the remote session's values of GUCs that affect datatype I/O
+ * and apply them locally in a new GUC nesting level. Returns the new
+ * nestlevel (which is needed by restoreLocalGucs to undo the settings),
+ * or -1 if no new nestlevel was needed.
+ *
+ * We use the equivalent of a function SET option to allow the settings to
+ * persist only until the caller calls restoreLocalGucs. If an error is
+ * thrown in between, guc.c will take care of undoing the settings.
+ */
+static int
+applyRemoteGucs(PGconn *conn)
+{
+ static const char *const GUCsAffectingIO[] = {
+ "DateStyle",
+ "IntervalStyle"
+ };
+
+ int nestlevel = -1;
+ int i;
+
+ for (i = 0; i < lengthof(GUCsAffectingIO); i++)
+ {
+ const char *gucName = GUCsAffectingIO[i];
+ const char *remoteVal = PQparameterStatus(conn, gucName);
+ const char *localVal;
+
+ /*
+ * If the remote server is pre-8.4, it won't have IntervalStyle, but
+ * that's okay because its output format won't be ambiguous. So just
+ * skip the GUC if we don't get a value for it. (We might eventually
+ * need more complicated logic with remote-version checks here.)
+ */
+ if (remoteVal == NULL)
+ continue;
+
+ /*
+ * Avoid GUC-setting overhead if the remote and local GUCs already
+ * have the same value.
+ */
+ localVal = GetConfigOption(gucName, false, false);
+ Assert(localVal != NULL);
+
+ if (strcmp(remoteVal, localVal) == 0)
+ continue;
+
+ /* Create new GUC nest level if we didn't already */
+ if (nestlevel < 0)
+ nestlevel = NewGUCNestLevel();
+
+ /* Apply the option (this will throw error on failure) */
+ (void) set_config_option(gucName, remoteVal,
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0);
+ }
+
+ return nestlevel;
+}
+
+/*
+ * Restore local GUCs after they have been overlaid with remote settings.
+ */
+static void
+restoreLocalGucs(int nestlevel)
+{
+ /* Do nothing if no new nestlevel was created */
+ if (nestlevel > 0)
+ AtEOXact_GUC(true, nestlevel);
+}