mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Eliminate memory leaks in plperl's spi_prepare() function.
Careless use of TopMemoryContext for I/O function data meant that repeated use of spi_prepare and spi_freeplan would leak memory at the session level, as per report from Christian Schröder. In addition, spi_prepare leaked a lot of transient data within the current plperl function's SPI Proc context, which would be a problem for repeated use of spi_prepare within a single plperl function call; and it wasn't terribly careful about releasing permanent allocations in event of an error, either. In passing, clean up some copy-and-pasteos in query-lookup error messages. Alex Hunsaker and Tom Lane
This commit is contained in:
parent
3ae9d4db47
commit
0fe397fa49
@ -183,6 +183,7 @@ typedef struct plperl_call_data
|
|||||||
typedef struct plperl_query_desc
|
typedef struct plperl_query_desc
|
||||||
{
|
{
|
||||||
char qname[24];
|
char qname[24];
|
||||||
|
MemoryContext plan_cxt; /* context holding this struct */
|
||||||
SPIPlanPtr plan;
|
SPIPlanPtr plan;
|
||||||
int nargs;
|
int nargs;
|
||||||
Oid *argtypes;
|
Oid *argtypes;
|
||||||
@ -3200,33 +3201,57 @@ plperl_spi_cursor_close(char *cursor)
|
|||||||
SV *
|
SV *
|
||||||
plperl_spi_prepare(char *query, int argc, SV **argv)
|
plperl_spi_prepare(char *query, int argc, SV **argv)
|
||||||
{
|
{
|
||||||
plperl_query_desc *qdesc;
|
volatile SPIPlanPtr plan = NULL;
|
||||||
plperl_query_entry *hash_entry;
|
volatile MemoryContext plan_cxt = NULL;
|
||||||
bool found;
|
plperl_query_desc *volatile qdesc = NULL;
|
||||||
SPIPlanPtr plan;
|
plperl_query_entry *volatile hash_entry = NULL;
|
||||||
int i;
|
|
||||||
|
|
||||||
MemoryContext oldcontext = CurrentMemoryContext;
|
MemoryContext oldcontext = CurrentMemoryContext;
|
||||||
ResourceOwner oldowner = CurrentResourceOwner;
|
ResourceOwner oldowner = CurrentResourceOwner;
|
||||||
|
MemoryContext work_cxt;
|
||||||
|
bool found;
|
||||||
|
int i;
|
||||||
|
|
||||||
check_spi_usage_allowed();
|
check_spi_usage_allowed();
|
||||||
|
|
||||||
BeginInternalSubTransaction(NULL);
|
BeginInternalSubTransaction(NULL);
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
* Allocate the new querydesc structure
|
|
||||||
************************************************************/
|
|
||||||
qdesc = (plperl_query_desc *) malloc(sizeof(plperl_query_desc));
|
|
||||||
MemSet(qdesc, 0, sizeof(plperl_query_desc));
|
|
||||||
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
|
|
||||||
qdesc->nargs = argc;
|
|
||||||
qdesc->argtypes = (Oid *) malloc(argc * sizeof(Oid));
|
|
||||||
qdesc->arginfuncs = (FmgrInfo *) malloc(argc * sizeof(FmgrInfo));
|
|
||||||
qdesc->argtypioparams = (Oid *) malloc(argc * sizeof(Oid));
|
|
||||||
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Allocate the new querydesc structure
|
||||||
|
*
|
||||||
|
* The qdesc struct, as well as all its subsidiary data, lives in its
|
||||||
|
* plan_cxt. But note that the SPIPlan does not.
|
||||||
|
************************************************************/
|
||||||
|
plan_cxt = AllocSetContextCreate(TopMemoryContext,
|
||||||
|
"PL/Perl spi_prepare query",
|
||||||
|
ALLOCSET_SMALL_MINSIZE,
|
||||||
|
ALLOCSET_SMALL_INITSIZE,
|
||||||
|
ALLOCSET_SMALL_MAXSIZE);
|
||||||
|
MemoryContextSwitchTo(plan_cxt);
|
||||||
|
qdesc = (plperl_query_desc *) palloc0(sizeof(plperl_query_desc));
|
||||||
|
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
|
||||||
|
qdesc->plan_cxt = plan_cxt;
|
||||||
|
qdesc->nargs = argc;
|
||||||
|
qdesc->argtypes = (Oid *) palloc(argc * sizeof(Oid));
|
||||||
|
qdesc->arginfuncs = (FmgrInfo *) palloc(argc * sizeof(FmgrInfo));
|
||||||
|
qdesc->argtypioparams = (Oid *) palloc(argc * sizeof(Oid));
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Do the following work in a short-lived context so that we don't
|
||||||
|
* leak a lot of memory in the PL/Perl function's SPI Proc context.
|
||||||
|
************************************************************/
|
||||||
|
work_cxt = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"PL/Perl spi_prepare workspace",
|
||||||
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
|
MemoryContextSwitchTo(work_cxt);
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Resolve argument type names and then look them up by oid
|
* Resolve argument type names and then look them up by oid
|
||||||
* in the system cache, and remember the required information
|
* in the system cache, and remember the required information
|
||||||
@ -3247,7 +3272,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
|||||||
getTypeInputInfo(typId, &typInput, &typIOParam);
|
getTypeInputInfo(typId, &typInput, &typIOParam);
|
||||||
|
|
||||||
qdesc->argtypes[i] = typId;
|
qdesc->argtypes[i] = typId;
|
||||||
perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
|
fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
|
||||||
qdesc->argtypioparams[i] = typIOParam;
|
qdesc->argtypioparams[i] = typIOParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3271,6 +3296,17 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
|||||||
elog(ERROR, "SPI_keepplan() failed");
|
elog(ERROR, "SPI_keepplan() failed");
|
||||||
qdesc->plan = plan;
|
qdesc->plan = plan;
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Insert a hashtable entry for the plan.
|
||||||
|
************************************************************/
|
||||||
|
hash_entry = hash_search(plperl_active_interp->query_hash,
|
||||||
|
qdesc->qname,
|
||||||
|
HASH_ENTER, &found);
|
||||||
|
hash_entry->query_data = qdesc;
|
||||||
|
|
||||||
|
/* Get rid of workspace */
|
||||||
|
MemoryContextDelete(work_cxt);
|
||||||
|
|
||||||
/* Commit the inner transaction, return to outer xact context */
|
/* Commit the inner transaction, return to outer xact context */
|
||||||
ReleaseCurrentSubTransaction();
|
ReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
@ -3286,16 +3322,21 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
|||||||
{
|
{
|
||||||
ErrorData *edata;
|
ErrorData *edata;
|
||||||
|
|
||||||
free(qdesc->argtypes);
|
|
||||||
free(qdesc->arginfuncs);
|
|
||||||
free(qdesc->argtypioparams);
|
|
||||||
free(qdesc);
|
|
||||||
|
|
||||||
/* Save error info */
|
/* Save error info */
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
edata = CopyErrorData();
|
edata = CopyErrorData();
|
||||||
FlushErrorState();
|
FlushErrorState();
|
||||||
|
|
||||||
|
/* Drop anything we managed to allocate */
|
||||||
|
if (hash_entry)
|
||||||
|
hash_search(plperl_active_interp->query_hash,
|
||||||
|
qdesc->qname,
|
||||||
|
HASH_REMOVE, NULL);
|
||||||
|
if (plan_cxt)
|
||||||
|
MemoryContextDelete(plan_cxt);
|
||||||
|
if (plan)
|
||||||
|
SPI_freeplan(plan);
|
||||||
|
|
||||||
/* Abort the inner transaction */
|
/* Abort the inner transaction */
|
||||||
RollbackAndReleaseCurrentSubTransaction();
|
RollbackAndReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
@ -3317,14 +3358,8 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
|
|||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
/************************************************************
|
/************************************************************
|
||||||
* Insert a hashtable entry for the plan and return
|
* Return the query's hash key to the caller.
|
||||||
* the key to the caller.
|
|
||||||
************************************************************/
|
************************************************************/
|
||||||
|
|
||||||
hash_entry = hash_search(plperl_active_interp->query_hash, qdesc->qname,
|
|
||||||
HASH_ENTER, &found);
|
|
||||||
hash_entry->query_data = qdesc;
|
|
||||||
|
|
||||||
return cstr2sv(qdesc->qname);
|
return cstr2sv(qdesc->qname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3359,16 +3394,14 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
|
|||||||
/************************************************************
|
/************************************************************
|
||||||
* Fetch the saved plan descriptor, see if it's o.k.
|
* Fetch the saved plan descriptor, see if it's o.k.
|
||||||
************************************************************/
|
************************************************************/
|
||||||
|
|
||||||
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
||||||
HASH_FIND, NULL);
|
HASH_FIND, NULL);
|
||||||
if (hash_entry == NULL)
|
if (hash_entry == NULL)
|
||||||
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
|
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
|
||||||
|
|
||||||
qdesc = hash_entry->query_data;
|
qdesc = hash_entry->query_data;
|
||||||
|
|
||||||
if (qdesc == NULL)
|
if (qdesc == NULL)
|
||||||
elog(ERROR, "spi_exec_prepared: panic - plperl query_hash value vanished");
|
elog(ERROR, "spi_exec_prepared: plperl query_hash value vanished");
|
||||||
|
|
||||||
if (qdesc->nargs != argc)
|
if (qdesc->nargs != argc)
|
||||||
elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
|
elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
|
||||||
@ -3500,12 +3533,11 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
|
|||||||
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
||||||
HASH_FIND, NULL);
|
HASH_FIND, NULL);
|
||||||
if (hash_entry == NULL)
|
if (hash_entry == NULL)
|
||||||
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
|
elog(ERROR, "spi_query_prepared: Invalid prepared query passed");
|
||||||
|
|
||||||
qdesc = hash_entry->query_data;
|
qdesc = hash_entry->query_data;
|
||||||
|
|
||||||
if (qdesc == NULL)
|
if (qdesc == NULL)
|
||||||
elog(ERROR, "spi_query_prepared: panic - plperl query_hash value vanished");
|
elog(ERROR, "spi_query_prepared: plperl query_hash value vanished");
|
||||||
|
|
||||||
if (qdesc->nargs != argc)
|
if (qdesc->nargs != argc)
|
||||||
elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
|
elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
|
||||||
@ -3610,12 +3642,12 @@ plperl_spi_freeplan(char *query)
|
|||||||
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
hash_entry = hash_search(plperl_active_interp->query_hash, query,
|
||||||
HASH_FIND, NULL);
|
HASH_FIND, NULL);
|
||||||
if (hash_entry == NULL)
|
if (hash_entry == NULL)
|
||||||
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
|
elog(ERROR, "spi_freeplan: Invalid prepared query passed");
|
||||||
|
|
||||||
qdesc = hash_entry->query_data;
|
qdesc = hash_entry->query_data;
|
||||||
|
|
||||||
if (qdesc == NULL)
|
if (qdesc == NULL)
|
||||||
elog(ERROR, "spi_exec_freeplan: panic - plperl query_hash value vanished");
|
elog(ERROR, "spi_freeplan: plperl query_hash value vanished");
|
||||||
|
plan = qdesc->plan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* free all memory before SPI_freeplan, so if it dies, nothing will be
|
* free all memory before SPI_freeplan, so if it dies, nothing will be
|
||||||
@ -3624,11 +3656,7 @@ plperl_spi_freeplan(char *query)
|
|||||||
hash_search(plperl_active_interp->query_hash, query,
|
hash_search(plperl_active_interp->query_hash, query,
|
||||||
HASH_REMOVE, NULL);
|
HASH_REMOVE, NULL);
|
||||||
|
|
||||||
plan = qdesc->plan;
|
MemoryContextDelete(qdesc->plan_cxt);
|
||||||
free(qdesc->argtypes);
|
|
||||||
free(qdesc->arginfuncs);
|
|
||||||
free(qdesc->argtypioparams);
|
|
||||||
free(qdesc);
|
|
||||||
|
|
||||||
SPI_freeplan(plan);
|
SPI_freeplan(plan);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user