Fix memory leaks in PL/Python.
authorTom Lane <[email protected]>
Thu, 5 Nov 2015 18:52:30 +0000 (13:52 -0500)
committerTom Lane <[email protected]>
Thu, 5 Nov 2015 18:52:40 +0000 (13:52 -0500)
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

15 files changed:
src/pl/plpython/plpy_cursorobject.c
src/pl/plpython/plpy_cursorobject.h
src/pl/plpython/plpy_exec.c
src/pl/plpython/plpy_main.c
src/pl/plpython/plpy_main.h
src/pl/plpython/plpy_planobject.c
src/pl/plpython/plpy_planobject.h
src/pl/plpython/plpy_procedure.c
src/pl/plpython/plpy_procedure.h
src/pl/plpython/plpy_spi.c
src/pl/plpython/plpy_subxactobject.c
src/pl/plpython/plpy_typeio.c
src/pl/plpython/plpy_typeio.h
src/pl/plpython/plpy_util.c
src/pl/plpython/plpy_util.h

index 2c458d35fdb1a19fad1a46670487d6468f63384b..103571ba15cc0befd1fea5fede49f0dfc67c4b82 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
+#include "utils/memutils.h"
 
 #include "plpython.h"
 
@@ -111,7 +112,12 @@ PLy_cursor_query(const char *query)
        return NULL;
    cursor->portalname = NULL;
    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;
    oldowner = CurrentResourceOwner;
@@ -139,7 +145,7 @@ PLy_cursor_query(const char *query)
            elog(ERROR, "SPI_cursor_open() failed: %s",
                 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);
    }
@@ -200,7 +206,12 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
        return NULL;
    cursor->portalname = NULL;
    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;
    oldowner = CurrentResourceOwner;
@@ -261,7 +272,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
            elog(ERROR, "SPI_cursor_open() failed: %s",
                 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);
    }
@@ -315,12 +326,13 @@ PLy_cursor_dealloc(PyObject *arg)
 
        if (PortalIsValid(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);
 }
 
index 3c28f4f8e716eb2f97704447ae62ff20ea789f69..c73033c486bbe60a314f7935ce6ddb234f830144 100644 (file)
@@ -14,6 +14,7 @@ typedef struct PLyCursorObject
    char       *portalname;
    PLyTypeInfo result;
    bool        closed;
+   MemoryContext mcxt;
 } PLyCursorObject;
 
 extern void PLy_cursor_init_type(void);
index 3ccebe403e428ad5281e3f3a11b73272fc21978f..24aed011e4b65819f05f144f7e49c0cca237b693 100644 (file)
@@ -852,6 +852,6 @@ PLy_abort_open_subtransactions(int save_subxact_level)
 
        MemoryContextSwitchTo(subtransactiondata->oldcontext);
        CurrentResourceOwner = subtransactiondata->oldowner;
-       PLy_free(subtransactiondata);
+       pfree(subtransactiondata);
    }
 }
index 63a284e238411ca8aaf1e83bb1061aa24f853210..3c2ebfa16afb55094cfc8897326e22190dd38b73 100644 (file)
@@ -277,7 +277,12 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
    flinfo.fn_mcxt = CurrentMemoryContext;
 
    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.result.out.d.typoid = VOIDOID;
 
@@ -364,17 +369,32 @@ PLy_current_execution_context(void)
    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 *
 PLy_push_execution_context(void)
 {
-   PLyExecutionContext *context = PLy_malloc(sizeof(PLyExecutionContext));
+   PLyExecutionContext *context;
 
+   context = (PLyExecutionContext *)
+       MemoryContextAlloc(TopTransactionContext, sizeof(PLyExecutionContext));
    context->curr_proc = NULL;
-   context->scratch_ctx = AllocSetContextCreate(TopTransactionContext,
-                                                "PL/Python scratch context",
-                                                ALLOCSET_DEFAULT_MINSIZE,
-                                                ALLOCSET_DEFAULT_INITSIZE,
-                                                ALLOCSET_DEFAULT_MAXSIZE);
+   context->scratch_ctx = NULL;
    context->next = PLy_execution_contexts;
    PLy_execution_contexts = context;
    return context;
@@ -390,6 +410,7 @@ PLy_pop_execution_context(void)
 
    PLy_execution_contexts = context->next;
 
-   MemoryContextDelete(context->scratch_ctx);
-   PLy_free(context);
+   if (context->scratch_ctx)
+       MemoryContextDelete(context->scratch_ctx);
+   pfree(context);
 }
index b13e2c21a111b045f61fdc4da23ba583003e0ed4..10426c43236b9c52274b9280352039446cd20cce 100644 (file)
@@ -25,4 +25,7 @@ typedef struct PLyExecutionContext
 /* Get the current execution context */
 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 */
index 8305bd68e96cc640a4725d53f8798302314a3439..a9040efb502b2a09951a5ce5751a15a05feb463c 100644 (file)
@@ -11,6 +11,7 @@
 #include "plpy_planobject.h"
 
 #include "plpy_elog.h"
+#include "utils/memutils.h"
 
 
 static void PLy_plan_dealloc(PyObject *arg);
@@ -80,6 +81,7 @@ PLy_plan_new(void)
    ob->types = NULL;
    ob->values = NULL;
    ob->args = NULL;
+   ob->mcxt = NULL;
 
    return (PyObject *) ob;
 }
@@ -96,20 +98,15 @@ PLy_plan_dealloc(PyObject *arg)
    PLyPlanObject *ob = (PLyPlanObject *) arg;
 
    if (ob->plan)
+   {
        SPI_freeplan(ob->plan);
-   if (ob->types)
-       PLy_free(ob->types);
-   if (ob->values)
-       PLy_free(ob->values);
-   if (ob->args)
+       ob->plan = NULL;
+   }
+   if (ob->mcxt)
    {
-       int         i;
-
-       for (i = 0; i < ob->nargs; i++)
-           PLy_typeinfo_dealloc(&ob->args[i]);
-       PLy_free(ob->args);
+       MemoryContextDelete(ob->mcxt);
+       ob->mcxt = NULL;
    }
-
    arg->ob_type->tp_free(arg);
 }
 
index 7a89ffc2c18090355b500434545b7555c92ccec2..c67559266ec5a71b99532197399b27205591a61b 100644 (file)
@@ -17,6 +17,7 @@ typedef struct PLyPlanObject
    Oid        *types;
    Datum      *values;
    PLyTypeInfo *args;
+   MemoryContext mcxt;
 } PLyPlanObject;
 
 extern void PLy_plan_init_type(void);
index 16ff84560bbf402e05c1ae41d49bcb66e470027f..e1f56209ef00568feea49d1dc3db5cf3f26d166f 100644 (file)
@@ -112,8 +112,9 @@ PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
        else if (!PLy_procedure_valid(proc, procTup))
        {
            /* Found it, but it's invalid, free and reuse the cache entry */
-           PLy_procedure_delete(proc);
-           PLy_free(proc);
+           entry->proc = NULL;
+           if (proc)
+               PLy_procedure_delete(proc);
            proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
            entry->proc = proc;
        }
@@ -142,11 +143,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
    char        procName[NAMEDATALEN + 256];
    Form_pg_proc procStruct;
    PLyProcedure *volatile proc;
-   char       *volatile procSource = NULL;
-   Datum       prosrcdatum;
-   bool        isnull;
-   int         i,
-               rv;
+   MemoryContext cxt;
+   MemoryContext oldcxt;
+   int         rv;
 
    procStruct = (Form_pg_proc) GETSTRUCT(procTup);
    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)
        elog(ERROR, "procedure name would overrun buffer");
 
-   proc = PLy_malloc(sizeof(PLyProcedure));
-   proc->proname = PLy_strdup(NameStr(procStruct->proname));
-   proc->pyname = PLy_strdup(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);
-   for (i = 0; i < FUNC_MAX_ARGS; i++)
-       PLy_typeinfo_init(&proc->args[i]);
-   proc->nargs = 0;
-   proc->langid = procStruct->prolang;
-   {
-       MemoryContext oldcxt;
+   cxt = AllocSetContextCreate(TopMemoryContext,
+                               procName,
+                               ALLOCSET_DEFAULT_MINSIZE,
+                               ALLOCSET_DEFAULT_INITSIZE,
+                               ALLOCSET_DEFAULT_MAXSIZE);
 
-       Datum       protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
-                                         Anum_pg_proc_protrftypes, &isnull);
+   oldcxt = MemoryContextSwitchTo(cxt);
 
-       oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-       proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
-       MemoryContextSwitchTo(oldcxt);
-   }
-   proc->code = proc->statics = NULL;
-   proc->globals = NULL;
-   proc->is_setof = procStruct->proretset;
-   proc->setof = NULL;
-   proc->src = NULL;
-   proc->argnames = NULL;
+   proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
+   proc->mcxt = cxt;
 
    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,
         * 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;
            char      **names,
                       *modes;
-           int         i,
-                       pos,
+           int         pos,
                        total;
 
            /* 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++)
            {
                HeapTuple   argTypeTup;
@@ -314,7 +322,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
                }
 
                /* get argument name */
-               proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
+               proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
 
                ReleaseSysCache(argTypeTup);
 
@@ -334,18 +342,16 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
        PLy_procedure_compile(proc, procSource);
 
        pfree(procSource);
-       procSource = NULL;
    }
    PG_CATCH();
    {
+       MemoryContextSwitchTo(oldcxt);
        PLy_procedure_delete(proc);
-       if (procSource)
-           pfree(procSource);
-
        PG_RE_THROW();
    }
    PG_END_TRY();
 
+   MemoryContextSwitchTo(oldcxt);
    return proc;
 }
 
@@ -372,7 +378,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
     */
    msrc = PLy_procedure_munge_source(proc->pyname, src);
    /* 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);
    pfree(msrc);
 
@@ -404,31 +410,10 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
 void
 PLy_procedure_delete(PLyProcedure *proc)
 {
-   int         i;
-
    Py_XDECREF(proc->code);
    Py_XDECREF(proc->statics);
    Py_XDECREF(proc->globals);
-   if (proc->proname)
-       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);
+   MemoryContextDelete(proc->mcxt);
 }
 
 /*
@@ -479,7 +464,8 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
    int         i;
    bool        valid;
 
-   Assert(proc != NULL);
+   if (proc == NULL)
+       return false;
 
    /* If the pg_proc tuple has changed, it's not valid */
    if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
index 6d4b00ba7c88a795c1136c396390e6aa540001a6..9fc8db079724a10980a9fcdcdd5d7e32b95d93c2 100644 (file)
@@ -14,6 +14,8 @@ extern void init_procedure_caches(void);
 /* cached procedure data */
 typedef struct PLyProcedure
 {
+   MemoryContext mcxt;         /* context holding this PLyProcedure and its
+                                * subsidiary data */
    char       *proname;        /* SQL name of procedure */
    char       *pyname;         /* Python name of procedure */
    TransactionId fn_xmin;
index d0e255f8359d3d7c8f119470dc8ff6edd045b5fd..58e78ecebcb39139ee677727cf1b6807722ed03d 100644 (file)
@@ -61,12 +61,21 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
    if ((plan = (PLyPlanObject *) PLy_plan_new()) == 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;
 
    plan->nargs = nargs;
-   plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL;
-   plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL;
-   plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
+   plan->types = nargs ? palloc(sizeof(Oid) * nargs) : NULL;
+   plan->values = nargs ? palloc(sizeof(Datum) * nargs) : NULL;
+   plan->args = nargs ? palloc(sizeof(PLyTypeInfo) * nargs) : NULL;
+
+   MemoryContextSwitchTo(oldcontext);
 
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
@@ -84,7 +93,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
         */
        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);
        }
 
@@ -391,10 +400,17 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
    {
        PLyTypeInfo args;
        int         i;
+       MemoryContext cxt;
 
        Py_DECREF(result->nrows);
        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;
        PG_TRY();
@@ -432,13 +448,13 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
        PG_CATCH();
        {
            MemoryContextSwitchTo(oldcontext);
-           PLy_typeinfo_dealloc(&args);
+           MemoryContextDelete(cxt);
            Py_DECREF(result);
            PG_RE_THROW();
        }
        PG_END_TRY();
 
-       PLy_typeinfo_dealloc(&args);
+       MemoryContextDelete(cxt);
        SPI_freetuptable(tuptable);
    }
 
index 2e7ec4fdab49945c3e2edc5e2cb8c934c4dab1df..81fb3a3a4ab8761a2f79a467223afa8dcfd5aad0 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "access/xact.h"
 #include "executor/spi.h"
+#include "utils/memutils.h"
 
 #include "plpython.h"
 
@@ -132,16 +133,22 @@ PLy_subtransaction_enter(PyObject *self, PyObject *unused)
    subxact->started = true;
    oldcontext = CurrentMemoryContext;
 
-   subxactdata = PLy_malloc(sizeof(*subxactdata));
+   subxactdata = (PLySubtransactionData *)
+       MemoryContextAlloc(TopTransactionContext,
+                          sizeof(PLySubtransactionData));
+
    subxactdata->oldcontext = oldcontext;
    subxactdata->oldowner = CurrentResourceOwner;
 
    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);
 
+   /* Caller wants to stay in original memory context */
+   MemoryContextSwitchTo(oldcontext);
+
    Py_INCREF(self);
    return self;
 }
@@ -204,7 +211,7 @@ PLy_subtransaction_exit(PyObject *self, PyObject *args)
 
    MemoryContextSwitchTo(subxactdata->oldcontext);
    CurrentResourceOwner = subxactdata->oldowner;
-   PLy_free(subxactdata);
+   pfree(subxactdata);
 
    /*
     * AtEOSubXact_SPI() should not have popped any SPI context, but just in
index 05add6e2ce88f8a402bde968ea4bcc36e2ddc975..7ad7a4400a53fdbc4eab28a1c99720fe4608ea1c 100644 (file)
@@ -29,8 +29,8 @@
 
 
 /* I/O function caching */
-static void PLy_input_datum_func2(PLyDatumToOb *arg, 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_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, 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 */
 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 PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
 
-/* make allocations in the TopMemoryContext */
-static void perm_fmgr_info(Oid functionId, FmgrInfo *finfo);
-
 void
-PLy_typeinfo_init(PLyTypeInfo *arg)
+PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt)
 {
    arg->is_rowtype = -1;
    arg->in.r.natts = arg->out.r.natts = 0;
@@ -73,30 +70,7 @@ PLy_typeinfo_init(PLyTypeInfo *arg)
    arg->typ_relid = InvalidOid;
    arg->typrel_xmin = InvalidTransactionId;
    ItemPointerSetInvalid(&arg->typrel_tid);
-}
-
-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);
-   }
+   arg->mcxt = mcxt;
 }
 
 /*
@@ -109,7 +83,7 @@ PLy_input_datum_func(PLyTypeInfo *arg, Oid typeOid, HeapTuple typeTup, Oid langi
    if (arg->is_rowtype > 0)
        elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
    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
@@ -118,7 +92,7 @@ PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trf
    if (arg->is_rowtype > 0)
        elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
    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
@@ -126,6 +100,9 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
 {
    int         i;
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();
+   MemoryContext oldcxt;
+
+   oldcxt = MemoryContextSwitchTo(arg->mcxt);
 
    if (arg->is_rowtype == 0)
        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.atts)
-           PLy_free(arg->in.r.atts);
+           pfree(arg->in.r.atts);
        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 */
@@ -182,7 +159,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
            elog(ERROR, "cache lookup failed for type %u",
                 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,
                              typeTup,
                              exec_ctx->curr_proc->langid,
@@ -190,6 +167,8 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
 
        ReleaseSysCache(typeTup);
    }
+
+   MemoryContextSwitchTo(oldcxt);
 }
 
 void
@@ -197,6 +176,9 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
 {
    int         i;
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();
+   MemoryContext oldcxt;
+
+   oldcxt = MemoryContextSwitchTo(arg->mcxt);
 
    if (arg->is_rowtype == 0)
        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.atts)
-           PLy_free(arg->out.r.atts);
+           pfree(arg->out.r.atts);
        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));
@@ -249,12 +231,14 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
            elog(ERROR, "cache lookup failed for type %u",
                 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->trftypes);
 
        ReleaseSysCache(typeTup);
    }
+
+   MemoryContextSwitchTo(oldcxt);
 }
 
 void
@@ -291,8 +275,8 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
 {
    PyObject   *volatile dict;
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();
+   MemoryContext scratch_context = PLy_get_scratch_context(exec_ctx);
    MemoryContext oldcontext = CurrentMemoryContext;
-   int         i;
 
    if (info->is_rowtype != 1)
        elog(ERROR, "PLyTypeInfo structure describes a datum");
@@ -303,11 +287,13 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
 
    PG_TRY();
    {
+       int         i;
+
        /*
         * Do the work in the scratch context to avoid leaking memory from the
         * datatype output function calls.
         */
-       MemoryContextSwitchTo(exec_ctx->scratch_ctx);
+       MemoryContextSwitchTo(scratch_context);
        for (i = 0; i < info->in.r.natts; i++)
        {
            char       *key;
@@ -331,7 +317,7 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
            }
        }
        MemoryContextSwitchTo(oldcontext);
-       MemoryContextReset(exec_ctx->scratch_ctx);
+       MemoryContextReset(scratch_context);
    }
    PG_CATCH();
    {
@@ -370,14 +356,17 @@ PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
 }
 
 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);
    Oid         element_type;
    Oid         base_type;
    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->typmod = -1;
    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)))
    {
        arg->func = PLyObject_ToTransform;
-       perm_fmgr_info(funcid, &arg->typtransform);
+       fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
    }
    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))
            arg->func = PLyObject_ToComposite;
 
-       arg->elm = PLy_malloc0(sizeof(*arg->elm));
+       arg->elm = palloc0(sizeof(*arg->elm));
        arg->elm->func = arg->func;
        arg->elm->typtransform = arg->typtransform;
        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,
                         &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
                         &arg->elm->typioparam, &funcid);
-       perm_fmgr_info(funcid, &arg->elm->typfunc);
+       fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
    }
+
+   MemoryContextSwitchTo(oldcxt);
 }
 
 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);
    Oid         element_type;
    Oid         base_type;
    Oid         funcid;
+   MemoryContext oldcxt;
+
+   oldcxt = MemoryContextSwitchTo(arg_mcxt);
 
    /* 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->typmod = -1;
    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)))
    {
        arg->func = PLyObject_FromTransform;
-       perm_fmgr_info(funcid, &arg->typtransform);
+       fmgr_info_cxt(funcid, &arg->typtransform, arg_mcxt);
    }
    else
        switch (base_type)
@@ -503,7 +497,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup, Oid lan
        char        dummy_delim;
        Oid         funcid;
 
-       arg->elm = PLy_malloc0(sizeof(*arg->elm));
+       arg->elm = palloc0(sizeof(*arg->elm));
        arg->elm->func = arg->func;
        arg->elm->typtransform = arg->typtransform;
        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,
                         &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim,
                         &arg->elm->typioparam, &funcid);
-       perm_fmgr_info(funcid, &arg->elm->typfunc);
+       fmgr_info_cxt(funcid, &arg->elm->typfunc, arg_mcxt);
    }
+
+   MemoryContextSwitchTo(oldcxt);
 }
 
 static PyObject *
@@ -752,13 +748,19 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
    Datum       rv;
    PLyTypeInfo info;
    TupleDesc   desc;
+   MemoryContext cxt;
 
    if (typmod != -1)
        elog(ERROR, "received unnamed record type as input");
 
    /* 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));
-   PLy_typeinfo_init(&info);
+   PLy_typeinfo_init(&info, cxt);
    /* Mark it as needing output routines lookup */
    info.is_rowtype = 2;
 
@@ -774,7 +776,7 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
 
    ReleaseTupleDesc(desc);
 
-   PLy_typeinfo_dealloc(&info);
+   MemoryContextDelete(cxt);
 
    return rv;
 }
@@ -916,16 +918,22 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
    HeapTuple   typeTup;
    PLyTypeInfo locinfo;
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();
+   MemoryContext cxt;
 
    /* 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));
-   PLy_typeinfo_init(&locinfo);
+   PLy_typeinfo_init(&locinfo, cxt);
 
    typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
    if (!HeapTupleIsValid(typeTup))
        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->trftypes);
 
@@ -933,7 +941,7 @@ PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
 
    result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string);
 
-   PLy_typeinfo_dealloc(&locinfo);
+   MemoryContextDelete(cxt);
 
    return result;
 }
@@ -1177,20 +1185,3 @@ PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object
 
    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);
-}
index b01151b0fc0f5f1105817fa23e8da0b78a7517db..29fff61dc56b7df151de38a6aedc2a827350d903 100644 (file)
@@ -88,10 +88,12 @@ typedef struct PLyTypeInfo
    Oid         typ_relid;
    TransactionId typrel_xmin;
    ItemPointerData typrel_tid;
+
+   /* context for subsidiary data (doesn't belong to this struct though) */
+   MemoryContext mcxt;
 } PLyTypeInfo;
 
-extern void PLy_typeinfo_init(PLyTypeInfo *arg);
-extern void PLy_typeinfo_dealloc(PLyTypeInfo *arg);
+extern void PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt);
 
 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);
index b6b92557678cf20411c42b53d97178305a8a9638..f2d59491376d83c1d3e688173ecb00d6cc271527 100644 (file)
 #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
  * PostgreSQL server encoding.  Reference ownership is passed to the
index 4c29f9aea3c176ea4701562adc5c2381945b5f74..66c5ccf8ac3dcac7809d3db669ab7a871ea1c86b 100644 (file)
@@ -6,11 +6,6 @@
 #ifndef 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 char *PLyUnicode_AsString(PyObject *unicode);