Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d843ed2

Browse files
committed
Fix a couple of contrib/dblink bugs.
dblink_exec leaked temporary database connections if any error occurred after connection setup, for example SELECT dblink_exec('...connect string...', 'select 1/0'); Add a PG_TRY block to ensure PQfinish gets done when it is needed. (dblink_record_internal is on the hairy edge of needing similar treatment, but seems not to be actively broken at the moment.) Also, in 9.0 and up, only one of the three functions using tuplestore return mode was properly checking that the query context would allow a tuplestore result. Noted while reviewing dblink patch. Back-patch to all supported branches.
1 parent 5e86c61 commit d843ed2

File tree

1 file changed

+115
-89
lines changed

1 file changed

+115
-89
lines changed

contrib/dblink/dblink.c

Lines changed: 115 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct remoteConn
6767
* Internal declarations
6868
*/
6969
static Datum dblink_record_internal(FunctionCallInfo fcinfo, bool is_async);
70+
static void prepTuplestoreResult(FunctionCallInfo fcinfo);
7071
static void materializeResult(FunctionCallInfo fcinfo, PGresult *res);
7172
static remoteConn *getConnectionByName(const char *name);
7273
static HTAB *createConnHash(void);
@@ -495,7 +496,6 @@ PG_FUNCTION_INFO_V1(dblink_fetch);
495496
Datum
496497
dblink_fetch(PG_FUNCTION_ARGS)
497498
{
498-
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
499499
PGresult *res = NULL;
500500
char *conname = NULL;
501501
remoteConn *rconn = NULL;
@@ -505,6 +505,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
505505
int howmany = 0;
506506
bool fail = true; /* default to backward compatible */
507507

508+
prepTuplestoreResult(fcinfo);
509+
508510
DBLINK_INIT;
509511

510512
if (PG_NARGS() == 4)
@@ -551,11 +553,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
551553
if (!conn)
552554
DBLINK_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-
559556
initStringInfo(&buf);
560557
appendStringInfo(&buf, "FETCH %d FROM %s", howmany, curname);
561558

@@ -632,7 +629,6 @@ dblink_get_result(PG_FUNCTION_ARGS)
632629
static Datum
633630
dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
634631
{
635-
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
636632
char *msg;
637633
PGresult *res = NULL;
638634
PGconn *conn = NULL;
@@ -643,16 +639,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
643639
bool fail = true; /* default to backward compatible */
644640
bool 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

657644
DBLINK_INIT;
658645

@@ -712,11 +699,6 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async)
712699
if (!conn)
713700
DBLINK_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 */
721703
if (!is_async)
722704
res = 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
*/
751763
static void
752764
materializeResult(FunctionCallInfo fcinfo, PGresult *res)
753765
{
754766
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
755767

768+
/* prepTuplestoreResult must have been called previously */
756769
Assert(rsinfo->returnMode == SFRM_Materialize);
757770

758771
PG_TRY();
@@ -1004,85 +1017,97 @@ PG_FUNCTION_INFO_V1(dblink_exec);
10041017
Datum
10051018
dblink_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

10181024
DBLINK_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 */
10321061
conn = pconn->conn;
10331062
sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
1034-
fail = PG_GETARG_BOOL(1);
10351063
}
10361064
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");
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 database and cleanup */
1110+
/* if needed, close the connection to the database */
10861111
if (freeconn)
10871112
PQfinish(conn);
10881113

@@ -1503,13 +1528,15 @@ dblink_get_notify(PG_FUNCTION_ARGS)
15031528
MemoryContext per_query_ctx;
15041529
MemoryContext oldcontext;
15051530

1531+
prepTuplestoreResult(fcinfo);
1532+
15061533
DBLINK_INIT;
15071534
if (PG_NARGS() == 1)
15081535
DBLINK_GET_NAMED_CONN;
15091536
else
15101537
conn = pconn->conn;
15111538

1512-
/* create the tuplestore */
1539+
/* create the tuplestore in per-query memory */
15131540
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
15141541
oldcontext = MemoryContextSwitchTo(per_query_ctx);
15151542

@@ -1522,7 +1549,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
15221549
TEXTOID, -1, 0);
15231550

15241551
tupstore = tuplestore_begin_heap(true, false, work_mem);
1525-
rsinfo->returnMode = SFRM_Materialize;
15261552
rsinfo->setResult = tupstore;
15271553
rsinfo->setDesc = tupdesc;
15281554

0 commit comments

Comments
 (0)