]> granicus.if.org Git - postgresql/commitdiff
PL/Python: Refactor subtransaction handling
authorPeter Eisentraut <peter_e@gmx.net>
Thu, 15 Dec 2011 14:52:57 +0000 (16:52 +0200)
committerPeter Eisentraut <peter_e@gmx.net>
Thu, 15 Dec 2011 14:52:57 +0000 (16:52 +0200)
Lots of repetitive code was moved into new functions
PLy_spi_subtransaction_{begin,commit,abort}.

Jan UrbaƄski

src/pl/plpython/plpython.c

index 29e0ac7c45466fbf17bb8cbee6327a1c66d964a4..dce8ff247b66b60458dcc2b902dd7c235022ab53 100644 (file)
@@ -2964,6 +2964,12 @@ static int       PLy_result_ass_item(PyObject *, Py_ssize_t, PyObject *);
 static int     PLy_result_ass_slice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
 
 
+/* handling of SPI operations inside subtransactions */
+static void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner);
+static void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner);
+static void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner);
+
+/* SPI operations */
 static PyObject *PLy_spi_prepare(PyObject *, PyObject *);
 static PyObject *PLy_spi_execute(PyObject *, PyObject *);
 static PyObject *PLy_spi_execute_query(char *query, long limit);
@@ -3390,6 +3396,90 @@ PLy_result_ass_slice(PyObject *arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject *
        return rv;
 }
 
+/*
+ * Utilities for running SPI functions in subtransactions.
+ *
+ * Usage:
+ *
+ *  MemoryContext oldcontext = CurrentMemoryContext;
+ *  ResourceOwner oldowner = CurrentResourceOwner;
+ *
+ *  PLy_spi_subtransaction_begin(oldcontext, oldowner);
+ *  PG_TRY();
+ *  {
+ *      <call SPI functions>
+ *      PLy_spi_subtransaction_commit(oldcontext, oldowner);
+ *  }
+ *  PG_CATCH();
+ *  {
+ *      <do cleanup>
+ *      PLy_spi_subtransaction_abort(oldcontext, oldowner);
+ *      return NULL;
+ *  }
+ *  PG_END_TRY();
+ *
+ * These utilities take care of restoring connection to the SPI manager and
+ * setting a Python exception in case of an abort.
+ */
+static void
+PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
+{
+       BeginInternalSubTransaction(NULL);
+       /* Want to run inside function's memory context */
+       MemoryContextSwitchTo(oldcontext);
+}
+
+static void
+PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
+{
+       /* 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();
+}
+
+static void
+PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
+{
+       ErrorData  *edata;
+       PLyExceptionEntry *entry;
+       PyObject   *exc;
+
+       /* Save error info */
+       MemoryContextSwitchTo(oldcontext);
+       edata = CopyErrorData();
+       FlushErrorState();
+
+       /* 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();
+
+       /* Look up the correct exception */
+       entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
+                                               HASH_FIND, NULL);
+       /* We really should find it, but just in case have a fallback */
+       Assert(entry != NULL);
+       exc = entry ? entry->exc : PLy_exc_spi_error;
+       /* Make Python raise the exception */
+       PLy_spi_exception_set(exc, edata);
+       FreeErrorData(edata);
+}
+
+
 /* SPI interface */
 static PyObject *
 PLy_spi_prepare(PyObject *self, PyObject *args)
@@ -3425,8 +3515,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -3504,50 +3593,14 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
                if (SPI_keepplan(plan->plan))
                        elog(ERROR, "SPI_keepplan failed");
 
-               /* 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
                Py_DECREF(plan);
                Py_XDECREF(optr);
 
-               /* 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -3626,9 +3679,7 @@ 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);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -3683,28 +3734,11 @@ 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
                int                     k;
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
 
                /*
                 * cleanup plan->values array
@@ -3719,26 +3753,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
                        }
                }
 
-               /* 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -3775,9 +3790,7 @@ PLy_spi_execute_query(char *query, long limit)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       /* Want to run inside function's memory context */
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -3785,48 +3798,11 @@ PLy_spi_execute_query(char *query, long limit)
                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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
-
-               /* 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &edata->sqlerrcode,
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -3944,8 +3920,7 @@ PLy_cursor_query(const char *query)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -3969,50 +3944,11 @@ PLy_cursor_query(const char *query)
 
                cursor->portalname = PLy_strdup(portal->name);
 
-               /* 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
-
-               /* Abort the inner transaction */
-               RollbackAndReleaseCurrentSubTransaction();
-               MemoryContextSwitchTo(oldcontext);
-               CurrentResourceOwner = oldowner;
-
-               Py_DECREF(cursor);
-
-               /*
-                * 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -4072,8 +4008,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -4130,28 +4065,11 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
 
                cursor->portalname = PLy_strdup(portal->name);
 
-               /* 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
                int                     k;
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
 
                /* cleanup plan->values array */
                for (k = 0; k < nargs; k++)
@@ -4164,28 +4082,9 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
                        }
                }
 
-               /* Abort the inner transaction */
-               RollbackAndReleaseCurrentSubTransaction();
-               MemoryContextSwitchTo(oldcontext);
-               CurrentResourceOwner = oldowner;
-
                Py_DECREF(cursor);
 
-               /*
-                * 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -4255,8 +4154,7 @@ PLy_cursor_iternext(PyObject *self)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -4277,50 +4175,13 @@ PLy_cursor_iternext(PyObject *self)
 
                SPI_freetuptable(SPI_tuptable);
 
-               /* 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
-
-               /* Abort the inner transaction */
-               RollbackAndReleaseCurrentSubTransaction();
-               MemoryContextSwitchTo(oldcontext);
-               CurrentResourceOwner = oldowner;
-
                SPI_freetuptable(SPI_tuptable);
 
-               /*
-                * 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &edata->sqlerrcode,
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();
@@ -4364,8 +4225,7 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
        oldcontext = CurrentMemoryContext;
        oldowner = CurrentResourceOwner;
 
-       BeginInternalSubTransaction(NULL);
-       MemoryContextSwitchTo(oldcontext);
+       PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
        PG_TRY();
        {
@@ -4398,50 +4258,13 @@ PLy_cursor_fetch(PyObject *self, PyObject *args)
 
                SPI_freetuptable(SPI_tuptable);
 
-               /* 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();
+               PLy_spi_subtransaction_commit(oldcontext, oldowner);
        }
        PG_CATCH();
        {
-               ErrorData  *edata;
-               PLyExceptionEntry *entry;
-               PyObject   *exc;
-
-               /* Save error info */
-               MemoryContextSwitchTo(oldcontext);
-               edata = CopyErrorData();
-               FlushErrorState();
-
-               /* Abort the inner transaction */
-               RollbackAndReleaseCurrentSubTransaction();
-               MemoryContextSwitchTo(oldcontext);
-               CurrentResourceOwner = oldowner;
-
                SPI_freetuptable(SPI_tuptable);
 
-               /*
-                * 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();
-
-               /* Look up the correct exception */
-               entry = hash_search(PLy_spi_exceptions, &edata->sqlerrcode,
-                                                       HASH_FIND, NULL);
-               /* We really should find it, but just in case have a fallback */
-               Assert(entry != NULL);
-               exc = entry ? entry->exc : PLy_exc_spi_error;
-               /* Make Python raise the exception */
-               PLy_spi_exception_set(exc, edata);
+               PLy_spi_subtransaction_abort(oldcontext, oldowner);
                return NULL;
        }
        PG_END_TRY();