@@ -67,6 +67,7 @@ typedef struct remoteConn
67
67
* Internal declarations
68
68
*/
69
69
static Datum dblink_record_internal (FunctionCallInfo fcinfo , bool is_async );
70
+ static void prepTuplestoreResult (FunctionCallInfo fcinfo );
70
71
static void materializeResult (FunctionCallInfo fcinfo , PGresult * res );
71
72
static remoteConn * getConnectionByName (const char * name );
72
73
static HTAB * createConnHash (void );
@@ -495,7 +496,6 @@ PG_FUNCTION_INFO_V1(dblink_fetch);
495
496
Datum
496
497
dblink_fetch (PG_FUNCTION_ARGS )
497
498
{
498
- ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
499
499
PGresult * res = NULL ;
500
500
char * conname = NULL ;
501
501
remoteConn * rconn = NULL ;
@@ -505,6 +505,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
505
505
int howmany = 0 ;
506
506
bool fail = true; /* default to backward compatible */
507
507
508
+ prepTuplestoreResult (fcinfo );
509
+
508
510
DBLINK_INIT ;
509
511
510
512
if (PG_NARGS () == 4 )
@@ -551,11 +553,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
551
553
if (!conn )
552
554
DBLINK_CONN_NOT_AVAIL ;
553
555
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
-
559
556
initStringInfo (& buf );
560
557
appendStringInfo (& buf , "FETCH %d FROM %s" , howmany , curname );
561
558
@@ -632,7 +629,6 @@ dblink_get_result(PG_FUNCTION_ARGS)
632
629
static Datum
633
630
dblink_record_internal (FunctionCallInfo fcinfo , bool is_async )
634
631
{
635
- ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
636
632
char * msg ;
637
633
PGresult * res = NULL ;
638
634
PGconn * conn = NULL ;
@@ -643,16 +639,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
643
639
bool fail = true; /* default to backward compatible */
644
640
bool freeconn = false;
645
641
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 );
656
643
657
644
DBLINK_INIT ;
658
645
@@ -712,11 +699,6 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
712
699
if (!conn )
713
700
DBLINK_CONN_NOT_AVAIL ;
714
701
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
-
720
702
/* synchronous query, or async result retrieval */
721
703
if (!is_async )
722
704
res = PQexec (conn , sql );
@@ -745,14 +727,45 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
745
727
}
746
728
747
729
/*
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.
750
762
*/
751
763
static void
752
764
materializeResult (FunctionCallInfo fcinfo , PGresult * res )
753
765
{
754
766
ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
755
767
768
+ /* prepTuplestoreResult must have been called previously */
756
769
Assert (rsinfo -> returnMode == SFRM_Materialize );
757
770
758
771
PG_TRY ();
@@ -1004,85 +1017,97 @@ PG_FUNCTION_INFO_V1(dblink_exec);
1004
1017
Datum
1005
1018
dblink_exec (PG_FUNCTION_ARGS )
1006
1019
{
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;
1017
1023
1018
1024
DBLINK_INIT ;
1019
1025
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 ();
1028
1027
{
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 )
1031
1059
{
1060
+ /* must be single text argument */
1032
1061
conn = pconn -> conn ;
1033
1062
sql = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
1034
- fail = PG_GETARG_BOOL (1 );
1035
1063
}
1036
1064
else
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" );
1051
1067
1052
- if (!conn )
1053
- DBLINK_CONN_NOT_AVAIL ;
1068
+ if (!conn )
1069
+ DBLINK_CONN_NOT_AVAIL ;
1054
1070
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 );
1061
1077
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
+ }
1076
1100
}
1077
- else
1101
+ PG_CATCH ();
1078
1102
{
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 ( );
1083
1107
}
1108
+ PG_END_TRY ();
1084
1109
1085
- /* if needed, close the connection to the database and cleanup */
1110
+ /* if needed, close the connection to the database */
1086
1111
if (freeconn )
1087
1112
PQfinish (conn );
1088
1113
@@ -1503,13 +1528,15 @@ dblink_get_notify(PG_FUNCTION_ARGS)
1503
1528
MemoryContext per_query_ctx ;
1504
1529
MemoryContext oldcontext ;
1505
1530
1531
+ prepTuplestoreResult (fcinfo );
1532
+
1506
1533
DBLINK_INIT ;
1507
1534
if (PG_NARGS () == 1 )
1508
1535
DBLINK_GET_NAMED_CONN ;
1509
1536
else
1510
1537
conn = pconn -> conn ;
1511
1538
1512
- /* create the tuplestore */
1539
+ /* create the tuplestore in per-query memory */
1513
1540
per_query_ctx = rsinfo -> econtext -> ecxt_per_query_memory ;
1514
1541
oldcontext = MemoryContextSwitchTo (per_query_ctx );
1515
1542
@@ -1522,7 +1549,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
1522
1549
TEXTOID , -1 , 0 );
1523
1550
1524
1551
tupstore = tuplestore_begin_heap (true, false, work_mem );
1525
- rsinfo -> returnMode = SFRM_Materialize ;
1526
1552
rsinfo -> setResult = tupstore ;
1527
1553
rsinfo -> setDesc = tupdesc ;
1528
1554
0 commit comments