@@ -81,6 +81,7 @@ typedef struct remoteConn
81
81
* Internal declarations
82
82
*/
83
83
static Datum dblink_record_internal (FunctionCallInfo fcinfo ,bool is_async );
84
+ static void prepTuplestoreResult (FunctionCallInfo fcinfo );
84
85
static void materializeResult (FunctionCallInfo fcinfo ,PGresult * res );
85
86
static remoteConn * getConnectionByName (const char * name );
86
87
static HTAB * createConnHash (void );
@@ -509,7 +510,6 @@ PG_FUNCTION_INFO_V1(dblink_fetch);
509
510
Datum
510
511
dblink_fetch (PG_FUNCTION_ARGS )
511
512
{
512
- ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
513
513
PGresult * res = NULL ;
514
514
char * conname = NULL ;
515
515
remoteConn * rconn = NULL ;
@@ -519,6 +519,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
519
519
int howmany = 0 ;
520
520
bool fail = true;/* default to backward compatible */
521
521
522
+ prepTuplestoreResult (fcinfo );
523
+
522
524
DBLINK_INIT ;
523
525
524
526
if (PG_NARGS ()== 4 )
@@ -565,11 +567,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
565
567
if (!conn )
566
568
DBLINK_CONN_NOT_AVAIL ;
567
569
568
- /* let the caller know we're sending back a tuplestore */
569
- rsinfo -> returnMode = SFRM_Materialize ;
570
- rsinfo -> setResult = NULL ;
571
- rsinfo -> setDesc = NULL ;
572
-
573
570
initStringInfo (& buf );
574
571
appendStringInfo (& buf ,"FETCH %d FROM %s" ,howmany ,curname );
575
572
@@ -646,7 +643,6 @@ dblink_get_result(PG_FUNCTION_ARGS)
646
643
static Datum
647
644
dblink_record_internal (FunctionCallInfo fcinfo ,bool is_async )
648
645
{
649
- ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
650
646
char * msg ;
651
647
PGresult * res = NULL ;
652
648
PGconn * conn = NULL ;
@@ -657,16 +653,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
657
653
bool fail = true;/* default to backward compatible */
658
654
bool freeconn = false;
659
655
660
- /* check to see if caller supports us returning a tuplestore */
661
- if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
662
- ereport (ERROR ,
663
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
664
- errmsg ("set-valued function called in context that cannot accept a set" )));
665
- if (!(rsinfo -> allowedModes & SFRM_Materialize ))
666
- ereport (ERROR ,
667
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
668
- errmsg ("materialize mode required, but it is not " \
669
- "allowed in this context" )));
656
+ prepTuplestoreResult (fcinfo );
670
657
671
658
DBLINK_INIT ;
672
659
@@ -726,11 +713,6 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
726
713
if (!conn )
727
714
DBLINK_CONN_NOT_AVAIL ;
728
715
729
- /* let the caller know we're sending back a tuplestore */
730
- rsinfo -> returnMode = SFRM_Materialize ;
731
- rsinfo -> setResult = NULL ;
732
- rsinfo -> setDesc = NULL ;
733
-
734
716
/* synchronous query, or async result retrieval */
735
717
if (!is_async )
736
718
res = PQexec (conn ,sql );
@@ -759,14 +741,45 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
759
741
}
760
742
761
743
/*
762
- * Materialize the PGresult to return them as the function result.
763
- * The res will be released in this function.
744
+ * Verify function caller can handle a tuplestore result, and set up for that.
745
+ *
746
+ * Note: if the caller returns without actually creating a tuplestore, the
747
+ * executor will treat the function result as an empty set.
748
+ */
749
+ static void
750
+ prepTuplestoreResult (FunctionCallInfo fcinfo )
751
+ {
752
+ ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
753
+
754
+ /* check to see if query supports us returning a tuplestore */
755
+ if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
756
+ ereport (ERROR ,
757
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
758
+ errmsg ("set-valued function called in context that cannot accept a set" )));
759
+ if (!(rsinfo -> allowedModes & SFRM_Materialize ))
760
+ ereport (ERROR ,
761
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
762
+ errmsg ("materialize mode required, but it is not allowed in this context" )));
763
+
764
+ /* let the executor know we're sending back a tuplestore */
765
+ rsinfo -> returnMode = SFRM_Materialize ;
766
+
767
+ /* caller must fill these to return a non-empty result */
768
+ rsinfo -> setResult = NULL ;
769
+ rsinfo -> setDesc = NULL ;
770
+ }
771
+
772
+ /*
773
+ * Copy the contents of the PGresult into a tuplestore to be returned
774
+ * as the result of the current function.
775
+ * The PGresult will be released in this function.
764
776
*/
765
777
static void
766
778
materializeResult (FunctionCallInfo fcinfo ,PGresult * res )
767
779
{
768
780
ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
769
781
782
+ /* prepTuplestoreResult must have been called previously */
770
783
Assert (rsinfo -> returnMode == SFRM_Materialize );
771
784
772
785
PG_TRY ();
@@ -1018,85 +1031,97 @@ PG_FUNCTION_INFO_V1(dblink_exec);
1018
1031
Datum
1019
1032
dblink_exec (PG_FUNCTION_ARGS )
1020
1033
{
1021
- char * msg ;
1022
- PGresult * res = NULL ;
1023
- text * sql_cmd_status = NULL ;
1024
- PGconn * conn = NULL ;
1025
- char * connstr = NULL ;
1026
- char * sql = NULL ;
1027
- char * conname = NULL ;
1028
- remoteConn * rconn = NULL ;
1029
- bool freeconn = false;
1030
- bool fail = true;/* default to backward compatible behavior */
1034
+ text * volatile sql_cmd_status = NULL ;
1035
+ PGconn * volatile conn = NULL ;
1036
+ volatile bool freeconn = false;
1031
1037
1032
1038
DBLINK_INIT ;
1033
1039
1034
- if (PG_NARGS ()== 3 )
1035
- {
1036
- /* must be text,text,bool */
1037
- DBLINK_GET_CONN ;
1038
- sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1039
- fail = PG_GETARG_BOOL (2 );
1040
- }
1041
- else if (PG_NARGS ()== 2 )
1040
+ PG_TRY ();
1042
1041
{
1043
- /* might be text,text or text,bool */
1044
- if (get_fn_expr_argtype (fcinfo -> flinfo ,1 )== BOOLOID )
1042
+ char * msg ;
1043
+ PGresult * res = NULL ;
1044
+ char * connstr = NULL ;
1045
+ char * sql = NULL ;
1046
+ char * conname = NULL ;
1047
+ remoteConn * rconn = NULL ;
1048
+ bool fail = true;/* default to backward compatible behavior */
1049
+
1050
+ if (PG_NARGS ()== 3 )
1051
+ {
1052
+ /* must be text,text,bool */
1053
+ DBLINK_GET_CONN ;
1054
+ sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1055
+ fail = PG_GETARG_BOOL (2 );
1056
+ }
1057
+ else if (PG_NARGS ()== 2 )
1058
+ {
1059
+ /* might be text,text or text,bool */
1060
+ if (get_fn_expr_argtype (fcinfo -> flinfo ,1 )== BOOLOID )
1061
+ {
1062
+ conn = pconn -> conn ;
1063
+ sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1064
+ fail = PG_GETARG_BOOL (1 );
1065
+ }
1066
+ else
1067
+ {
1068
+ DBLINK_GET_CONN ;
1069
+ sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1070
+ }
1071
+ }
1072
+ else if (PG_NARGS ()== 1 )
1045
1073
{
1074
+ /* must be single text argument */
1046
1075
conn = pconn -> conn ;
1047
1076
sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1048
- fail = PG_GETARG_BOOL (1 );
1049
1077
}
1050
1078
else
1051
- {
1052
- DBLINK_GET_CONN ;
1053
- sql = text_to_cstring (PG_GETARG_TEXT_PP (1 ));
1054
- }
1055
- }
1056
- else if (PG_NARGS ()== 1 )
1057
- {
1058
- /* must be single text argument */
1059
- conn = pconn -> conn ;
1060
- sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1061
- }
1062
- else
1063
- /* shouldn't happen */
1064
- elog (ERROR ,"wrong number of arguments" );
1079
+ /* shouldn't happen */
1080
+ elog (ERROR ,"wrong number of arguments" );
1065
1081
1066
- if (!conn )
1067
- DBLINK_CONN_NOT_AVAIL ;
1082
+ if (!conn )
1083
+ DBLINK_CONN_NOT_AVAIL ;
1068
1084
1069
- res = PQexec (conn ,sql );
1070
- if (!res ||
1071
- (PQresultStatus (res )!= PGRES_COMMAND_OK &&
1072
- PQresultStatus (res )!= PGRES_TUPLES_OK ))
1073
- {
1074
- dblink_res_error (conname ,res ,"could not execute command" ,fail );
1085
+ res = PQexec (conn ,sql );
1086
+ if (!res ||
1087
+ (PQresultStatus (res )!= PGRES_COMMAND_OK &&
1088
+ PQresultStatus (res )!= PGRES_TUPLES_OK ))
1089
+ {
1090
+ dblink_res_error (conname ,res ,"could not execute command" ,fail );
1075
1091
1076
- /*
1077
- * and save a copy of the command status string to return as our
1078
- * result tuple
1079
- */
1080
- sql_cmd_status = cstring_to_text ("ERROR" );
1081
- }
1082
- else if (PQresultStatus (res )== PGRES_COMMAND_OK )
1083
- {
1084
- /*
1085
- * and save a copy of the command status string to return as our
1086
- * result tuple
1087
- */
1088
- sql_cmd_status = cstring_to_text (PQcmdStatus (res ));
1089
- PQclear (res );
1092
+ /*
1093
+ * and save a copy of the command status string to return as our
1094
+ * result tuple
1095
+ */
1096
+ sql_cmd_status = cstring_to_text ("ERROR" );
1097
+ }
1098
+ else if (PQresultStatus (res )== PGRES_COMMAND_OK )
1099
+ {
1100
+ /*
1101
+ * and save a copy of the command status string to return as our
1102
+ * result tuple
1103
+ */
1104
+ sql_cmd_status = cstring_to_text (PQcmdStatus (res ));
1105
+ PQclear (res );
1106
+ }
1107
+ else
1108
+ {
1109
+ PQclear (res );
1110
+ ereport (ERROR ,
1111
+ (errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
1112
+ errmsg ("statement returning results not allowed" )));
1113
+ }
1090
1114
}
1091
- else
1115
+ PG_CATCH ();
1092
1116
{
1093
- PQclear ( res );
1094
- ereport ( ERROR ,
1095
- ( errcode ( ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
1096
- errmsg ( "statement returning results not allowed" )) );
1117
+ /* if needed, close the connection to the database */
1118
+ if ( freeconn )
1119
+ PQfinish ( conn );
1120
+ PG_RE_THROW ( );
1097
1121
}
1122
+ PG_END_TRY ();
1098
1123
1099
- /* if needed, close the connection to the databaseand cleanup */
1124
+ /* if needed, close the connection to the database */
1100
1125
if (freeconn )
1101
1126
PQfinish (conn );
1102
1127
@@ -1517,13 +1542,15 @@ dblink_get_notify(PG_FUNCTION_ARGS)
1517
1542
MemoryContext per_query_ctx ;
1518
1543
MemoryContext oldcontext ;
1519
1544
1545
+ prepTuplestoreResult (fcinfo );
1546
+
1520
1547
DBLINK_INIT ;
1521
1548
if (PG_NARGS ()== 1 )
1522
1549
DBLINK_GET_NAMED_CONN ;
1523
1550
else
1524
1551
conn = pconn -> conn ;
1525
1552
1526
- /* create the tuplestore */
1553
+ /* create the tuplestorein per-query memory */
1527
1554
per_query_ctx = rsinfo -> econtext -> ecxt_per_query_memory ;
1528
1555
oldcontext = MemoryContextSwitchTo (per_query_ctx );
1529
1556
@@ -1536,7 +1563,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
1536
1563
TEXTOID ,-1 ,0 );
1537
1564
1538
1565
tupstore = tuplestore_begin_heap (true, false,work_mem );
1539
- rsinfo -> returnMode = SFRM_Materialize ;
1540
1566
rsinfo -> setResult = tupstore ;
1541
1567
rsinfo -> setDesc = tupdesc ;
1542
1568