Commit 8c75ad43 authored by Tom Lane's avatar Tom Lane

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
parent 64b2e7ad
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "access/xact.h" #include "access/xact.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "utils/memutils.h"
#include "plpython.h" #include "plpython.h"
...@@ -111,7 +112,12 @@ PLy_cursor_query(const char *query) ...@@ -111,7 +112,12 @@ PLy_cursor_query(const char *query)
return NULL; return NULL;
cursor->portalname = NULL; cursor->portalname = NULL;
cursor->closed = false; cursor->closed = false;
PLy_typeinfo_init(&cursor->result); cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python cursor context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
oldowner = CurrentResourceOwner; oldowner = CurrentResourceOwner;
...@@ -139,7 +145,7 @@ PLy_cursor_query(const char *query) ...@@ -139,7 +145,7 @@ PLy_cursor_query(const char *query)
elog(ERROR, "SPI_cursor_open() failed: %s", elog(ERROR, "SPI_cursor_open() failed: %s",
SPI_result_code_string(SPI_result)); SPI_result_code_string(SPI_result));
cursor->portalname = PLy_strdup(portal->name); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
PLy_spi_subtransaction_commit(oldcontext, oldowner); PLy_spi_subtransaction_commit(oldcontext, oldowner);
} }
...@@ -200,7 +206,12 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) ...@@ -200,7 +206,12 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
return NULL; return NULL;
cursor->portalname = NULL; cursor->portalname = NULL;
cursor->closed = false; cursor->closed = false;
PLy_typeinfo_init(&cursor->result); cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python cursor context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
PLy_typeinfo_init(&cursor->result, cursor->mcxt);
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
oldowner = CurrentResourceOwner; oldowner = CurrentResourceOwner;
...@@ -261,7 +272,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args) ...@@ -261,7 +272,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
elog(ERROR, "SPI_cursor_open() failed: %s", elog(ERROR, "SPI_cursor_open() failed: %s",
SPI_result_code_string(SPI_result)); SPI_result_code_string(SPI_result));
cursor->portalname = PLy_strdup(portal->name); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
PLy_spi_subtransaction_commit(oldcontext, oldowner); PLy_spi_subtransaction_commit(oldcontext, oldowner);
} }
...@@ -315,12 +326,13 @@ PLy_cursor_dealloc(PyObject *arg) ...@@ -315,12 +326,13 @@ PLy_cursor_dealloc(PyObject *arg)
if (PortalIsValid(portal)) if (PortalIsValid(portal))
SPI_cursor_close(portal); SPI_cursor_close(portal);
cursor->closed = true;
}
if (cursor->mcxt)
{
MemoryContextDelete(cursor->mcxt);
cursor->mcxt = NULL;
} }
PLy_free(cursor->portalname);
cursor->portalname = NULL;
PLy_typeinfo_dealloc(&cursor->result);
arg->ob_type->tp_free(arg); arg->ob_type->tp_free(arg);
} }
......
...@@ -14,6 +14,7 @@ typedef struct PLyCursorObject ...@@ -14,6 +14,7 @@ typedef struct PLyCursorObject
char *portalname; char *portalname;
PLyTypeInfo result; PLyTypeInfo result;
bool closed; bool closed;
MemoryContext mcxt;
} PLyCursorObject; } PLyCursorObject;
extern void PLy_cursor_init_type(void); extern void PLy_cursor_init_type(void);
......
...@@ -852,6 +852,6 @@ PLy_abort_open_subtransactions(int save_subxact_level) ...@@ -852,6 +852,6 @@ PLy_abort_open_subtransactions(int save_subxact_level)
MemoryContextSwitchTo(subtransactiondata->oldcontext); MemoryContextSwitchTo(subtransactiondata->oldcontext);
CurrentResourceOwner = subtransactiondata->oldowner; CurrentResourceOwner = subtransactiondata->oldowner;
PLy_free(subtransactiondata); pfree(subtransactiondata);
} }
} }
...@@ -277,7 +277,12 @@ plpython_inline_handler(PG_FUNCTION_ARGS) ...@@ -277,7 +277,12 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
flinfo.fn_mcxt = CurrentMemoryContext; flinfo.fn_mcxt = CurrentMemoryContext;
MemSet(&proc, 0, sizeof(PLyProcedure)); MemSet(&proc, 0, sizeof(PLyProcedure));
proc.pyname = PLy_strdup("__plpython_inline_block"); proc.mcxt = AllocSetContextCreate(TopMemoryContext,
"__plpython_inline_block",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
proc.langid = codeblock->langOid; proc.langid = codeblock->langOid;
proc.result.out.d.typoid = VOIDOID; proc.result.out.d.typoid = VOIDOID;
...@@ -364,17 +369,32 @@ PLy_current_execution_context(void) ...@@ -364,17 +369,32 @@ PLy_current_execution_context(void)
return PLy_execution_contexts; return PLy_execution_contexts;
} }
MemoryContext
PLy_get_scratch_context(PLyExecutionContext *context)
{
/*
* A scratch context might never be needed in a given plpython procedure,
* so allocate it on first request.
*/
if (context->scratch_ctx == NULL)
context->scratch_ctx =
AllocSetContextCreate(TopTransactionContext,
"PL/Python scratch context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
return context->scratch_ctx;
}
static PLyExecutionContext * static PLyExecutionContext *
PLy_push_execution_context(void) PLy_push_execution_context(void)
{ {
PLyExecutionContext *context = PLy_malloc(sizeof(PLyExecutionContext)); PLyExecutionContext *context;
context = (PLyExecutionContext *)
MemoryContextAlloc(TopTransactionContext, sizeof(PLyExecutionContext));
context->curr_proc = NULL; context->curr_proc = NULL;
context->scratch_ctx = AllocSetContextCreate(TopTransactionContext, context->scratch_ctx = NULL;
"PL/Python scratch context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
context->next = PLy_execution_contexts; context->next = PLy_execution_contexts;
PLy_execution_contexts = context; PLy_execution_contexts = context;
return context; return context;
...@@ -390,6 +410,7 @@ PLy_pop_execution_context(void) ...@@ -390,6 +410,7 @@ PLy_pop_execution_context(void)
PLy_execution_contexts = context->next; PLy_execution_contexts = context->next;
MemoryContextDelete(context->scratch_ctx); if (context->scratch_ctx)
PLy_free(context); MemoryContextDelete(context->scratch_ctx);
pfree(context);
} }
...@@ -25,4 +25,7 @@ typedef struct PLyExecutionContext ...@@ -25,4 +25,7 @@ typedef struct PLyExecutionContext
/* Get the current execution context */ /* Get the current execution context */
extern PLyExecutionContext *PLy_current_execution_context(void); extern PLyExecutionContext *PLy_current_execution_context(void);
/* Get the scratch memory context for specified execution context */
extern MemoryContext PLy_get_scratch_context(PLyExecutionContext *context);
#endif /* PLPY_MAIN_H */ #endif /* PLPY_MAIN_H */
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "plpy_planobject.h" #include "plpy_planobject.h"
#include "plpy_elog.h" #include "plpy_elog.h"
#include "utils/memutils.h"
static void PLy_plan_dealloc(PyObject *arg); static void PLy_plan_dealloc(PyObject *arg);
...@@ -80,6 +81,7 @@ PLy_plan_new(void) ...@@ -80,6 +81,7 @@ PLy_plan_new(void)
ob->types = NULL; ob->types = NULL;
ob->values = NULL; ob->values = NULL;
ob->args = NULL; ob->args = NULL;
ob->mcxt = NULL;
return (PyObject *) ob; return (PyObject *) ob;
} }
...@@ -96,20 +98,15 @@ PLy_plan_dealloc(PyObject *arg) ...@@ -96,20 +98,15 @@ PLy_plan_dealloc(PyObject *arg)
PLyPlanObject *ob = (PLyPlanObject *) arg; PLyPlanObject *ob = (PLyPlanObject *) arg;
if (ob->plan) if (ob->plan)
{
SPI_freeplan(ob->plan); SPI_freeplan(ob->plan);
if (ob->types) ob->plan = NULL;
PLy_free(ob->types); }
if (ob->values) if (ob->mcxt)
PLy_free(ob->values);
if (ob->args)
{ {
int i; MemoryContextDelete(ob->mcxt);
ob->mcxt = NULL;
for (i = 0; i < ob->nargs; i++)
PLy_typeinfo_dealloc(&ob->args[i]);
PLy_free(ob->args);
} }
arg->ob_type->tp_free(arg); arg->ob_type->tp_free(arg);
} }
......
...@@ -17,6 +17,7 @@ typedef struct PLyPlanObject ...@@ -17,6 +17,7 @@ typedef struct PLyPlanObject
Oid *types; Oid *types;
Datum *values; Datum *values;
PLyTypeInfo *args; PLyTypeInfo *args;
MemoryContext mcxt;
} PLyPlanObject; } PLyPlanObject;
extern void PLy_plan_init_type(void); extern void PLy_plan_init_type(void);
......
...@@ -112,8 +112,9 @@ PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger) ...@@ -112,8 +112,9 @@ PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
else if (!PLy_procedure_valid(proc, procTup)) else if (!PLy_procedure_valid(proc, procTup))
{ {
/* Found it, but it's invalid, free and reuse the cache entry */ /* Found it, but it's invalid, free and reuse the cache entry */
PLy_procedure_delete(proc); entry->proc = NULL;
PLy_free(proc); if (proc)
PLy_procedure_delete(proc);
proc = PLy_procedure_create(procTup, fn_oid, is_trigger); proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
entry->proc = proc; entry->proc = proc;
} }
...@@ -142,11 +143,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -142,11 +143,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
char procName[NAMEDATALEN + 256]; char procName[NAMEDATALEN + 256];
Form_pg_proc procStruct; Form_pg_proc procStruct;
PLyProcedure *volatile proc; PLyProcedure *volatile proc;
char *volatile procSource = NULL; MemoryContext cxt;
Datum prosrcdatum; MemoryContext oldcxt;
bool isnull; int rv;
int i,
rv;
procStruct = (Form_pg_proc) GETSTRUCT(procTup); procStruct = (Form_pg_proc) GETSTRUCT(procTup);
rv = snprintf(procName, sizeof(procName), rv = snprintf(procName, sizeof(procName),
...@@ -156,38 +155,48 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -156,38 +155,48 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
if (rv >= sizeof(procName) || rv < 0) if (rv >= sizeof(procName) || rv < 0)
elog(ERROR, "procedure name would overrun buffer"); elog(ERROR, "procedure name would overrun buffer");
proc = PLy_malloc(sizeof(PLyProcedure)); cxt = AllocSetContextCreate(TopMemoryContext,
proc->proname = PLy_strdup(NameStr(procStruct->proname)); procName,
proc->pyname = PLy_strdup(procName); ALLOCSET_DEFAULT_MINSIZE,
proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data); ALLOCSET_DEFAULT_INITSIZE,
proc->fn_tid = procTup->t_self; ALLOCSET_DEFAULT_MAXSIZE);
/* Remember if function is STABLE/IMMUTABLE */
proc->fn_readonly =
(procStruct->provolatile != PROVOLATILE_VOLATILE);
PLy_typeinfo_init(&proc->result);
for (i = 0; i < FUNC_MAX_ARGS; i++)
PLy_typeinfo_init(&proc->args[i]);
proc->nargs = 0;
proc->langid = procStruct->prolang;
{
MemoryContext oldcxt;
Datum protrftypes_datum = SysCacheGetAttr(PROCOID, procTup, oldcxt = MemoryContextSwitchTo(cxt);
Anum_pg_proc_protrftypes, &isnull);
oldcxt = MemoryContextSwitchTo(TopMemoryContext); proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum); proc->mcxt = cxt;
MemoryContextSwitchTo(oldcxt);
}
proc->code = proc->statics = NULL;
proc->globals = NULL;
proc->is_setof = procStruct->proretset;
proc->setof = NULL;
proc->src = NULL;
proc->argnames = NULL;
PG_TRY(); PG_TRY();
{ {
Datum protrftypes_datum;
Datum prosrcdatum;
bool isnull;
char *procSource;
int i;
proc->proname = pstrdup(NameStr(procStruct->proname));
proc->pyname = pstrdup(procName);
proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
proc->fn_tid = procTup->t_self;
/* Remember if function is STABLE/IMMUTABLE */
proc->fn_readonly =
(procStruct->provolatile != PROVOLATILE_VOLATILE);
PLy_typeinfo_init(&proc->result, proc->mcxt);
for (i = 0; i < FUNC_MAX_ARGS; i++)
PLy_typeinfo_init(&proc->args[i], proc->mcxt);
proc->nargs = 0;
proc->langid = procStruct->prolang;
protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
Anum_pg_proc_protrftypes,
&isnull);
proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
proc->code = proc->statics = NULL;
proc->globals = NULL;
proc->is_setof = procStruct->proretset;
proc->setof = NULL;
proc->src = NULL;
proc->argnames = NULL;
/* /*
* get information required for output conversion of the return value, * get information required for output conversion of the return value,
* but only if this isn't a trigger. * but only if this isn't a trigger.
...@@ -250,8 +259,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -250,8 +259,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
Oid *types; Oid *types;
char **names, char **names,
*modes; *modes;
int i, int pos,
pos,
total; total;
/* extract argument type info from the pg_proc tuple */ /* extract argument type info from the pg_proc tuple */
...@@ -271,7 +279,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -271,7 +279,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
} }
} }
proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs); proc->argnames = (char **) palloc0(sizeof(char *) * proc->nargs);
for (i = pos = 0; i < total; i++) for (i = pos = 0; i < total; i++)
{ {
HeapTuple argTypeTup; HeapTuple argTypeTup;
...@@ -314,7 +322,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -314,7 +322,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
} }
/* get argument name */ /* get argument name */
proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL; proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
ReleaseSysCache(argTypeTup); ReleaseSysCache(argTypeTup);
...@@ -334,18 +342,16 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) ...@@ -334,18 +342,16 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
PLy_procedure_compile(proc, procSource); PLy_procedure_compile(proc, procSource);
pfree(procSource); pfree(procSource);
procSource = NULL;
} }
PG_CATCH(); PG_CATCH();
{ {
MemoryContextSwitchTo(oldcxt);
PLy_procedure_delete(proc); PLy_procedure_delete(proc);
if (procSource)
pfree(procSource);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
MemoryContextSwitchTo(oldcxt);
return proc; return proc;
} }
...@@ -372,7 +378,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src) ...@@ -372,7 +378,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
*/ */
msrc = PLy_procedure_munge_source(proc->pyname, src); msrc = PLy_procedure_munge_source(proc->pyname, src);
/* Save the mangled source for later inclusion in tracebacks */ /* Save the mangled source for later inclusion in tracebacks */
proc->src = PLy_strdup(msrc); proc->src = MemoryContextStrdup(proc->mcxt, msrc);
crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL); crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
pfree(msrc); pfree(msrc);
...@@ -404,31 +410,10 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src) ...@@ -404,31 +410,10 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
void void
PLy_procedure_delete(PLyProcedure *proc) PLy_procedure_delete(PLyProcedure *proc)
{ {
int i;
Py_XDECREF(proc->code); Py_XDECREF(proc->code);
Py_XDECREF(proc->statics); Py_XDECREF(proc->statics);
Py_XDECREF(proc->globals); Py_XDECREF(proc->globals);
if (proc->proname) MemoryContextDelete(proc->mcxt);
PLy_free(proc->proname);
if (proc->pyname)
PLy_free(proc->pyname);
for (i = 0; i < proc->nargs; i++)
{
if (proc->args[i].is_rowtype == 1)
{
if (proc->args[i].in.r.atts)
PLy_free(proc->args[i].in.r.atts);
if (proc->args[i].out.r.atts)
PLy_free(proc->args[i].out.r.atts);
}
if (proc->argnames && proc->argnames[i])
PLy_free(proc->argnames[i]);
}
if (proc->src)
PLy_free(proc->src);
if (proc->argnames)
PLy_free(proc->argnames);
} }
/* /*
...@@ -479,7 +464,8 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup) ...@@ -479,7 +464,8 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
int i; int i;
bool valid; bool valid;
Assert(proc != NULL); if (proc == NULL)
return false;
/* If the pg_proc tuple has changed, it's not valid */ /* If the pg_proc tuple has changed, it's not valid */
if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) && if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
......
...@@ -14,6 +14,8 @@ extern void init_procedure_caches(void); ...@@ -14,6 +14,8 @@ extern void init_procedure_caches(void);
/* cached procedure data */ /* cached procedure data */
typedef struct PLyProcedure typedef struct PLyProcedure
{ {
MemoryContext mcxt; /* context holding this PLyProcedure and its
* subsidiary data */
char *proname; /* SQL name of procedure */ char *proname; /* SQL name of procedure */
char *pyname; /* Python name of procedure */ char *pyname; /* Python name of procedure */
TransactionId fn_xmin; TransactionId fn_xmin;
......
...@@ -61,12 +61,21 @@ PLy_spi_prepare(PyObject *self, PyObject *args) ...@@ -61,12 +61,21 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL) if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
return NULL; return NULL;
plan->mcxt = AllocSetContextCreate(TopMemoryContext,
"PL/Python plan context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(plan->mcxt);
nargs = list ? PySequence_Length(list) : 0; nargs = list ? PySequence_Length(list) : 0;
plan->nargs = nargs; plan->nargs = nargs;
plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL; plan->types = nargs ? palloc(sizeof(Oid) * nargs) : NULL;
plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL; plan->values = nargs ? palloc(sizeof(Datum) * nargs) : NULL;
plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL; plan->args = nargs ? palloc(sizeof(PLyTypeInfo) * nargs) : NULL;
MemoryContextSwitchTo(oldcontext);
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
oldowner = CurrentResourceOwner; oldowner = CurrentResourceOwner;
...@@ -84,7 +93,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args) ...@@ -84,7 +93,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
*/ */
for (i = 0; i < nargs; i++) for (i = 0; i < nargs; i++)
{ {
PLy_typeinfo_init(&plan->args[i]); PLy_typeinfo_init(&plan->args[i], plan->mcxt);
plan->values[i] = PointerGetDatum(NULL); plan->values[i] = PointerGetDatum(NULL);
} }
...@@ -391,10 +400,17 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) ...@@ -391,10 +400,17 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
{ {
PLyTypeInfo args; PLyTypeInfo args;
int i; int i;
MemoryContext cxt;
Py_DECREF(result->nrows); Py_DECREF(result->nrows);
result->nrows = PyInt_FromLong(rows); result->nrows = PyInt_FromLong(rows);
PLy_typeinfo_init(&args);
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
PLy_typeinfo_init(&args, cxt);
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
PG_TRY(); PG_TRY();
...@@ -432,13 +448,13 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) ...@@ -432,13 +448,13 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
PG_CATCH(); PG_CATCH();
{ {
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
PLy_typeinfo_dealloc(&args); MemoryContextDelete(cxt);
Py_DECREF(result); Py_DECREF(result);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
PLy_typeinfo_dealloc(&args); MemoryContextDelete(cxt);
SPI_freetuptable(tuptable); SPI_freetuptable(tuptable);
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "access/xact.h" #include "access/xact.h"
#include "executor/spi.h" #include "executor/spi.h"
#include "utils/memutils.h"
#include "plpython.h" #include "plpython.h"
...@@ -132,16 +133,22 @@ PLy_subtransaction_enter(PyObject *self, PyObject *unused) ...@@ -132,16 +133,22 @@ PLy_subtransaction_enter(PyObject *self, PyObject *unused)
subxact->started = true; subxact->started = true;
oldcontext = CurrentMemoryContext; oldcontext = CurrentMemoryContext;
subxactdata = PLy_malloc(sizeof(*subxactdata)); subxactdata = (PLySubtransactionData *)
MemoryContextAlloc(TopTransactionContext,
sizeof(PLySubtransactionData));
subxactdata->oldcontext = oldcontext; subxactdata->oldcontext = oldcontext;
subxactdata->oldowner = CurrentResourceOwner; subxactdata->oldowner = CurrentResourceOwner;
BeginInternalSubTransaction(NULL); BeginInternalSubTransaction(NULL);
/* Do not want to leave the previous memory context */
MemoryContextSwitchTo(oldcontext);
/* Be sure that cells of explicit_subtransactions list are long-lived */
MemoryContextSwitchTo(TopTransactionContext);
explicit_subtransactions = lcons(subxactdata, explicit_subtransactions); explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
/* Caller wants to stay in original memory context */
MemoryContextSwitchTo(oldcontext);
Py_INCREF(self); Py_INCREF(self);
return self; return self;
} }
...@@ -204,7 +211,7 @@ PLy_subtransaction_exit(PyObject *self, PyObject *args) ...@@ -204,7 +211,7 @@ PLy_subtransaction_exit(PyObject *self, PyObject *args)
MemoryContextSwitchTo(subxactdata->oldcontext); MemoryContextSwitchTo(subxactdata->oldcontext);
CurrentResourceOwner = subxactdata->oldowner; CurrentResourceOwner = subxactdata->oldowner;
PLy_free(subxactdata); pfree(subxactdata);
/* /*
* AtEOSubXact_SPI() should not have popped any SPI context, but just in * AtEOSubXact_SPI() should not have popped any SPI context, but just in
......
...@@ -29,8 +29,8 @@ ...@@ -29,8 +29,8 @@
/* I/O function caching */ /* I/O function caching */
static void PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes); static void PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes);
static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *trftypes); static void PLy_output_datum_func2(PLyObToDatum *arg, MemoryContext arg_mcxt, HeapTuple typeTup, Oid langid, List *trftypes);
/* conversion from Datums to Python objects */ /* conversion from Datums to Python objects */
static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d); static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);
...@@ -60,11 +60,8 @@ static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject ...@@ -60,11 +60,8 @@ static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject
static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence); static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence);
static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object); static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
/* make allocations in the TopMemoryContext */
static void perm_fmgr_info(Oid functionId, FmgrInfo *finfo);
void void
PLy_typeinfo_init(PLyTypeInfo *arg) PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt)
{ {
arg->is_rowtype = -1; arg->is_rowtype = -1;
arg->in.r.natts = arg->out.r.natts = 0; arg->in.r.natts = arg->out.r.natts = 0;
...@@ -73,30 +70,7 @@ PLy_typeinfo_init(PLyTypeInfo *arg) ...@@ -73,30 +70,7 @@ PLy_typeinfo_init(PLyTypeInfo *arg)
arg->typ_relid = InvalidOid; arg->typ_relid = InvalidOid;
arg->typrel_xmin = InvalidTransactionId; arg->typrel_xmin = InvalidTransactionId;
ItemPointerSetInvalid(&arg->typrel_tid); ItemPointerSetInvalid(&arg->typrel_tid);
} arg->mcxt = mcxt;
void
PLy_typeinfo_dealloc(PLyTypeInfo *arg)
{
if (arg->is_rowtype == 1)
{
int i;
for (i = 0; i < arg->in.r.natts; i++)
{
if (arg->in.r.atts[i].elm != NULL)
PLy_free(arg->in.r.atts[i].elm);
}
if (arg->in.r.atts)
PLy_free(arg->in.r.atts);
for (i = 0; i < arg->out.r.natts; i++)
{
if (arg->out.r.atts[i].elm != NULL)
PLy_free(arg->out.r.atts[i].elm);
}
if (arg->out.r.atts)
PLy_free(arg->out.r.atts);
}
} }
/* /*
...@@ -109,7 +83,7 @@ PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langi ...@@ -109,7 +83,7 @@ PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langi
if (arg->is_rowtype > 0) if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for Tuple"); elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
arg->is_rowtype = 0; arg->is_rowtype = 0;
PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup, langid, trftypes); PLy_input_datum_func2(&(arg->in.d), arg->mcxt, typeOid, typeTup, langid, trftypes);
} }
void void
...@@ -118,7 +92,7 @@ PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trf ...@@ -118,7 +92,7 @@ PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trf
if (arg->is_rowtype > 0) if (arg->is_rowtype > 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple"); elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
arg->is_rowtype = 0; arg->is_rowtype = 0;
PLy_output_datum_func2(&(arg->out.d), typeTup, langid, trftypes); PLy_output_datum_func2(&(arg->out.d), arg->mcxt, typeTup, langid, trftypes);
} }
void void
...@@ -126,6 +100,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -126,6 +100,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
{ {
int i; int i;
PLyExecutionContext *exec_ctx = PLy_current_execution_context(); PLyExecutionContext *exec_ctx = PLy_current_execution_context();
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(arg->mcxt);
if (arg->is_rowtype == 0) if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
...@@ -134,9 +111,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -134,9 +111,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
if (arg->in.r.natts != desc->natts) if (arg->in.r.natts != desc->natts)
{ {
if (arg->in.r.atts) if (arg->in.r.atts)
PLy_free(arg->in.r.atts); pfree(arg->in.r.atts);
arg->in.r.natts = desc->natts; arg->in.r.natts = desc->natts;
arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); arg->in.r.atts = palloc0(desc->natts * sizeof(PLyDatumToOb));
} }
/* Can this be an unnamed tuple? If not, then an Assert would be enough */ /* Can this be an unnamed tuple? If not, then an Assert would be enough */
...@@ -182,7 +159,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -182,7 +159,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
elog(ERROR, "cache lookup failed for type %u", elog(ERROR, "cache lookup failed for type %u",
desc->attrs[i]->atttypid); desc->attrs[i]->atttypid);
PLy_input_datum_func2(&(arg->in.r.atts[i]), PLy_input_datum_func2(&(arg->in.r.atts[i]), arg->mcxt,
desc->attrs[i]->atttypid, desc->attrs[i]->atttypid,
typeTup, typeTup,
exec_ctx->curr_proc->langid, exec_ctx->curr_proc->langid,
...@@ -190,6 +167,8 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -190,6 +167,8 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
} }
MemoryContextSwitchTo(oldcxt);
} }
void void
...@@ -197,6 +176,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -197,6 +176,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
{ {
int i; int i;
PLyExecutionContext *exec_ctx = PLy_current_execution_context(); PLyExecutionContext *exec_ctx = PLy_current_execution_context();
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(arg->mcxt);
if (arg->is_rowtype == 0) if (arg->is_rowtype == 0)
elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
...@@ -205,9 +187,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -205,9 +187,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
if (arg->out.r.natts != desc->natts) if (arg->out.r.natts != desc->natts)
{ {
if (arg->out.r.atts) if (arg->out.r.atts)
PLy_free(arg->out.r.atts); pfree(arg->out.r.atts);
arg->out.r.natts = desc->natts; arg->out.r.natts = desc->natts;
arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyObToDatum)); arg->out.r.atts = palloc0(desc->natts * sizeof(PLyObToDatum));
} }
Assert(OidIsValid(desc->tdtypeid)); Assert(OidIsValid(desc->tdtypeid));
...@@ -249,12 +231,14 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) ...@@ -249,12 +231,14 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
elog(ERROR, "cache lookup failed for type %u", elog(ERROR, "cache lookup failed for type %u",
desc->attrs[i]->atttypid); desc->attrs[i]->atttypid);
PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup, PLy_output_datum_func2(&(arg->out.r.atts[i]), arg->mcxt, typeTup,
exec_ctx->curr_proc->langid, exec_ctx->curr_proc->langid,
exec_ctx->curr_proc->trftypes); exec_ctx->curr_proc->trftypes);
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
} }
MemoryContextSwitchTo(oldcxt);
} }
void void
...@@ -291,8 +275,8 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc) ...@@ -291,8 +275,8 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
{ {
PyObject *volatile dict; PyObject *volatile dict;
PLyExecutionContext *exec_ctx = PLy_current_execution_context(); PLyExecutionContext *exec_ctx = PLy_current_execution_context();
MemoryContext scratch_context = PLy_get_scratch_context(exec_ctx);
MemoryContext oldcontext = CurrentMemoryContext; MemoryContext oldcontext = CurrentMemoryContext;
int i;
if (info->is_rowtype != 1) if (info->is_rowtype != 1)
elog(ERROR, "PLyTypeInfo structure describes a datum"); elog(ERROR, "PLyTypeInfo structure describes a datum");
...@@ -303,11 +287,13 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc) ...@@ -303,11 +287,13 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
PG_TRY(); PG_TRY();
{ {
int i;
/* /*
* Do the work in the scratch context to avoid leaking memory from the * Do the work in the scratch context to avoid leaking memory from the
* datatype output function calls. * datatype output function calls.
*/ */
MemoryContextSwitchTo(exec_ctx->scratch_ctx); MemoryContextSwitchTo(scratch_context);
for (i = 0; i < info->in.r.natts; i++) for (i = 0; i < info->in.r.natts; i++)
{ {
char *key; char *key;
...@@ -331,7 +317,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc) ...@@ -331,7 +317,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
} }
} }
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
MemoryContextReset(exec_ctx->scratch_ctx); MemoryContextReset(scratch_context);
} }
PG_CATCH(); PG_CATCH();
{ {
...@@ -370,14 +356,17 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv) ...@@ -370,14 +356,17 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
} }
static void static void
PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *trftypes) PLy_output_datum_func2(PLyObToDatum *arg, MemoryContext arg_mcxt, HeapTuple typeTup, Oid langid, List *trftypes)
{ {
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type; Oid element_type;
Oid base_type; Oid base_type;
Oid funcid; Oid funcid;
MemoryContext oldcxt;
perm_fmgr_info(typeStruct->typinput, &arg->typfunc); oldcxt = MemoryContextSwitchTo(arg_mcxt);
fmgr_info_cxt(typeStruct->typinput, &arg->typfunc, arg_mcxt);
arg->typoid = HeapTupleGetOid(typeTup); arg->typoid = HeapTupleGetOid(typeTup);
arg->typmod = -1; arg->typmod = -1;
arg->typioparam = getTypeIOParam(typeTup); arg->typioparam = getTypeIOParam(typeTup);
...@@ -394,7 +383,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t ...@@ -394,7 +383,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t
if ((funcid = get_transform_tosql(base_type, langid, trftypes))) if ((funcid = get_transform_tosql(base_type, langid, trftypes)))
{ {
arg->func = PLyObject_ToTransform; arg->func = PLyObject_ToTransform;
perm_fmgr_info(funcid, &arg->typtransform); fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
} }
else if (typeStruct->typtype == TYPTYPE_COMPOSITE) else if (typeStruct->typtype == TYPTYPE_COMPOSITE)
{ {
...@@ -422,7 +411,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t ...@@ -422,7 +411,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t
if (type_is_rowtype(element_type)) if (type_is_rowtype(element_type))
arg->func = PLyObject_ToComposite; arg->func = PLyObject_ToComposite;
arg->elm = PLy_malloc0(sizeof(*arg->elm)); arg->elm = palloc0(sizeof(*arg->elm));
arg->elm->func = arg->func; arg->elm->func = arg->func;
arg->elm->typtransform = arg->typtransform; arg->elm->typtransform = arg->typtransform;
arg->func = PLySequence_ToArray; arg->func = PLySequence_ToArray;
...@@ -432,20 +421,25 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t ...@@ -432,20 +421,25 @@ PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *t
get_type_io_data(element_type, IOFunc_input, get_type_io_data(element_type, IOFunc_input,
&arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
&arg->elm->typioparam, &funcid); &arg->elm->typioparam, &funcid);
perm_fmgr_info(funcid, &arg->elm->typfunc); fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
} }
MemoryContextSwitchTo(oldcxt);
} }
static void static void
PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes) PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes)
{ {
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
Oid element_type; Oid element_type;
Oid base_type; Oid base_type;
Oid funcid; Oid funcid;
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(arg_mcxt);
/* Get the type's conversion information */ /* Get the type's conversion information */
perm_fmgr_info(typeStruct->typoutput, &arg->typfunc); fmgr_info_cxt(typeStruct->typoutput, &arg->typfunc, arg_mcxt);
arg->typoid = HeapTupleGetOid(typeTup); arg->typoid = HeapTupleGetOid(typeTup);
arg->typmod = -1; arg->typmod = -1;
arg->typioparam = getTypeIOParam(typeTup); arg->typioparam = getTypeIOParam(typeTup);
...@@ -461,7 +455,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan ...@@ -461,7 +455,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan
if ((funcid = get_transform_fromsql(base_type, langid, trftypes))) if ((funcid = get_transform_fromsql(base_type, langid, trftypes)))
{ {
arg->func = PLyObject_FromTransform; arg->func = PLyObject_FromTransform;
perm_fmgr_info(funcid, &arg->typtransform); fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
} }
else else
switch (base_type) switch (base_type)
...@@ -503,7 +497,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan ...@@ -503,7 +497,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan
char dummy_delim; char dummy_delim;
Oid funcid; Oid funcid;
arg->elm = PLy_malloc0(sizeof(*arg->elm)); arg->elm = palloc0(sizeof(*arg->elm));
arg->elm->func = arg->func; arg->elm->func = arg->func;
arg->elm->typtransform = arg->typtransform; arg->elm->typtransform = arg->typtransform;
arg->func = PLyList_FromArray; arg->func = PLyList_FromArray;
...@@ -512,8 +506,10 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan ...@@ -512,8 +506,10 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan
get_type_io_data(element_type, IOFunc_output, get_type_io_data(element_type, IOFunc_output,
&arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
&arg->elm->typioparam, &funcid); &arg->elm->typioparam, &funcid);
perm_fmgr_info(funcid, &arg->elm->typfunc); fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
} }
MemoryContextSwitchTo(oldcxt);
} }
static PyObject * static PyObject *
...@@ -752,13 +748,19 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv) ...@@ -752,13 +748,19 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
Datum rv; Datum rv;
PLyTypeInfo info; PLyTypeInfo info;
TupleDesc desc; TupleDesc desc;
MemoryContext cxt;
if (typmod != -1) if (typmod != -1)
elog(ERROR, "received unnamed record type as input"); elog(ERROR, "received unnamed record type as input");
/* Create a dummy PLyTypeInfo */ /* Create a dummy PLyTypeInfo */
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemSet(&info, 0, sizeof(PLyTypeInfo)); MemSet(&info, 0, sizeof(PLyTypeInfo));
PLy_typeinfo_init(&info); PLy_typeinfo_init(&info, cxt);
/* Mark it as needing output routines lookup */ /* Mark it as needing output routines lookup */
info.is_rowtype = 2; info.is_rowtype = 2;
...@@ -774,7 +776,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv) ...@@ -774,7 +776,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
ReleaseTupleDesc(desc); ReleaseTupleDesc(desc);
PLy_typeinfo_dealloc(&info); MemoryContextDelete(cxt);
return rv; return rv;
} }
...@@ -916,16 +918,22 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string) ...@@ -916,16 +918,22 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
HeapTuple typeTup; HeapTuple typeTup;
PLyTypeInfo locinfo; PLyTypeInfo locinfo;
PLyExecutionContext *exec_ctx = PLy_current_execution_context(); PLyExecutionContext *exec_ctx = PLy_current_execution_context();
MemoryContext cxt;
/* Create a dummy PLyTypeInfo */ /* Create a dummy PLyTypeInfo */
cxt = AllocSetContextCreate(CurrentMemoryContext,
"PL/Python temp context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
MemSet(&locinfo, 0, sizeof(PLyTypeInfo)); MemSet(&locinfo, 0, sizeof(PLyTypeInfo));
PLy_typeinfo_init(&locinfo); PLy_typeinfo_init(&locinfo, cxt);
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid)); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
if (!HeapTupleIsValid(typeTup)) if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid); elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid);
PLy_output_datum_func2(&locinfo.out.d, typeTup, PLy_output_datum_func2(&locinfo.out.d, locinfo.mcxt, typeTup,
exec_ctx->curr_proc->langid, exec_ctx->curr_proc->langid,
exec_ctx->curr_proc->trftypes); exec_ctx->curr_proc->trftypes);
...@@ -933,7 +941,7 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string) ...@@ -933,7 +941,7 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string); result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string);
PLy_typeinfo_dealloc(&locinfo); MemoryContextDelete(cxt);
return result; return result;
} }
...@@ -1177,20 +1185,3 @@ PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object ...@@ -1177,20 +1185,3 @@ PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object
return result; return result;
} }
/*
* This routine is a crock, and so is everyplace that calls it. The problem
* is that the cached form of plpython functions/queries is allocated permanently
* (mostly via malloc()) and never released until backend exit. Subsidiary
* data structures such as fmgr info records therefore must live forever
* as well. A better implementation would store all this stuff in a per-
* function memory context that could be reclaimed at need. In the meantime,
* fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
* it might allocate, and whatever the eventual function might allocate using
* fn_mcxt, will live forever too.
*/
static void
perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
{
fmgr_info_cxt(functionId, finfo, TopMemoryContext);
}
...@@ -88,10 +88,12 @@ typedef struct PLyTypeInfo ...@@ -88,10 +88,12 @@ typedef struct PLyTypeInfo
Oid typ_relid; Oid typ_relid;
TransactionId typrel_xmin; TransactionId typrel_xmin;
ItemPointerData typrel_tid; ItemPointerData typrel_tid;
/* context for subsidiary data (doesn't belong to this struct though) */
MemoryContext mcxt;
} PLyTypeInfo; } PLyTypeInfo;
extern void PLy_typeinfo_init(PLyTypeInfo *arg); extern void PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt);
extern void PLy_typeinfo_dealloc(PLyTypeInfo *arg);
extern void PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes); extern void PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langid, List *trftypes);
extern void PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes); extern void PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes);
......
...@@ -17,42 +17,6 @@ ...@@ -17,42 +17,6 @@
#include "plpy_elog.h" #include "plpy_elog.h"
void *
PLy_malloc(size_t bytes)
{
/* We need our allocations to be long-lived, so use TopMemoryContext */
return MemoryContextAlloc(TopMemoryContext, bytes);
}
void *
PLy_malloc0(size_t bytes)
{
void *ptr = PLy_malloc(bytes);
MemSet(ptr, 0, bytes);
return ptr;
}
char *
PLy_strdup(const char *str)
{
char *result;
size_t len;
len = strlen(str) + 1;
result = PLy_malloc(len);
memcpy(result, str, len);
return result;
}
/* define this away */
void
PLy_free(void *ptr)
{
pfree(ptr);
}
/* /*
* Convert a Python unicode object to a Python string/bytes object in * Convert a Python unicode object to a Python string/bytes object in
* PostgreSQL server encoding. Reference ownership is passed to the * PostgreSQL server encoding. Reference ownership is passed to the
......
...@@ -6,11 +6,6 @@ ...@@ -6,11 +6,6 @@
#ifndef PLPY_UTIL_H #ifndef PLPY_UTIL_H
#define PLPY_UTIL_H #define PLPY_UTIL_H
extern void *PLy_malloc(size_t bytes);
extern void *PLy_malloc0(size_t bytes);
extern char *PLy_strdup(const char *str);
extern void PLy_free(void *ptr);
extern PyObject *PLyUnicode_Bytes(PyObject *unicode); extern PyObject *PLyUnicode_Bytes(PyObject *unicode);
extern char *PLyUnicode_AsString(PyObject *unicode); extern char *PLyUnicode_AsString(PyObject *unicode);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment