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);
    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)
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
        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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   /* Want to run inside function's memory context */
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
        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
            }
        }
 
-       /* 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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   /* Want to run inside function's memory context */
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
        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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
 
        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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
 
        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++)
            }
        }
 
-       /* 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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
 
        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();
    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;
 
-   BeginInternalSubTransaction(NULL);
-   MemoryContextSwitchTo(oldcontext);
+   PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
    PG_TRY();
    {
 
        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();