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