summaryrefslogtreecommitdiff
path: root/src/pl/plpython/plpy_main.c
diff options
context:
space:
mode:
authorTom Lane2012-03-13 17:19:06 +0000
committerTom Lane2012-03-13 17:19:06 +0000
commited75380bdae30dc1313aef44beafad860cf246c0 (patch)
tree564084e9eedb713dda711e35573ded9a3d5ff17d /src/pl/plpython/plpy_main.c
parent2e46bf67114586835f4a9908f1a1f08ee8ba83a8 (diff)
Create a stack of pl/python "execution contexts".
This replaces the former global variable PLy_curr_procedure, and provides a place to stash per-call-level information. In particular we create a per-call-level scratch memory context. For the moment, the scratch context is just used to avoid leaking memory from datatype output function calls in PLyDict_FromTuple. There probably will be more use-cases in future. Although this is a fix for a pre-existing memory leakage bug, it seems sufficiently invasive to not want to back-patch; it feels better as part of the major rearrangement of plpython code that we've already done as part of 9.2. Jan UrbaƄski
Diffstat (limited to 'src/pl/plpython/plpy_main.c')
-rw-r--r--src/pl/plpython/plpy_main.c114
1 files changed, 90 insertions, 24 deletions
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index ae9d87e9a63..277dedc22d2 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -12,6 +12,7 @@
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/guc.h"
+#include "utils/memutils.h"
#include "utils/syscache.h"
#include "plpython.h"
@@ -66,11 +67,17 @@ static void plpython_error_callback(void *arg);
static void plpython_inline_error_callback(void *arg);
static void PLy_init_interp(void);
+static PLyExecutionContext *PLy_push_execution_context(void);
+static void PLy_pop_execution_context(void);
+
static const int plpython_python_version = PY_MAJOR_VERSION;
/* initialize global variables */
PyObject *PLy_interp_globals = NULL;
+/* this doesn't need to be global; use PLy_current_execution_context() */
+static PLyExecutionContext *PLy_execution_contexts = NULL;
+
void
_PG_init(void)
@@ -114,6 +121,8 @@ _PG_init(void)
explicit_subtransactions = NIL;
+ PLy_execution_contexts = NULL;
+
inited = true;
}
@@ -179,13 +188,20 @@ Datum
plpython_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
- PLyProcedure *save_curr_proc;
+ PLyExecutionContext *exec_ctx;
ErrorContextCallback plerrcontext;
+ /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
- save_curr_proc = PLy_curr_procedure;
+ /*
+ * Push execution context onto stack. It is important that this get
+ * popped again, so avoid putting anything that could throw error between
+ * here and the PG_TRY. (plpython_error_callback expects the stack entry
+ * to be there, so we have to make the context first.)
+ */
+ exec_ctx = PLy_push_execution_context();
/*
* Setup error traceback support for ereport()
@@ -203,20 +219,20 @@ plpython_call_handler(PG_FUNCTION_ARGS)
HeapTuple trv;
proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
- PLy_curr_procedure = proc;
+ exec_ctx->curr_proc = proc;
trv = PLy_exec_trigger(fcinfo, proc);
retval = PointerGetDatum(trv);
}
else
{
proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
- PLy_curr_procedure = proc;
+ exec_ctx->curr_proc = proc;
retval = PLy_exec_function(fcinfo, proc);
}
}
PG_CATCH();
{
- PLy_curr_procedure = save_curr_proc;
+ PLy_pop_execution_context();
PyErr_Clear();
PG_RE_THROW();
}
@@ -224,8 +240,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
/* Pop the error context stack */
error_context_stack = plerrcontext.previous;
-
- PLy_curr_procedure = save_curr_proc;
+ /* ... and then the execution context */
+ PLy_pop_execution_context();
return retval;
}
@@ -244,22 +260,14 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
- PLyProcedure *save_curr_proc;
PLyProcedure proc;
+ PLyExecutionContext *exec_ctx;
ErrorContextCallback plerrcontext;
+ /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
- save_curr_proc = PLy_curr_procedure;
-
- /*
- * Setup error traceback support for ereport()
- */
- plerrcontext.callback = plpython_inline_error_callback;
- plerrcontext.previous = error_context_stack;
- error_context_stack = &plerrcontext;
-
MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
MemSet(&flinfo, 0, sizeof(flinfo));
fake_fcinfo.flinfo = &flinfo;
@@ -270,27 +278,44 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
proc.pyname = PLy_strdup("__plpython_inline_block");
proc.result.out.d.typoid = VOIDOID;
+ /*
+ * Push execution context onto stack. It is important that this get
+ * popped again, so avoid putting anything that could throw error between
+ * here and the PG_TRY. (plpython_inline_error_callback doesn't currently
+ * need the stack entry, but for consistency with plpython_call_handler
+ * we do it in this order.)
+ */
+ exec_ctx = PLy_push_execution_context();
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ plerrcontext.callback = plpython_inline_error_callback;
+ plerrcontext.previous = error_context_stack;
+ error_context_stack = &plerrcontext;
+
PG_TRY();
{
PLy_procedure_compile(&proc, codeblock->source_text);
- PLy_curr_procedure = &proc;
+ exec_ctx->curr_proc = &proc;
PLy_exec_function(&fake_fcinfo, &proc);
}
PG_CATCH();
{
+ PLy_pop_execution_context();
PLy_procedure_delete(&proc);
- PLy_curr_procedure = save_curr_proc;
PyErr_Clear();
PG_RE_THROW();
}
PG_END_TRY();
- PLy_procedure_delete(&proc);
-
/* Pop the error context stack */
error_context_stack = plerrcontext.previous;
+ /* ... and then the execution context */
+ PLy_pop_execution_context();
- PLy_curr_procedure = save_curr_proc;
+ /* Now clean up the transient procedure we made */
+ PLy_procedure_delete(&proc);
PG_RETURN_VOID();
}
@@ -313,9 +338,11 @@ static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
static void
plpython_error_callback(void *arg)
{
- if (PLy_curr_procedure)
+ PLyExecutionContext *exec_ctx = PLy_current_execution_context();
+
+ if (exec_ctx->curr_proc)
errcontext("PL/Python function \"%s\"",
- PLy_procedure_name(PLy_curr_procedure));
+ PLy_procedure_name(exec_ctx->curr_proc));
}
static void
@@ -323,3 +350,42 @@ plpython_inline_error_callback(void *arg)
{
errcontext("PL/Python anonymous code block");
}
+
+PLyExecutionContext *
+PLy_current_execution_context(void)
+{
+ if (PLy_execution_contexts == NULL)
+ elog(ERROR, "no Python function is currently executing");
+
+ return PLy_execution_contexts;
+}
+
+static PLyExecutionContext *
+PLy_push_execution_context(void)
+{
+ PLyExecutionContext *context = PLy_malloc(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->next = PLy_execution_contexts;
+ PLy_execution_contexts = context;
+ return context;
+}
+
+static void
+PLy_pop_execution_context(void)
+{
+ PLyExecutionContext *context = PLy_execution_contexts;
+
+ if (context == NULL)
+ elog(ERROR, "no Python function is currently executing");
+
+ PLy_execution_contexts = context->next;
+
+ MemoryContextDelete(context->scratch_ctx);
+ PLy_free(context);
+}