Content-Length: 424406 | pFad | http://github.com/postgres/postgres/commit/2b92dc4eeb51a1b66641718e072b96b63659448c

C5 Fix memory leakage in postgres_fdw's DirectModify code path. · postgres/postgres@2b92dc4 · GitHub
Skip to content

Commit 2b92dc4

Browse files
committed
Fix memory leakage in postgres_fdw's DirectModify code path.
postgres_fdw tries to use PG_TRY blocks to ensure that it will eventually free the PGresult created by the remote modify command. However, it's fundamentally impossible for this scheme to work reliably when there's RETURNING data, because the query could fail in between invocations of postgres_fdw's DirectModify methods. There is at least one instance of exactly this situation in the regression tests, and the ensuing session-lifespan leak is visible under Valgrind. We can improve matters by using a memory context reset callback attached to the ExecutorState context. That ensures that the PGresult will be freed when the ExecutorState context is torn down, even if control never reaches postgresEndDirectModify. I have little faith that there aren't other potential PGresult leakages in the backend modules that use libpq. So I think it'd be a good idea to apply this concept universally by creating infrastructure that attaches a reset callback to every PGresult generated in the backend. However, that seems too invasive for v18 at this point, let alone the back branches. So for the moment, apply this narrow fix that just makes DirectModify safe. I have a patch in the queue for the more general idea, but it will have to wait for v19. Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com> Discussion: https://postgr.es/m/2976982.1748049023@sss.pgh.pa.us Backpatch-through: 13
1 parent ca70ee6 commit 2b92dc4

File tree

1 file changed

+35
-27
lines changed

1 file changed

+35
-27
lines changed

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ typedef struct PgFdwDirectModifyState
239239
PGresult *result; /* result for query */
240240
int num_tuples; /* # of result tuples */
241241
int next_tuple; /* index of next one to return */
242+
MemoryContextCallback result_cb; /* ensures result will get freed */
242243
Relation resultRel; /* relcache entry for the target relation */
243244
AttrNumber *attnoMap; /* array of attnums of input user columns */
244245
AttrNumber ctidAttno; /* attnum of input ctid column */
@@ -2659,6 +2660,17 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
26592660
dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
26602661
node->fdw_state = (void *) dmstate;
26612662

2663+
/*
2664+
* We use a memory context callback to ensure that the dmstate's PGresult
2665+
* (if any) will be released, even if the query fails somewhere that's
2666+
* outside our control. The callback is always armed for the duration of
2667+
* the query; this relies on PQclear(NULL) being a no-op.
2668+
*/
2669+
dmstate->result_cb.func = (MemoryContextCallbackFunction) PQclear;
2670+
dmstate->result_cb.arg = NULL;
2671+
MemoryContextRegisterResetCallback(CurrentMemoryContext,
2672+
&dmstate->result_cb);
2673+
26622674
/*
26632675
* Identify which user to do the remote access as. This should match what
26642676
* ExecCheckPermissions() does.
@@ -2806,7 +2818,13 @@ postgresEndDirectModify(ForeignScanState *node)
28062818
return;
28072819

28082820
/* Release PGresult */
2809-
PQclear(dmstate->result);
2821+
if (dmstate->result)
2822+
{
2823+
PQclear(dmstate->result);
2824+
dmstate->result = NULL;
2825+
/* ... and don't forget to disable the callback */
2826+
dmstate->result_cb.arg = NULL;
2827+
}
28102828

28112829
/* Release remote connection */
28122830
ReleaseConnection(dmstate->conn);
@@ -4575,13 +4593,17 @@ execute_dml_stmt(ForeignScanState *node)
45754593
/*
45764594
* Get the result, and check for success.
45774595
*
4578-
* We don't use a PG_TRY block here, so be careful not to throw error
4579-
* without releasing the PGresult.
4596+
* We use a memory context callback to ensure that the PGresult will be
4597+
* released, even if the query fails somewhere that's outside our control.
4598+
* The callback is already registered, just need to fill in its arg.
45804599
*/
4600+
Assert(dmstate->result == NULL);
45814601
dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
4602+
dmstate->result_cb.arg = dmstate->result;
4603+
45824604
if (PQresultStatus(dmstate->result) !=
45834605
(dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
4584-
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
4606+
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, false,
45854607
dmstate->query);
45864608

45874609
/* Get the number of rows affected. */
@@ -4625,30 +4647,16 @@ get_returning_data(ForeignScanState *node)
46254647
}
46264648
else
46274649
{
4628-
/*
4629-
* On error, be sure to release the PGresult on the way out. Callers
4630-
* do not have PG_TRY blocks to ensure this happens.
4631-
*/
4632-
PG_TRY();
4633-
{
4634-
HeapTuple newtup;
4635-
4636-
newtup = make_tuple_from_result_row(dmstate->result,
4637-
dmstate->next_tuple,
4638-
dmstate->rel,
4639-
dmstate->attinmeta,
4640-
dmstate->retrieved_attrs,
4641-
node,
4642-
dmstate->temp_cxt);
4643-
ExecStoreHeapTuple(newtup, slot, false);
4644-
}
4645-
PG_CATCH();
4646-
{
4647-
PQclear(dmstate->result);
4648-
PG_RE_THROW();
4649-
}
4650-
PG_END_TRY();
4650+
HeapTuple newtup;
46514651

4652+
newtup = make_tuple_from_result_row(dmstate->result,
4653+
dmstate->next_tuple,
4654+
dmstate->rel,
4655+
dmstate->attinmeta,
4656+
dmstate->retrieved_attrs,
4657+
node,
4658+
dmstate->temp_cxt);
4659+
ExecStoreHeapTuple(newtup, slot, false);
46524660
/* Get the updated/deleted tuple. */
46534661
if (dmstate->rel)
46544662
resultSlot = slot;

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgres/postgres/commit/2b92dc4eeb51a1b66641718e072b96b63659448c

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy