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();