Skip to content

Commit 8c75ad4

Browse files
committed
Fix memory leaks in PL/Python.
Previously, plpython was in the habit of allocating a lot of stuff in TopMemoryContext, and it was very slipshod about making sure that stuff got cleaned up; in particular, use of TopMemoryContext as fn_mcxt for function calls represents an unfixable leak, since we generally don't know what the called function might have allocated in fn_mcxt. This results in session-lifespan leakage in certain usage scenarios, as for example in a case reported by Ed Behn back in July. To fix, get rid of all the retail allocations in TopMemoryContext. All long-lived allocations are now made in sub-contexts that are associated with specific objects (either pl/python procedures, or Python-visible objects such as cursors and plans). We can clean these up when the associated object is deleted. I went so far as to get rid of PLy_malloc completely. There were a couple of places where it could still have been used safely, but on the whole it was just an invitation to bad coding. Haribabu Kommi, based on a draft patch by Heikki Linnakangas; some further work by me
1 parent 64b2e7a commit 8c75ad4

15 files changed

+221
-223
lines changed

src/pl/plpython/plpy_cursorobject.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "access/xact.h"
1010
#include "mb/pg_wchar.h"
11+
#include "utils/memutils.h"
1112

1213
#include "plpython.h"
1314

@@ -111,7 +112,12 @@ PLy_cursor_query(const char *query)
111112
return NULL;
112113
cursor->portalname = NULL;
113114
cursor->closed = false;
114-
PLy_typeinfo_init(&cursor->result);
115+
cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
116+
"PL/Python cursor context",
117+
ALLOCSET_DEFAULT_MINSIZE,
118+
ALLOCSET_DEFAULT_INITSIZE,
119+
ALLOCSET_DEFAULT_MAXSIZE);
120+
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
115121

116122
oldcontext = CurrentMemoryContext;
117123
oldowner = CurrentResourceOwner;
@@ -139,7 +145,7 @@ PLy_cursor_query(const char *query)
139145
elog(ERROR, "SPI_cursor_open() failed: %s",
140146
SPI_result_code_string(SPI_result));
141147

142-
cursor->portalname = PLy_strdup(portal->name);
148+
cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
143149

144150
PLy_spi_subtransaction_commit(oldcontext, oldowner);
145151
}
@@ -200,7 +206,12 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
200206
return NULL;
201207
cursor->portalname = NULL;
202208
cursor->closed = false;
203-
PLy_typeinfo_init(&cursor->result);
209+
cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
210+
"PL/Python cursor context",
211+
ALLOCSET_DEFAULT_MINSIZE,
212+
ALLOCSET_DEFAULT_INITSIZE,
213+
ALLOCSET_DEFAULT_MAXSIZE);
214+
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
204215

205216
oldcontext = CurrentMemoryContext;
206217
oldowner = CurrentResourceOwner;
@@ -261,7 +272,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
261272
elog(ERROR, "SPI_cursor_open() failed: %s",
262273
SPI_result_code_string(SPI_result));
263274

264-
cursor->portalname = PLy_strdup(portal->name);
275+
cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
265276

266277
PLy_spi_subtransaction_commit(oldcontext, oldowner);
267278
}
@@ -315,12 +326,13 @@ PLy_cursor_dealloc(PyObject *arg)
315326

316327
if (PortalIsValid(portal))
317328
SPI_cursor_close(portal);
329+
cursor->closed = true;
330+
}
331+
if (cursor->mcxt)
332+
{
333+
MemoryContextDelete(cursor->mcxt);
334+
cursor->mcxt = NULL;
318335
}
319-
320-
PLy_free(cursor->portalname);
321-
cursor->portalname = NULL;
322-
323-
PLy_typeinfo_dealloc(&cursor->result);
324336
arg->ob_type->tp_free(arg);
325337
}
326338

src/pl/plpython/plpy_cursorobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ typedef struct PLyCursorObject
1414
char *portalname;
1515
PLyTypeInfo result;
1616
bool closed;
17+
MemoryContext mcxt;
1718
} PLyCursorObject;
1819

1920
extern void PLy_cursor_init_type(void);

src/pl/plpython/plpy_exec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,6 @@ PLy_abort_open_subtransactions(int save_subxact_level)
852852

853853
MemoryContextSwitchTo(subtransactiondata->oldcontext);
854854
CurrentResourceOwner = subtransactiondata->oldowner;
855-
PLy_free(subtransactiondata);
855+
pfree(subtransactiondata);
856856
}
857857
}

src/pl/plpython/plpy_main.c

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,12 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
277277
flinfo.fn_mcxt = CurrentMemoryContext;
278278

