summaryrefslogtreecommitdiff
path: root/src/pl/plpython/plpython.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2011-02-02 22:06:10 +0200
committerPeter Eisentraut <peter_e@gmx.net>2011-02-02 22:06:10 +0200
commit0c5933d0104c1788479592a84cca53da357381f9 (patch)
treef177882aa761e677620b820e6dbb214b5ba9e0ae /src/pl/plpython/plpython.c
parentc73fe72e2735d20aa132640d8fab4e0eca1ced95 (diff)
Wrap PL/Python SPI calls into subtransactions
This allows the language-specific try/catch construct to catch and handle exceptions arising from SPI calls, matching the behavior of other PLs. As an additional bonus you no longer get all the ugly "unrecognized error in PLy_spi_execute_query" errors. Jan UrbaƄski, reviewed by Steve Singer
Diffstat (limited to 'src/pl/plpython/plpython.c')
-rw-r--r--src/pl/plpython/plpython.c118
1 files changed, 106 insertions, 12 deletions
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index fbfeb2c9f16..fff7de76743 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -101,6 +101,7 @@ typedef int Py_ssize_t;
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "tcop/tcopprot.h"
+#include "access/xact.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
@@ -2856,6 +2857,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
char *query;
void *tmpplan;
volatile MemoryContext oldcontext;
+ volatile ResourceOwner oldowner;
int nargs;
if (!PyArg_ParseTuple(args, "s|O", &query, &list))
@@ -2879,6 +2881,11 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
oldcontext = CurrentMemoryContext;
+ oldowner = CurrentResourceOwner;
+
+ BeginInternalSubTransaction(NULL);
+ MemoryContextSwitchTo(oldcontext);
+
PG_TRY();
{
int i;
@@ -2958,20 +2965,42 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
if (plan->plan == NULL)
elog(ERROR, "SPI_saveplan failed: %s",
SPI_result_code_string(SPI_result));
+
+ /* Commit the inner transaction, return to outer xact context */
+ ReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
+ * in case it did, make sure we remain connected.
+ */
+ SPI_restore_connection();
}
PG_CATCH();
{
ErrorData *edata;
+ /* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
Py_DECREF(plan);
Py_XDECREF(optr);
- if (!PyErr_Occurred())
- PLy_exception_set(PLy_exc_spi_error,
- "unrecognized error in PLy_spi_prepare");
- PLy_elog(WARNING, NULL);
+
+ /* Abort the inner transaction */
+ RollbackAndReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it
+ * will have left us in a disconnected state. We need this hack to
+ * return to connected state.
+ */
+ SPI_restore_connection();
+
+ /* Make Python raise the exception */
PLy_spi_exception_set(edata);
return NULL;
}
@@ -3013,6 +3042,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
rv;
PLyPlanObject *plan;
volatile MemoryContext oldcontext;
+ volatile ResourceOwner oldowner;
PyObject *ret;
if (list != NULL)
@@ -3048,6 +3078,12 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
}
oldcontext = CurrentMemoryContext;
+ oldowner = CurrentResourceOwner;
+
+ BeginInternalSubTransaction(NULL);
+ /* Want to run inside function's memory context */
+ MemoryContextSwitchTo(oldcontext);
+
PG_TRY();
{
char *nulls;
@@ -3100,12 +3136,24 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
if (nargs > 0)
pfree(nulls);
+
+ /* Commit the inner transaction, return to outer xact context */
+ ReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
+ * in case it did, make sure we remain connected.
+ */
+ SPI_restore_connection();
}
PG_CATCH();
{
int k;
ErrorData *edata;
+ /* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
@@ -3123,10 +3171,19 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
}
}
- if (!PyErr_Occurred())
- PLy_exception_set(PLy_exc_spi_error,
- "unrecognized error in PLy_spi_execute_plan");
- PLy_elog(WARNING, NULL);
+ /* Abort the inner transaction */
+ RollbackAndReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it
+ * will have left us in a disconnected state. We need this hack to
+ * return to connected state.
+ */
+ SPI_restore_connection();
+
+ /* Make Python raise the exception */
PLy_spi_exception_set(edata);
return NULL;
}
@@ -3142,6 +3199,14 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
}
}
+ if (rv < 0)
+ {
+ PLy_exception_set(PLy_exc_spi_error,
+ "SPI_execute_plan failed: %s",
+ SPI_result_code_string(rv));
+ return NULL;
+ }
+
return ret;
}
@@ -3150,26 +3215,55 @@ PLy_spi_execute_query(char *query, long limit)
{
int rv;
volatile MemoryContext oldcontext;
+ volatile ResourceOwner oldowner;
PyObject *ret;
oldcontext = CurrentMemoryContext;
+ oldowner = CurrentResourceOwner;
+
+ BeginInternalSubTransaction(NULL);
+ /* Want to run inside function's memory context */
+ MemoryContextSwitchTo(oldcontext);
+
PG_TRY();
{
pg_verifymbstr(query, strlen(query), false);
rv = SPI_execute(query, PLy_curr_procedure->fn_readonly, limit);
ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
+
+ /* Commit the inner transaction, return to outer xact context */
+ ReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
+ * in case it did, make sure we remain connected.
+ */
+ SPI_restore_connection();
}
PG_CATCH();
{
ErrorData *edata;
+ /* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
- if (!PyErr_Occurred())
- PLy_exception_set(PLy_exc_spi_error,
- "unrecognized error in PLy_spi_execute_query");
- PLy_elog(WARNING, NULL);
+
+ /* Abort the inner transaction */
+ RollbackAndReleaseCurrentSubTransaction();
+ MemoryContextSwitchTo(oldcontext);
+ CurrentResourceOwner = oldowner;
+
+ /*
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it
+ * will have left us in a disconnected state. We need this hack to
+ * return to connected state.
+ */
+ SPI_restore_connection();
+
+ /* Make Python raise the exception */
PLy_spi_exception_set(edata);
return NULL;
}