@@ -67,6 +67,7 @@ typedef struct remoteConn
6767 * Internal declarations
6868 */
6969static Datum dblink_record_internal (FunctionCallInfo fcinfo ,bool is_async );
70+ static void prepTuplestoreResult (FunctionCallInfo fcinfo );
7071static void materializeResult (FunctionCallInfo fcinfo ,PGresult * res );
7172static remoteConn * getConnectionByName (const char * name );
7273static HTAB * createConnHash (void );
@@ -495,7 +496,6 @@ PG_FUNCTION_INFO_V1(dblink_fetch);
495496Datum
496497dblink_fetch (PG_FUNCTION_ARGS )
497498{
498- ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
499499PGresult * res = NULL ;
500500char * conname = NULL ;
501501remoteConn * rconn = NULL ;
@@ -505,6 +505,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
505505int howmany = 0 ;
506506bool fail = true;/* default to backward compatible */
507507
508+ prepTuplestoreResult (fcinfo );
509+
508510DBLINK_INIT ;
509511
510512if (PG_NARGS ()== 4 )
@@ -551,11 +553,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
551553if (!conn )
552554DBLINK_CONN_NOT_AVAIL ;
553555
554- /* let the caller know we're sending back a tuplestore */
555- rsinfo -> returnMode = SFRM_Materialize ;
556- rsinfo -> setResult = NULL ;
557- rsinfo -> setDesc = NULL ;
558-
559556initStringInfo (& buf );
560557appendStringInfo (& buf ,"FETCH %d FROM %s" ,howmany ,curname );
561558
@@ -632,7 +629,6 @@ dblink_get_result(PG_FUNCTION_ARGS)
632629static Datum
633630dblink_record_internal (FunctionCallInfo fcinfo ,bool is_async )
634631{
635- ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
636632char * msg ;
637633PGresult * res = NULL ;
638634PGconn * conn = NULL ;
@@ -643,16 +639,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
643639bool fail = true;/* default to backward compatible */
644640bool freeconn = false;
645641
646- /* check to see if caller supports us returning a tuplestore */
647- if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
648- ereport (ERROR ,
649- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
650- errmsg ("set-valued function called in context that cannot accept a set" )));
651- if (!(rsinfo -> allowedModes & SFRM_Materialize ))
652- ereport (ERROR ,
653- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
654- errmsg ("materialize mode required, but it is not " \
655- "allowed in this context" )));
642+ prepTuplestoreResult (fcinfo );
656643
657644DBLINK_INIT ;
658645
@@ -712,11 +699,6 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
712699if (!conn )
713700DBLINK_CONN_NOT_AVAIL ;
714701
715- /* let the caller know we're sending back a tuplestore */
716- rsinfo -> returnMode = SFRM_Materialize ;
717- rsinfo -> setResult = NULL ;
718- rsinfo -> setDesc = NULL ;
719-
720702/* synchronous query, or async result retrieval */
721703if (!is_async )
722704res = PQexec (conn ,sql );
@@ -745,14 +727,45 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
745727}
746728
747729/*
748- * Materialize the PGresult to return them as the function result.
749- * The res will be released in this function.
730+ * Verify function caller can handle a tuplestore result, and set up for that.
731+ *
732+ * Note: if the caller returns without actually creating a tuplestore, the
733+ * executor will treat the function result as an empty set.
734+ */
735+ static void
736+ prepTuplestoreResult (FunctionCallInfo fcinfo )
737+ {
738+ ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
739+
740+ /* check to see if query supports us returning a tuplestore */
741+ if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
742+ ereport (ERROR ,
743+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
744+ errmsg ("set-valued function called in context that cannot accept a set" )));
745+ if (!(rsinfo -> allowedModes & SFRM_Materialize ))
746+ ereport (ERROR ,
747+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
748+ errmsg ("materialize mode required, but it is not allowed in this context" )));
749+
750+ /* let the executor know we're sending back a tuplestore */
751+ rsinfo -> returnMode = SFRM_Materialize ;
752+
753+ /* caller must fill these to return a non-empty result */
754+ rsinfo -> setResult = NULL ;
755+ rsinfo -> setDesc = NULL ;
756+ }
757+
758+ /*
759+ * Copy the contents of the PGresult into a tuplestore to be returned
760+ * as the result of the current function.
761+ * The PGresult will be released in this function.
750762 */
751763static void
752764materializeResult (FunctionCallInfo fcinfo ,PGresult * res )
753765{
754766ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
755767
768+ /* prepTuplestoreResult must have been called previously */
756769Assert (rsinfo -> returnMode == SFRM_Materialize );
757770
758771PG_TRY ();
@@ -1004,85 +1017,97 @@ PG_FUNCTION_INFO_V1(dblink_exec);
10041017Datum
10051018dblink_exec (PG_FUNCTION_ARGS )
10061019{
1007- char * msg ;
1008- PGresult * res = NULL ;
1009- text * sql_cmd_status = NULL ;
1010- PGconn * conn = NULL ;
1011- char * connstr = NULL ;
1012- char * sql = NULL ;
1013- char * conname = NULL ;
1014- remoteConn * rconn = NULL ;
1015- bool freeconn = false;
1016- bool fail = true;/* default to backward compatible behavior */
1020+ text * volatile sql_cmd_status = NULL ;
1021+ PGconn * volatile conn = NULL ;
1022+ volatile bool freeconn = false;
10171023
10181024DBLINK_INIT ;
10191025
1020- if (PG_NARGS ()== 3 )
1021- {
1022- /* must be text,text,bool */
1023- DBLINK_GET_CONN ;
1024- sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1025- fail = PG_GETARG_BOOL (2 );
1026- }
1027- else if (PG_NARGS ()== 2 )
1026+ PG_TRY ();
10281027{
1029- /* might be text,text or text,bool */
1030- if (get_fn_expr_argtype (fcinfo -> flinfo ,1 )== BOOLOID )
1028+ char * msg ;
1029+ PGresult * res = NULL ;
1030+ char * connstr = NULL ;
1031+ char * sql = NULL ;
1032+ char * conname = NULL ;
1033+ remoteConn * rconn = NULL ;
1034+ bool fail = true;/* default to backward compatible behavior */
1035+
1036+ if (PG_NARGS ()== 3 )
1037+ {
1038+ /* must be text,text,bool */
1039+ DBLINK_GET_CONN ;
1040+ sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1041+ fail = PG_GETARG_BOOL (2 );
1042+ }
1043+ else if (PG_NARGS ()== 2 )
1044+ {
1045+ /* might be text,text or text,bool */
1046+ if (get_fn_expr_argtype (fcinfo -> flinfo ,1 )== BOOLOID )
1047+ {
1048+ conn = pconn -> conn ;
1049+ sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1050+ fail = PG_GETARG_BOOL (1 );
1051+ }
1052+ else
1053+ {
1054+ DBLINK_GET_CONN ;
1055+ sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1056+ }
1057+ }
1058+ else if (PG_NARGS ()== 1 )
10311059{
1060+ /* must be single text argument */
10321061conn = pconn -> conn ;
10331062sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1034- fail = PG_GETARG_BOOL (1 );
10351063}
10361064else
1037- {
1038- DBLINK_GET_CONN ;
1039- sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1040- }
1041- }
1042- else if (PG_NARGS ()== 1 )
1043- {
1044- /* must be single text argument */
1045- conn = pconn -> conn ;
1046- sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1047- }
1048- else
1049- /* shouldn't happen */
1050- elog (ERROR ,"wrong number of arguments" );
1065+ /* shouldn't happen */
1066+ elog (ERROR ,"wrong number of arguments" );
10511067
1052- if (!conn )
1053- DBLINK_CONN_NOT_AVAIL ;
1068+ if (!conn )
1069+ DBLINK_CONN_NOT_AVAIL ;
10541070
1055- res = PQexec (conn ,sql );
1056- if (!res ||
1057- (PQresultStatus (res )!= PGRES_COMMAND_OK &&
1058- PQresultStatus (res )!= PGRES_TUPLES_OK ))
1059- {
1060- dblink_res_error (conname ,res ,"could not execute command" ,fail );
1071+ res = PQexec (conn ,sql );
1072+ if (!res ||
1073+ (PQresultStatus (res )!= PGRES_COMMAND_OK &&
1074+ PQresultStatus (res )!= PGRES_TUPLES_OK ))
1075+ {
1076+ dblink_res_error (conname ,res ,"could not execute command" ,fail );
10611077
1062- /*
1063- * and save a copy of the command status string to return as our
1064- * result tuple
1065- */
1066- sql_cmd_status = cstring_to_text ("ERROR" );
1067- }
1068- else if (PQresultStatus (res )== PGRES_COMMAND_OK )
1069- {
1070- /*
1071- * and save a copy of the command status string to return as our
1072- * result tuple
1073- */
1074- sql_cmd_status = cstring_to_text (PQcmdStatus (res ));
1075- PQclear (res );
1078+ /*
1079+ * and save a copy of the command status string to return as our
1080+ * result tuple
1081+ */
1082+ sql_cmd_status = cstring_to_text ("ERROR" );
1083+ }
1084+ else if (PQresultStatus (res )== PGRES_COMMAND_OK )
1085+ {
1086+ /*
1087+ * and save a copy of the command status string to return as our
1088+ * result tuple
1089+ */
1090+ sql_cmd_status = cstring_to_text (PQcmdStatus (res ));
1091+ PQclear (res );
1092+ }
1093+ else
1094+ {
1095+ PQclear (res );
1096+ ereport (ERROR ,
1097+ (errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
1098+ errmsg ("statement returning results not allowed" )));
1099+ }
10761100}
1077- else
1101+ PG_CATCH ();
10781102{
1079- PQclear ( res );
1080- ereport ( ERROR ,
1081- ( errcode ( ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
1082- errmsg ( "statement returning results not allowed" )) );
1103+ /* if needed, close the connection to the database */
1104+ if ( freeconn )
1105+ PQfinish ( conn );
1106+ PG_RE_THROW ( );
10831107}
1108+ PG_END_TRY ();
10841109
1085- /* if needed, close the connection to the databaseand cleanup */
1110+ /* if needed, close the connection to the database */
10861111if (freeconn )
10871112PQfinish (conn );
10881113
@@ -1503,13 +1528,15 @@ dblink_get_notify(PG_FUNCTION_ARGS)
15031528MemoryContext per_query_ctx ;
15041529MemoryContext oldcontext ;
15051530
1531+ prepTuplestoreResult (fcinfo );
1532+
15061533DBLINK_INIT ;
15071534if (PG_NARGS ()== 1 )
15081535DBLINK_GET_NAMED_CONN ;
15091536else
15101537conn = pconn -> conn ;
15111538
1512- /* create the tuplestore */
1539+ /* create the tuplestorein per-query memory */
15131540per_query_ctx = rsinfo -> econtext -> ecxt_per_query_memory ;
15141541oldcontext = MemoryContextSwitchTo (per_query_ctx );
15151542
@@ -1522,7 +1549,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
15221549TEXTOID ,-1 ,0 );
15231550
15241551tupstore = tuplestore_begin_heap (true, false,work_mem );
1525- rsinfo -> returnMode = SFRM_Materialize ;
15261552rsinfo -> setResult = tupstore ;
15271553rsinfo -> setDesc = tupdesc ;
15281554