/* * 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. */ void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner) { BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); }
bool pl_ist_begin(char state) { bool r = true; PG_TRY(); { /* Prevent wrap around */ if (pl_ist_count + 1 == 0) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("too many internal subtransactions"), errdetail("Subtransaction %lu was expected to exit next.", pl_ist_count) )); } if (state == pl_ist_committed) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot start a committed subtransaction") )); } if (state == pl_ist_aborted) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot start an aborted subtransaction") )); } if (state == pl_ist_open) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("subtransaction is already open") )); } BeginInternalSubTransaction(NULL); pl_ist_count = pl_ist_count + 1; } PG_CATCH(); { PyErr_SetPgError(true); pl_state = pl_in_failed_transaction; r = false; } PG_END_TRY(); return(r); }
/* * subxact.__enter__() or subxact.enter() * * Start an explicit subtransaction. SPI calls within an explicit * subtransaction will not start another one, so you can atomically * execute many SPI calls and still get a controllable exception if * one of them fails. */ static PyObject * PLy_subtransaction_enter(PyObject *self, PyObject *unused) { PLySubtransactionData *subxactdata; MemoryContext oldcontext; PLySubtransactionObject *subxact = (PLySubtransactionObject *) self; if (subxact->started) { PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered"); return NULL; } if (subxact->exited) { PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited"); return NULL; } subxact->started = true; oldcontext = CurrentMemoryContext; subxactdata = (PLySubtransactionData *) MemoryContextAlloc(TopTransactionContext, sizeof(PLySubtransactionData)); subxactdata->oldcontext = oldcontext; subxactdata->oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Be sure that cells of explicit_subtransactions list are long-lived */ MemoryContextSwitchTo(TopTransactionContext); explicit_subtransactions = lcons(subxactdata, explicit_subtransactions); /* Caller wants to stay in original memory context */ MemoryContextSwitchTo(oldcontext); Py_INCREF(self); return self; }
SV * plperl_spi_fetchrow(char *cursor) { SV *row; /* * Execute the FETCH inside a sub-transaction, so we can cope with errors * sanely */ MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { Portal p = SPI_cursor_find(cursor); if (!p) row = newSV(0); else { SPI_cursor_fetch(p, true, 1); if (SPI_processed == 0) { SPI_cursor_close(p); row = newSV(0); } else { row = plperl_hash_from_tuple(SPI_tuptable->vals[0], SPI_tuptable->tupdesc); } 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(); } PG_CATCH(); { ErrorData *edata; /* 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(); /* Punt the error to Perl */ croak("%s", edata->message); /* Can't get here, but keep compiler quiet */ return NULL; } PG_END_TRY(); return row; }
SV * plperl_spi_query(char *query) { SV *cursor; /* * Execute the query inside a sub-transaction, so we can cope with errors * sanely */ MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { void *plan; Portal portal = NULL; /* Create a cursor for the query */ plan = SPI_prepare(query, 0, NULL); if (plan) portal = SPI_cursor_open(NULL, plan, NULL, NULL, false); if (portal) cursor = newSVpv(portal->name, 0); else cursor = newSV(0); /* 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(); /* 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(); /* Punt the error to Perl */ croak("%s", edata->message); /* Can't get here, but keep compiler quiet */ return NULL; } PG_END_TRY(); return cursor; }
HV * plperl_spi_exec(char *query, int limit) { HV *ret_hv; /* * Execute the query inside a sub-transaction, so we can cope with errors * sanely */ MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { int spi_rv; spi_rv = SPI_execute(query, plperl_current_prodesc->fn_readonly, limit); ret_hv = plperl_spi_execute_fetch_result(SPI_tuptable, SPI_processed, spi_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(); /* 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(); /* Punt the error to Perl */ croak("%s", edata->message); /* Can't get here, but keep compiler quiet */ return NULL; } PG_END_TRY(); return ret_hv; }
static void test_spi_exec_utility(int *passed, int *total) { int rc; volatile int caseno = 0; /* Initialize */ rc = SPI_connect(); if (rc != SPI_OK_CONNECT) elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc)); /* * *-*-1 * - query error */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { spi_exec_utility("RESET dummy_parameter"); elog(WARNING, "*-*-%d failed", caseno); ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; RollbackAndReleaseCurrentSubTransaction(); FlushErrorState(); SPI_restore_connection(); } PG_END_TRY(); /* * *-*-2 * - query success */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { spi_exec_utility("RESET client_min_messages"); elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "*-*-%d failed", caseno); RollbackAndReleaseCurrentSubTransaction(); SPI_restore_connection(); } PG_END_TRY(); /* report # of tests */ (*total) += caseno; /* Cleanup */ rc = SPI_finish(); if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED) elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc)); }
static void test_spi_exec_query(int *passed, int *total) { int rc; volatile int caseno = 0; SPIPlanPtr ptr = NULL; SPIPlanPtr org_ptr; /* Initialize */ rc = SPI_connect(); if (rc != SPI_OK_CONNECT) elog(ERROR, "could not connect SPI: %s", SPI_result_code_string(rc)); /* * *-*-1 * - plan is not cached */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT); if (ptr != NULL && SPI_processed == 1) { elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; } ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "*-*-%d failed", caseno); RollbackAndReleaseCurrentSubTransaction(); SPI_restore_connection(); } PG_END_TRY(); /* * *-*-2 * - plan is cached */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { org_ptr = ptr; spi_exec_query(NULL, 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT); if (ptr == org_ptr && SPI_processed == 1) { elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; } ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "*-*-%d failed", caseno); RollbackAndReleaseCurrentSubTransaction(); FlushErrorState(); SPI_restore_connection(); } PG_END_TRY(); SPI_freeplan(ptr); ptr = NULL; /* * *-*-3 * - query error */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { spi_exec_query("SELECT 1 / 0", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT); elog(WARNING, "*-*-%d failed", caseno); ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; RollbackAndReleaseCurrentSubTransaction(); FlushErrorState(); SPI_restore_connection(); } PG_END_TRY(); SPI_freeplan(ptr); ptr = NULL; /* * *-*-4 * - query success */ caseno++; BeginInternalSubTransaction("test"); PG_TRY(); { spi_exec_query("SELECT 1", 0, NULL, &ptr, NULL, NULL, SPI_OK_SELECT); if (ptr != NULL && SPI_processed == 1) { elog(WARNING, "%s-%d ok", __FUNCTION__, caseno); (*passed)++; } ReleaseCurrentSubTransaction(); } PG_CATCH(); { elog(WARNING, "*-*-%d failed", caseno); PG_RE_THROW(); RollbackAndReleaseCurrentSubTransaction(); SPI_restore_connection(); } PG_END_TRY(); SPI_freeplan(ptr); ptr = NULL; /* report # of tests */ (*total) += caseno; /* Cleanup */ rc = SPI_finish(); if (rc != SPI_OK_FINISH && rc != SPI_ERROR_UNCONNECTED) elog(ERROR, "could not finish SPI: %s", SPI_result_code_string(rc)); }