Skip to content

Commit a4d3a50

Browse files
committed
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
1 parent cd7d00a commit a4d3a50

File tree

1 file changed

+72
-44
lines changed

1 file changed

+72
-44
lines changed

src/pl/plperl/plperl.c

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ typedef struct plperl_call_data
184184
typedef struct plperl_query_desc
185185
{
186186
char qname[24];
187+
MemoryContext plan_cxt; /* context holding this struct */
187188
SPIPlanPtr plan;
188189
int nargs;
189190
Oid *argtypes;
@@ -3209,33 +3210,57 @@ plperl_spi_cursor_close(char *cursor)
32093210
SV *
32103211
plperl_spi_prepare(char *query, int argc, SV **argv)
32113212
{
3212-
plperl_query_desc *qdesc;
3213-
plperl_query_entry *hash_entry;
3214-
bool found;
3215-
SPIPlanPtr plan;
3216-
int i;
3217-
3213+
volatile SPIPlanPtr plan = NULL;
3214+
volatile MemoryContext plan_cxt = NULL;
3215+
plperl_query_desc *volatile qdesc = NULL;
3216+
plperl_query_entry *volatile hash_entry = NULL;
32183217
MemoryContext oldcontext = CurrentMemoryContext;
32193218
ResourceOwner oldowner = CurrentResourceOwner;
3219+
MemoryContext work_cxt;
3220+
bool found;
3221+
int i;
32203222

32213223
check_spi_usage_allowed();
32223224

32233225
BeginInternalSubTransaction(NULL);
32243226
MemoryContextSwitchTo(oldcontext);
32253227

3226-
/************************************************************
3227-
* Allocate the new querydesc structure
3228-
************************************************************/
3229-
qdesc = (plperl_query_desc *) malloc(sizeof(plperl_query_desc));
3230-
MemSet(qdesc, 0, sizeof(plperl_query_desc));
3231-
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
3232-
qdesc->nargs = argc;
3233-
qdesc->argtypes = (Oid *) malloc(argc * sizeof(Oid));
3234-
qdesc->arginfuncs = (FmgrInfo *) malloc(argc * sizeof(FmgrInfo));
3235-
qdesc->argtypioparams = (Oid *) malloc(argc * sizeof(Oid));
3236-
32373228
PG_TRY();
32383229
{
3230+
CHECK_FOR_INTERRUPTS();
3231+
3232+
/************************************************************
3233+
* Allocate the new querydesc structure
3234+
*
3235+
* The qdesc struct, as well as all its subsidiary data, lives in its
3236+
* plan_cxt. But note that the SPIPlan does not.
3237+
************************************************************/
3238+
plan_cxt = AllocSetContextCreate(TopMemoryContext,
3239+
"PL/Perl spi_prepare query",
3240+
ALLOCSET_SMALL_MINSIZE,
3241+
ALLOCSET_SMALL_INITSIZE,
3242+
ALLOCSET_SMALL_MAXSIZE);
3243+
MemoryContextSwitchTo(plan_cxt);
3244+
qdesc = (plperl_query_desc *) palloc0(sizeof(plperl_query_desc));
3245+
snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
3246+
qdesc->plan_cxt = plan_cxt;
3247+
qdesc->nargs = argc;
3248+
qdesc->argtypes = (Oid *) palloc(argc * sizeof(Oid));
3249+
qdesc->arginfuncs = (FmgrInfo *) palloc(argc * sizeof(FmgrInfo));
3250+
qdesc->argtypioparams = (Oid *) palloc(argc * sizeof(Oid));
3251+
MemoryContextSwitchTo(oldcontext);
3252+
3253+
/************************************************************
3254+
* Do the following work in a short-lived context so that we don't
3255+
* leak a lot of memory in the PL/Perl function's SPI Proc context.
3256+
************************************************************/
3257+
work_cxt = AllocSetContextCreate(CurrentMemoryContext,
3258+
"PL/Perl spi_prepare workspace",
3259+
ALLOCSET_DEFAULT_MINSIZE,
3260+
ALLOCSET_DEFAULT_INITSIZE,
3261+
ALLOCSET_DEFAULT_MAXSIZE);
3262+
MemoryContextSwitchTo(work_cxt);
3263+
32393264
/************************************************************
32403265
* Resolve argument type names and then look them up by oid
32413266
* in the system cache, and remember the required information
@@ -3256,7 +3281,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
32563281
getTypeInputInfo(typId, &typInput, &typIOParam);
32573282

32583283
qdesc->argtypes[i] = typId;
3259-
perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
3284+
fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
32603285
qdesc->argtypioparams[i] = typIOParam;
32613286
}
32623287

@@ -3280,6 +3305,17 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
32803305
elog(ERROR, "SPI_keepplan() failed");
32813306
qdesc->plan = plan;
32823307

3308+
/************************************************************
3309+
* Insert a hashtable entry for the plan.
3310+
************************************************************/
3311+
hash_entry = hash_search(plperl_active_interp->query_hash,
3312+
qdesc->qname,
3313+
HASH_ENTER, &found);
3314+
hash_entry->query_data = qdesc;
3315+
3316+
/* Get rid of workspace */
3317+
MemoryContextDelete(work_cxt);
3318+
32833319
/* Commit the inner transaction, return to outer xact context */
32843320
ReleaseCurrentSubTransaction();
32853321
MemoryContextSwitchTo(oldcontext);
@@ -3295,16 +3331,21 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
32953331
{
32963332
ErrorData *edata;
32973333

3298-
free(qdesc->argtypes);
3299-
free(qdesc->arginfuncs);
3300-
free(qdesc->argtypioparams);
3301-
free(qdesc);
3302-
33033334
/* Save error info */
33043335
MemoryContextSwitchTo(oldcontext);
33053336
edata = CopyErrorData();
33063337
FlushErrorState();
33073338

3339+
/* Drop anything we managed to allocate */
3340+
if (hash_entry)
3341+
hash_search(plperl_active_interp->query_hash,
3342+
qdesc->qname,
3343+
HASH_REMOVE, NULL);
3344+
if (plan_cxt)
3345+
MemoryContextDelete(plan_cxt);
3346+
if (plan)
3347+
SPI_freeplan(plan);
3348+
33083349
/* Abort the inner transaction */
33093350
RollbackAndReleaseCurrentSubTransaction();
33103351
MemoryContextSwitchTo(oldcontext);
@@ -3326,14 +3367,8 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
33263367
PG_END_TRY();
33273368

33283369
/************************************************************
3329-
* Insert a hashtable entry for the plan and return
3330-
* the key to the caller.
3370+
* Return the query's hash key to the caller.
33313371
************************************************************/
3332-
3333-
hash_entry = hash_search(plperl_active_interp->query_hash, qdesc->qname,
3334-
HASH_ENTER, &found);
3335-
hash_entry->query_data = qdesc;
3336-
33373372
return cstr2sv(qdesc->qname);
33383373
}
33393374

@@ -3368,16 +3403,14 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
33683403
/************************************************************
33693404
* Fetch the saved plan descriptor, see if it's o.k.
33703405
************************************************************/
3371-
33723406
hash_entry = hash_search(plperl_active_interp->query_hash, query,
33733407
HASH_FIND, NULL);
33743408
if (hash_entry == NULL)
33753409
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
33763410

33773411
qdesc = hash_entry->query_data;
3378-
33793412
if (qdesc == NULL)
3380-
elog(ERROR, "spi_exec_prepared: panic - plperl query_hash value vanished");
3413+
elog(ERROR, "spi_exec_prepared: plperl query_hash value vanished");
33813414

33823415
if (qdesc->nargs != argc)
33833416
elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
@@ -3509,12 +3542,11 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
35093542
hash_entry = hash_search(plperl_active_interp->query_hash, query,
35103543
HASH_FIND, NULL);
35113544
if (hash_entry == NULL)
3512-
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
3545+
elog(ERROR, "spi_query_prepared: Invalid prepared query passed");
35133546

35143547
qdesc = hash_entry->query_data;
3515-
35163548
if (qdesc == NULL)
3517-
elog(ERROR, "spi_query_prepared: panic - plperl query_hash value vanished");
3549+
elog(ERROR, "spi_query_prepared: plperl query_hash value vanished");
35183550

35193551
if (qdesc->nargs != argc)
35203552
elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
@@ -3619,12 +3651,12 @@ plperl_spi_freeplan(char *query)
36193651
hash_entry = hash_search(plperl_active_interp->query_hash, query,
36203652
HASH_FIND, NULL);
36213653
if (hash_entry == NULL)
3622-
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
3654+
elog(ERROR, "spi_freeplan: Invalid prepared query passed");
36233655

36243656
qdesc = hash_entry->query_data;
3625-
36263657
if (qdesc == NULL)
3627-
elog(ERROR, "spi_exec_freeplan: panic - plperl query_hash value vanished");
3658+
elog(ERROR, "spi_freeplan: plperl query_hash value vanished");
3659+
plan = qdesc->plan;
36283660

36293661
/*
36303662
* free all memory before SPI_freeplan, so if it dies, nothing will be
@@ -3633,11 +3665,7 @@ plperl_spi_freeplan(char *query)
36333665
hash_search(plperl_active_interp->query_hash, query,
36343666
HASH_REMOVE, NULL);
36353667

3636-
plan = qdesc->plan;
3637-
free(qdesc->argtypes);
3638-
free(qdesc->arginfuncs);
3639-
free(qdesc->argtypioparams);
3640-
free(qdesc);
3668+
MemoryContextDelete(qdesc->plan_cxt);
36413669

36423670
SPI_freeplan(plan);
36433671
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy