]> granicus.if.org Git - postgresql/commitdiff
Fix memory leaks in PL/Python.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Nov 2015 18:52:30 +0000 (13:52 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
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);