aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-03-11 21:31:28 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2013-03-11 21:31:28 -0400
commitcc3f281ffb0a5d9b187e7a7b7de4a045809ff683 (patch)
treeb4e6954f09642abb8502d15e5127aabbaf9c056d /contrib/postgres_fdw/postgres_fdw.c
parent8f9cc41daf08be802933dc788517743719ee0949 (diff)
downloadpostgresql-cc3f281ffb0a5d9b187e7a7b7de4a045809ff683.tar.gz
postgresql-cc3f281ffb0a5d9b187e7a7b7de4a045809ff683.zip
Fix postgres_fdw's issues with inconsistent interpretation of data values.
For datatypes whose output formatting depends on one or more GUC settings, we have to worry about whether the other server will interpret the value the same way it was meant. pg_dump has been aware of this hazard for a long time, but postgres_fdw needs to deal with it too. To fix data retrieval from the remote server, set the necessary remote GUC settings at connection startup. (We were already assuming that settings made then would persist throughout the remote session.) To fix data transmission to the remote server, temporarily force the relevant GUCs to the right values when we're about to convert any data values to text for transmission. This is all pretty grotty, and not very cheap either. It's tempting to think of defining one uber-GUC that would override any settings that might render printed data values unportable. But of course, older remote servers wouldn't know any such thing and would still need this logic. While at it, revert commit f7951eef89be78c50ea2241f593d76dfefe176c9, since this provides a real fix. (The timestamptz given in the error message returned from the "remote" server will now reliably be shown in UTC.)
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c65
1 files changed, 64 insertions, 1 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index a6db061d603..95505c8a1c7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -30,6 +30,7 @@
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
+#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -1554,9 +1555,12 @@ create_cursor(ForeignScanState *node)
if (numParams > 0 && !fsstate->extparams_done)
{
ParamListInfo params = node->ss.ps.state->es_param_list_info;
+ int nestlevel;
List *param_numbers;
ListCell *lc;
+ nestlevel = set_transmission_modes();
+
param_numbers = (List *)
list_nth(fsstate->fdw_private, FdwScanPrivateExternParamIds);
foreach(lc, param_numbers)
@@ -1598,6 +1602,9 @@ create_cursor(ForeignScanState *node)
prm->value);
}
}
+
+ reset_transmission_modes(nestlevel);
+
fsstate->extparams_done = true;
}
@@ -1706,6 +1713,56 @@ fetch_more_data(ForeignScanState *node)
}
/*
+ * Force assorted GUC parameters to settings that ensure that we'll output
+ * data values in a form that is unambiguous to the remote server.
+ *
+ * This is rather expensive and annoying to do once per row, but there's
+ * little choice if we want to be sure values are transmitted accurately;
+ * we can't leave the settings in place between rows for fear of affecting
+ * user-visible computations.
+ *
+ * We use the equivalent of a function SET option to allow the settings to
+ * persist only until the caller calls reset_transmission_modes(). If an
+ * error is thrown in between, guc.c will take care of undoing the settings.
+ *
+ * The return value is the nestlevel that must be passed to
+ * reset_transmission_modes() to undo things.
+ */
+int
+set_transmission_modes(void)
+{
+ int nestlevel = NewGUCNestLevel();
+
+ /*
+ * The values set here should match what pg_dump does. See also
+ * configure_remote_session in connection.c.
+ */
+ if (DateStyle != USE_ISO_DATES)
+ (void) set_config_option("datestyle", "ISO",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0);
+ if (IntervalStyle != INTSTYLE_POSTGRES)
+ (void) set_config_option("intervalstyle", "postgres",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0);
+ if (extra_float_digits < 3)
+ (void) set_config_option("extra_float_digits", "3",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0);
+
+ return nestlevel;
+}
+
+/*
+ * Undo the effects of set_transmission_modes().
+ */
+void
+reset_transmission_modes(int nestlevel)
+{
+ AtEOXact_GUC(true, nestlevel);
+}
+
+/*
* Utility routine to close a cursor.
*/
static void
@@ -1791,16 +1848,20 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate,
/* 1st parameter should be ctid, if it's in use */
if (tupleid != NULL)
{
+ /* don't need set_transmission_modes for TID output */
p_values[pindex] = OutputFunctionCall(&fmstate->p_flinfo[pindex],
PointerGetDatum(tupleid));
pindex++;
}
/* get following parameters from slot */
- if (slot != NULL)
+ if (slot != NULL && fmstate->target_attrs != NIL)
{
+ int nestlevel;
ListCell *lc;
+ nestlevel = set_transmission_modes();
+
foreach(lc, fmstate->target_attrs)
{
int attnum = lfirst_int(lc);
@@ -1815,6 +1876,8 @@ convert_prep_stmt_params(PgFdwModifyState *fmstate,
value);
pindex++;
}
+
+ reset_transmission_modes(nestlevel);
}
Assert(pindex == fmstate->p_nums);