mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-19 12:56:59 +08:00
The idea behind SPI_push was to allow transitioning back into an "unconnected" state when a SPI-using procedure calls unrelated code that might or might not invoke SPI. That sounds good, but in practice the only thing it does for us is to catch cases where a called SPI-using function forgets to call SPI_connect --- which is a highly improbable failure mode, since it would be exposed immediately by direct testing of said function. As against that, we've had multiple bugs induced by forgetting to call SPI_push/SPI_pop around code that might invoke SPI-using functions; these are much harder to catch and indeed have gone undetected for years in some cases. And we've had to band-aid around some problems of this ilk by introducing conditional push/pop pairs in some places, which really kind of defeats the purpose altogether; if we can't draw bright lines between connected and unconnected code, what's the point? Hence, get rid of SPI_push[_conditional], SPI_pop[_conditional], and the underlying state variable _SPI_curid. It turns out SPI_restore_connection can go away too, which is a nice side benefit since it was never more than a kluge. Provide no-op macros for the deleted functions so as to avoid an API break for external modules. A side effect of this removal is that SPI_palloc and allied functions no longer permit being called when unconnected; they'll throw an error instead. The apparent usefulness of the previous behavior was a mirage as well, because it was depended on by only a few places (which I fixed in preceding commits), and it posed a risk of allocations being unexpectedly long-lived if someone forgot a SPI_push call. Discussion: <20808.1478481403@sss.pgh.pa.us>
218 lines
5.4 KiB
C
218 lines
5.4 KiB
C
/*
|
|
* the PLySubtransaction class
|
|
*
|
|
* src/pl/plpython/plpy_subxactobject.c
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/xact.h"
|
|
#include "utils/memutils.h"
|
|
|
|
#include "plpython.h"
|
|
|
|
#include "plpy_subxactobject.h"
|
|
|
|
#include "plpy_elog.h"
|
|
|
|
|
|
List *explicit_subtransactions = NIL;
|
|
|
|
|
|
static void PLy_subtransaction_dealloc(PyObject *subxact);
|
|
static PyObject *PLy_subtransaction_enter(PyObject *self, PyObject *unused);
|
|
static PyObject *PLy_subtransaction_exit(PyObject *self, PyObject *args);
|
|
|
|
static char PLy_subtransaction_doc[] = {
|
|
"PostgreSQL subtransaction context manager"
|
|
};
|
|
|
|
static PyMethodDef PLy_subtransaction_methods[] = {
|
|
{"__enter__", PLy_subtransaction_enter, METH_VARARGS, NULL},
|
|
{"__exit__", PLy_subtransaction_exit, METH_VARARGS, NULL},
|
|
/* user-friendly names for Python <2.6 */
|
|
{"enter", PLy_subtransaction_enter, METH_VARARGS, NULL},
|
|
{"exit", PLy_subtransaction_exit, METH_VARARGS, NULL},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
static PyTypeObject PLy_SubtransactionType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"PLySubtransaction", /* tp_name */
|
|
sizeof(PLySubtransactionObject), /* tp_size */
|
|
0, /* tp_itemsize */
|
|
|
|
/*
|
|
* methods
|
|
*/
|
|
PLy_subtransaction_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
PLy_subtransaction_doc, /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
PLy_subtransaction_methods, /* tp_tpmethods */
|
|
};
|
|
|
|
|
|
void
|
|
PLy_subtransaction_init_type(void)
|
|
{
|
|
if (PyType_Ready(&PLy_SubtransactionType) < 0)
|
|
elog(ERROR, "could not initialize PLy_SubtransactionType");
|
|
}
|
|
|
|
/* s = plpy.subtransaction() */
|
|
PyObject *
|
|
PLy_subtransaction_new(PyObject *self, PyObject *unused)
|
|
{
|
|
PLySubtransactionObject *ob;
|
|
|
|
ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);
|
|
|
|
if (ob == NULL)
|
|
return NULL;
|
|
|
|
ob->started = false;
|
|
ob->exited = false;
|
|
|
|
return (PyObject *) ob;
|
|
}
|
|
|
|
/* Python requires a dealloc function to be defined */
|
|
static void
|
|
PLy_subtransaction_dealloc(PyObject *subxact)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
|
|
*
|
|
* Exit an explicit subtransaction. exc_type is an exception type, exc
|
|
* is the exception object, tb is the traceback. If exc_type is None,
|
|
* commit the subtransactiony, if not abort it.
|
|
*
|
|
* The method signature is chosen to allow subtransaction objects to
|
|
* be used as context managers as described in
|
|
* <http://www.python.org/dev/peps/pep-0343/>.
|
|
*/
|
|
static PyObject *
|
|
PLy_subtransaction_exit(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *type;
|
|
PyObject *value;
|
|
PyObject *traceback;
|
|
PLySubtransactionData *subxactdata;
|
|
PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
|
|
|
|
if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
|
|
return NULL;
|
|
|
|
if (!subxact->started)
|
|
{
|
|
PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
|
|
return NULL;
|
|
}
|
|
|
|
if (subxact->exited)
|
|
{
|
|
PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
|
|
return NULL;
|
|
}
|
|
|
|
if (explicit_subtransactions == NIL)
|
|
{
|
|
PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
|
|
return NULL;
|
|
}
|
|
|
|
subxact->exited = true;
|
|
|
|
if (type != Py_None)
|
|
{
|
|
/* Abort the inner transaction */
|
|
RollbackAndReleaseCurrentSubTransaction();
|
|
}
|
|
else
|
|
{
|
|
ReleaseCurrentSubTransaction();
|
|
}
|
|
|
|
subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
|
|
explicit_subtransactions = list_delete_first(explicit_subtransactions);
|
|
|
|
MemoryContextSwitchTo(subxactdata->oldcontext);
|
|
CurrentResourceOwner = subxactdata->oldowner;
|
|
pfree(subxactdata);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|