279279
MemSet(&proc, 0, sizeof(PLyProcedure));
280-
proc.pyname = PLy_strdup("__plpython_inline_block");
280+
proc.mcxt = AllocSetContextCreate(TopMemoryContext,
281+
"__plpython_inline_block",
282+
ALLOCSET_DEFAULT_MINSIZE,
283+
ALLOCSET_DEFAULT_INITSIZE,
284+
ALLOCSET_DEFAULT_MAXSIZE);
285+
proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
281286
proc.langid = codeblock->langOid;
282287
proc.result.out.d.typoid = VOIDOID;
283288

@@ -364,17 +369,32 @@ PLy_current_execution_context(void)
364369
return PLy_execution_contexts;
365370
}
366371

372+
MemoryContext
373+
PLy_get_scratch_context(PLyExecutionContext *context)
374+
{
375+
/*
376+
* A scratch context might never be needed in a given plpython procedure,
377+
* so allocate it on first request.
378+
*/
379+
if (context->scratch_ctx == NULL)
380+
context->scratch_ctx =
381+
AllocSetContextCreate(TopTransactionContext,
382+
"PL/Python scratch context",
383+
ALLOCSET_DEFAULT_MINSIZE,
384+
ALLOCSET_DEFAULT_INITSIZE,
385+
ALLOCSET_DEFAULT_MAXSIZE);
386+
return context->scratch_ctx;
387+
}
388+
367389
static PLyExecutionContext *
368390
PLy_push_execution_context(void)
369391
{
370-
PLyExecutionContext *context = PLy_malloc(sizeof(PLyExecutionContext));
392+
PLyExecutionContext *context;
371393

394+
context = (PLyExecutionContext *)
395+
MemoryContextAlloc(TopTransactionContext, sizeof(PLyExecutionContext));
372396
context->curr_proc = NULL;
373-
context->scratch_ctx = AllocSetContextCreate(TopTransactionContext,
374-
"PL/Python scratch context",
375-
ALLOCSET_DEFAULT_MINSIZE,
376-
ALLOCSET_DEFAULT_INITSIZE,
377-
ALLOCSET_DEFAULT_MAXSIZE);
397+
context->scratch_ctx = NULL;
378398
context->next = PLy_execution_contexts;
379399
PLy_execution_contexts = context;
380400
return context;
@@ -390,6 +410,7 @@ PLy_pop_execution_context(void)
390410

391411
PLy_execution_contexts = context->next;
392412

393-
MemoryContextDelete(context->scratch_ctx);
394-
PLy_free(context);
413+
if (context->scratch_ctx)
414+
MemoryContextDelete(context->scratch_ctx);
415+
pfree(context);
395416
}

src/pl/plpython/plpy_main.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ typedef struct PLyExecutionContext
2525
/* Get the current execution context */
2626
extern PLyExecutionContext *PLy_current_execution_context(void);
2727

28+
/* Get the scratch memory context for specified execution context */
29+
extern MemoryContext PLy_get_scratch_context(PLyExecutionContext *context);
30+
2831
#endif /* PLPY_MAIN_H */

src/pl/plpython/plpy_planobject.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "plpy_planobject.h"
1212

1313
#include "plpy_elog.h"
14+
#include "utils/memutils.h"
1415

1516

1617
static void PLy_plan_dealloc(PyObject *arg);
@@ -80,6 +81,7 @@ PLy_plan_new(void)
8081
ob->types = NULL;
8182
ob->values = NULL;
8283
ob->args = NULL;
84+
ob->mcxt = NULL;
8385

8486
return (PyObject *) ob;
8587
}
@@ -96,20 +98,15 @@ PLy_plan_dealloc(PyObject *arg)
9698
PLyPlanObject *ob = (PLyPlanObject *) arg;
9799

98100
if (ob->plan)
101+
{
99102
SPI_freeplan(ob->plan);
100-
if (ob->types)
101-
PLy_free(ob->types);
102-
if (ob->values)
103-
PLy_free(ob->values);
104-
if (ob->args)
103+
ob->plan = NULL;
104+
}
105+
if (ob->mcxt)
105106
{
106-
int i;
107-
108-
for (i = 0; i < ob->nargs; i++)
109-
PLy_typeinfo_dealloc(&ob->args[i]);
110-
PLy_free(ob->args);
107+
MemoryContextDelete(ob->mcxt);
108+
ob->mcxt = NULL;
111109
}
112-
113110
arg->ob_type->tp_free(arg);
114111
}
115112

src/pl/plpython/plpy_planobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef struct PLyPlanObject
1717
Oid *types;
1818
Datum *values;
1919
PLyTypeInfo *args;
20+
MemoryContext mcxt;
2021
} PLyPlanObject;
2122

2223
extern void PLy_plan_init_type(void);

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