diff options
author | Peter Eisentraut <peter_e@gmx.net> | 2011-02-02 22:06:10 +0200 |
---|---|---|
committer | Peter Eisentraut <peter_e@gmx.net> | 2011-02-02 22:06:10 +0200 |
commit | 0c5933d0104c1788479592a84cca53da357381f9 (patch) | |
tree | f177882aa761e677620b820e6dbb214b5ba9e0ae /src/pl/plpython/plpython.c | |
parent | c73fe72e2735d20aa132640d8fab4e0eca1ced95 (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.c | 118 |
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; } |