mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Create infrastructure to reliably prevent leakage of PGresults.
Commit232d8caea
fixed a case where postgres_fdw could lose track of a PGresult object, resulting in a process-lifespan memory leak. But I have little faith that there aren't other potential PGresult leakages, now or in future, in the backend modules that use libpq. Therefore, this patch proposes infrastructure that makes all PGresults returned from libpq act as though they are palloc'd in the CurrentMemoryContext (with the option to relocate them to another context later). This should greatly reduce the risk of careless leaks, and it also permits removal of a bunch of code that attempted to prevent such leaks via PG_TRY blocks. This patch adds infrastructure that wraps each PGresult in a "libpqsrv_PGresult" that provides a memory context reset callback to PQclear the PGresult. Code using this abstraction is inherently memory-safe to the same extent as we are accustomed to in most backend code. Furthermore, we add some macros that automatically redirect calls of the libpq functions concerned with PGresults to use this infrastructure, so that almost no source-code changes are needed to wheel this infrastructure into place in all the backend code that uses libpq. Perhaps in future we could create similar infrastructure for PGconn objects, but there seems less need for that. This patch just creates the infrastructure and makes relevant code use it, including reverting232d8caea
in favor of this mechanism. A good deal of follow-on simplification is possible now that we don't have to be so cautious about freeing PGresults, but I'll put that in a separate patch. 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
This commit is contained in:
@ -240,7 +240,6 @@ typedef struct PgFdwDirectModifyState
|
||||
PGresult *result; /* result for query */
|
||||
int num_tuples; /* # of result tuples */
|
||||
int next_tuple; /* index of next one to return */
|
||||
MemoryContextCallback result_cb; /* ensures result will get freed */
|
||||
Relation resultRel; /* relcache entry for the target relation */
|
||||
AttrNumber *attnoMap; /* array of attnums of input user columns */
|
||||
AttrNumber ctidAttno; /* attnum of input ctid column */
|
||||
@ -2671,17 +2670,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
|
||||
dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
|
||||
node->fdw_state = dmstate;
|
||||
|
||||
/*
|
||||
* We use a memory context callback to ensure that the dmstate's PGresult
|
||||
* (if any) will be released, even if the query fails somewhere that's
|
||||
* outside our control. The callback is always armed for the duration of
|
||||
* the query; this relies on PQclear(NULL) being a no-op.
|
||||
*/
|
||||
dmstate->result_cb.func = (MemoryContextCallbackFunction) PQclear;
|
||||
dmstate->result_cb.arg = NULL;
|
||||
MemoryContextRegisterResetCallback(CurrentMemoryContext,
|
||||
&dmstate->result_cb);
|
||||
|
||||
/*
|
||||
* Identify which user to do the remote access as. This should match what
|
||||
* ExecCheckPermissions() does.
|
||||
@ -2829,13 +2817,7 @@ postgresEndDirectModify(ForeignScanState *node)
|
||||
return;
|
||||
|
||||
/* Release PGresult */
|
||||
if (dmstate->result)
|
||||
{
|
||||
PQclear(dmstate->result);
|
||||
dmstate->result = NULL;
|
||||
/* ... and don't forget to disable the callback */
|
||||
dmstate->result_cb.arg = NULL;
|
||||
}
|
||||
PQclear(dmstate->result);
|
||||
|
||||
/* Release remote connection */
|
||||
ReleaseConnection(dmstate->conn);
|
||||
@ -4615,20 +4597,20 @@ execute_dml_stmt(ForeignScanState *node)
|
||||
|
||||
/*
|
||||
* Get the result, and check for success.
|
||||
*
|
||||
* We use a memory context callback to ensure that the PGresult will be
|
||||
* released, even if the query fails somewhere that's outside our control.
|
||||
* The callback is already registered, just need to fill in its arg.
|
||||
*/
|
||||
Assert(dmstate->result == NULL);
|
||||
dmstate->result = pgfdw_get_result(dmstate->conn);
|
||||
dmstate->result_cb.arg = dmstate->result;
|
||||
|
||||
if (PQresultStatus(dmstate->result) !=
|
||||
(dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
|
||||
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, false,
|
||||
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
|
||||
dmstate->query);
|
||||
|
||||
/*
|
||||
* The result potentially needs to survive across multiple executor row
|
||||
* cycles, so move it to the context where the dmstate is.
|
||||
*/
|
||||
dmstate->result = libpqsrv_PGresultSetParent(dmstate->result,
|
||||
GetMemoryChunkContext(dmstate));
|
||||
|
||||
/* Get the number of rows affected. */
|
||||
if (dmstate->has_returning)
|
||||
dmstate->num_tuples = PQntuples(dmstate->result);
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include "foreign/foreign.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "libpq-fe.h"
|
||||
#include "libpq/libpq-be-fe.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/pathnodes.h"
|
||||
#include "utils/relcache.h"
|
||||
|
Reference in New Issue
Block a user