mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
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
This commit is contained in:
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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
|
|||||||
/* 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 @@
|
|||||||
#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)
|
|||||||
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)
|
|||||||
PLyPlanObject *ob = (PLyPlanObject *) arg;
|
PLyPlanObject *ob = (PLyPlanObject *) arg;
|
||||||
|
|
||||||
if (ob->plan)
|
if (ob->plan)
|
||||||
SPI_freeplan(ob->plan);
|
|
||||||
if (ob->types)
|
|
||||||
PLy_free(ob->types);
|
|
||||||
if (ob->values)
|
|
||||||
PLy_free(ob->values);
|
|
||||||
if (ob->args)
|
|
||||||
{
|
{
|
||||||
int i;
|
SPI_freeplan(ob->plan);
|
||||||
|
ob->plan = NULL;
|
||||||
for (i = 0; i < ob->nargs; i++)
|
}
|
||||||
PLy_typeinfo_dealloc(&ob->args[i]);
|
if (ob->mcxt)
|
||||||
PLy_free(ob->args);
|
{
|
||||||
|
MemoryContextDelete(ob->mcxt);
|
||||||
|
ob->mcxt = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
arg->ob_type->tp_free(arg);
|
arg->ob_type->tp_free(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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)
|
|||||||
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);
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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)
|
|||||||
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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
|
|
||||||
|
|
||||||
/* 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
|
|||||||
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)
|
|||||||
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
|
|||||||
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
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
ReleaseSysCache(typeTup);
|
ReleaseSysCache(typeTup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
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 @@
|
|||||||
#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 @@
|
|||||||
#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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user