47
47
#include "utils/acl.h"
48
48
#include "utils/builtins.h"
49
49
#include "utils/fmgroids.h"
50
+ #include "utils/guc.h"
50
51
#include "utils/lsyscache.h"
51
52
#include "utils/memutils.h"
52
53
#include "utils/rel.h"
@@ -80,7 +81,8 @@ typedef struct storeInfo
80
81
*/
81
82
static Datum dblink_record_internal (FunctionCallInfo fcinfo , bool is_async );
82
83
static void prepTuplestoreResult (FunctionCallInfo fcinfo );
83
- static void materializeResult (FunctionCallInfo fcinfo , PGresult * res );
84
+ static void materializeResult (FunctionCallInfo fcinfo , PGconn * conn ,
85
+ PGresult * res );
84
86
static void materializeQueryResult (FunctionCallInfo fcinfo ,
85
87
PGconn * conn ,
86
88
const char * conname ,
@@ -110,6 +112,8 @@ static char *escape_param_str(const char *from);
110
112
static void validate_pkattnums (Relation rel ,
111
113
int2vector * pkattnums_arg , int32 pknumatts_arg ,
112
114
int * * pkattnums , int * pknumatts );
115
+ static int applyRemoteGucs (PGconn * conn );
116
+ static void restoreLocalGucs (int nestlevel );
113
117
114
118
/* Global */
115
119
static remoteConn * pconn = NULL ;
@@ -597,7 +601,7 @@ dblink_fetch(PG_FUNCTION_ARGS)
597
601
errmsg ("cursor \"%s\" does not exist" , curname )));
598
602
}
599
603
600
- materializeResult (fcinfo , res );
604
+ materializeResult (fcinfo , conn , res );
601
605
return (Datum ) 0 ;
602
606
}
603
607
@@ -742,7 +746,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
742
746
}
743
747
else
744
748
{
745
- materializeResult (fcinfo , res );
749
+ materializeResult (fcinfo , conn , res );
746
750
}
747
751
}
748
752
}
@@ -798,7 +802,7 @@ prepTuplestoreResult(FunctionCallInfo fcinfo)
798
802
* The PGresult will be released in this function.
799
803
*/
800
804
static void
801
- materializeResult (FunctionCallInfo fcinfo , PGresult * res )
805
+ materializeResult (FunctionCallInfo fcinfo , PGconn * conn , PGresult * res )
802
806
{
803
807
ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
804
808
@@ -808,7 +812,7 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
808
812
PG_TRY ();
809
813
{
810
814
TupleDesc tupdesc ;
811
- bool is_sql_cmd = false ;
815
+ bool is_sql_cmd ;
812
816
int ntuples ;
813
817
int nfields ;
814
818
@@ -869,13 +873,18 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
869
873
if (ntuples > 0 )
870
874
{
871
875
AttInMetadata * attinmeta ;
876
+ int nestlevel = -1 ;
872
877
Tuplestorestate * tupstore ;
873
878
MemoryContext oldcontext ;
874
879
int row ;
875
880
char * * values ;
876
881
877
882
attinmeta = TupleDescGetAttInMetadata (tupdesc );
878
883
884
+ /* Set GUCs to ensure we read GUC-sensitive data types correctly */
885
+ if (!is_sql_cmd )
886
+ nestlevel = applyRemoteGucs (conn );
887
+
879
888
oldcontext = MemoryContextSwitchTo (
880
889
rsinfo -> econtext -> ecxt_per_query_memory );
881
890
tupstore = tuplestore_begin_heap (true, false, work_mem );
@@ -912,6 +921,9 @@ materializeResult(FunctionCallInfo fcinfo, PGresult *res)
912
921
tuplestore_puttuple (tupstore , tuple );
913
922
}
914
923
924
+ /* clean up GUC settings, if we changed any */
925
+ restoreLocalGucs (nestlevel );
926
+
915
927
/* clean up and return the tuplestore */
916
928
tuplestore_donestoring (tupstore );
917
929
}
@@ -1045,6 +1057,7 @@ static PGresult *
1045
1057
storeQueryResult (storeInfo * sinfo , PGconn * conn , const char * sql )
1046
1058
{
1047
1059
bool first = true;
1060
+ int nestlevel = -1 ;
1048
1061
PGresult * res ;
1049
1062
1050
1063
if (!PQsendQuery (conn , sql ))
@@ -1064,6 +1077,15 @@ storeQueryResult(storeInfo *sinfo, PGconn *conn, const char *sql)
1064
1077
if (PQresultStatus (sinfo -> cur_res ) == PGRES_SINGLE_TUPLE )
1065
1078
{
1066
1079
/* got one row from possibly-bigger resultset */
1080
+
1081
+ /*
1082
+ * Set GUCs to ensure we read GUC-sensitive data types correctly.
1083
+ * We shouldn't do this until we have a row in hand, to ensure
1084
+ * libpq has seen any earlier ParameterStatus protocol messages.
1085
+ */
1086
+ if (first && nestlevel < 0 )
1087
+ nestlevel = applyRemoteGucs (conn );
1088
+
1067
1089
storeRow (sinfo , sinfo -> cur_res , first );
1068
1090
1069
1091
PQclear (sinfo -> cur_res );
@@ -1084,6 +1106,9 @@ storeQueryResult(storeInfo *sinfo, PGconn *conn, const char *sql)
1084
1106
}
1085
1107
}
1086
1108
1109
+ /* clean up GUC settings, if we changed any */
1110
+ restoreLocalGucs (nestlevel );
1111
+
1087
1112
/* return last_res */
1088
1113
res = sinfo -> last_res ;
1089
1114
sinfo -> last_res = NULL ;
@@ -2765,3 +2790,73 @@ validate_pkattnums(Relation rel,
2765
2790
errmsg ("invalid attribute number %d" , pkattnum )));
2766
2791
}
2767
2792
}
2793
+
2794
+ /*
2795
+ * Copy the remote session's values of GUCs that affect datatype I/O
2796
+ * and apply them locally in a new GUC nesting level. Returns the new
2797
+ * nestlevel (which is needed by restoreLocalGucs to undo the settings),
2798
+ * or -1 if no new nestlevel was needed.
2799
+ *
2800
+ * We use the equivalent of a function SET option to allow the settings to
2801
+ * persist only until the caller calls restoreLocalGucs. If an error is
2802
+ * thrown in between, guc.c will take care of undoing the settings.
2803
+ */
2804
+ static int
2805
+ applyRemoteGucs (PGconn * conn )
2806
+ {
2807
+ static const char * const GUCsAffectingIO [] = {
2808
+ "DateStyle" ,
2809
+ "IntervalStyle"
2810
+ };
2811
+
2812
+ int nestlevel = -1 ;
2813
+ int i ;
2814
+
2815
+ for (i = 0 ; i < lengthof (GUCsAffectingIO ); i ++ )
2816
+ {
2817
+ const char * gucName = GUCsAffectingIO [i ];
2818
+ const char * remoteVal = PQparameterStatus (conn , gucName );
2819
+ const char * localVal ;
2820
+
2821
+ /*
2822
+ * If the remote server is pre-8.4, it won't have IntervalStyle, but
2823
+ * that's okay because its output format won't be ambiguous. So just
2824
+ * skip the GUC if we don't get a value for it. (We might eventually
2825
+ * need more complicated logic with remote-version checks here.)
2826
+ */
2827
+ if (remoteVal == NULL )
2828
+ continue ;
2829
+
2830
+ /*
2831
+ * Avoid GUC-setting overhead if the remote and local GUCs already
2832
+ * have the same value.
2833
+ */
2834
+ localVal = GetConfigOption (gucName , false, false);
2835
+ Assert (localVal != NULL );
2836
+
2837
+ if (strcmp (remoteVal , localVal ) == 0 )
2838
+ continue ;
2839
+
2840
+ /* Create new GUC nest level if we didn't already */
2841
+ if (nestlevel < 0 )
2842
+ nestlevel = NewGUCNestLevel ();
2843
+
2844
+ /* Apply the option (this will throw error on failure) */
2845
+ (void ) set_config_option (gucName , remoteVal ,
2846
+ PGC_USERSET , PGC_S_SESSION ,
2847
+ GUC_ACTION_SAVE , true, 0 );
2848
+ }
2849
+
2850
+ return nestlevel ;
2851
+ }
2852
+
2853
+ /*
2854
+ * Restore local GUCs after they have been overlaid with remote settings.
2855
+ */
2856
+ static void
2857
+ restoreLocalGucs (int nestlevel )
2858
+ {
2859
+ /* Do nothing if no new nestlevel was created */
2860
+ if (nestlevel > 0 )
2861
+ AtEOXact_GUC (true, nestlevel );
2862
+ }
0 commit comments