
This moves the code around from one huge file into hopefully logical and more manageable modules. For the most part, the code itself was not touched, except: PLy_function_handler and PLy_trigger_handler were renamed to PLy_exec_function and PLy_exec_trigger, because they were not actually handlers in the PL handler sense, and it makes the naming more similar to the way PL/pgSQL is organized. The initialization of the procedure caches was separated into a new function init_procedure_caches to keep the hash tables private to plpy_procedures.c. Jan Urbański and Peter Eisentraut
218 lines
5.3 KiB
C
218 lines
5.3 KiB
C
/*
|
|
* the PLySubtransaction class
|
|
*
|
|
* src/pl/plpython/plpy_subxactobject.c
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/xact.h"
|
|
#include "executor/spi.h"
|
|
|
|
#include "plpython.h"
|
|
|
|
#include "plpy_subxactobject.h"
|
|
|
|
#include "plpy_elog.h"
|
|
|
|
|
|
List *explicit_subtransactions = NIL;
|
|
|
|
|
|
static void PLy_subtransaction_dealloc(PyObject *);
|
|
static PyObject *PLy_subtransaction_enter(PyObject *, PyObject *);
|
|
static PyObject *PLy_subtransaction_exit(PyObject *, PyObject *);
|
|
|
|
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 = PLy_malloc(sizeof(*subxactdata));
|
|
subxactdata->oldcontext = oldcontext;
|
|
subxactdata->oldowner = CurrentResourceOwner;
|
|
|
|
BeginInternalSubTransaction(NULL);
|
|
/* Do not want to leave the previous memory context */
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
|
|
|
|
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;
|
|
PLy_free(subxactdata);
|
|
|
|
/*
|
|
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
|
|
* case it did, make sure we remain connected.
|
|
*/
|
|
SPI_restore_connection();
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|