Line data Source code
1 : /*
2 : * the PLySubtransaction class
3 : *
4 : * src/pl/plpython/plpy_subxactobject.c
5 : */
6 :
7 : #include "postgres.h"
8 :
9 : #include "access/xact.h"
10 : #include "plpy_elog.h"
11 : #include "plpy_subxactobject.h"
12 : #include "plpython.h"
13 : #include "utils/memutils.h"
14 :
15 : List *explicit_subtransactions = NIL;
16 :
17 :
18 : static void PLy_subtransaction_dealloc(PyObject *subxact);
19 : static PyObject *PLy_subtransaction_enter(PyObject *self, PyObject *unused);
20 : static PyObject *PLy_subtransaction_exit(PyObject *self, PyObject *args);
21 :
22 : static char PLy_subtransaction_doc[] =
23 : "PostgreSQL subtransaction context manager";
24 :
25 : static PyMethodDef PLy_subtransaction_methods[] = {
26 : {"__enter__", PLy_subtransaction_enter, METH_VARARGS, NULL},
27 : {"__exit__", PLy_subtransaction_exit, METH_VARARGS, NULL},
28 : /* user-friendly names for Python <2.6 */
29 : {"enter", PLy_subtransaction_enter, METH_VARARGS, NULL},
30 : {"exit", PLy_subtransaction_exit, METH_VARARGS, NULL},
31 : {NULL, NULL, 0, NULL}
32 : };
33 :
34 : static PyTypeObject PLy_SubtransactionType = {
35 : PyVarObject_HEAD_INIT(NULL, 0)
36 : .tp_name = "PLySubtransaction",
37 : .tp_basicsize = sizeof(PLySubtransactionObject),
38 : .tp_dealloc = PLy_subtransaction_dealloc,
39 : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
40 : .tp_doc = PLy_subtransaction_doc,
41 : .tp_methods = PLy_subtransaction_methods,
42 : };
43 :
44 :
45 : void
46 46 : PLy_subtransaction_init_type(void)
47 : {
48 46 : if (PyType_Ready(&PLy_SubtransactionType) < 0)
49 0 : elog(ERROR, "could not initialize PLy_SubtransactionType");
50 46 : }
51 :
52 : /* s = plpy.subtransaction() */
53 : PyObject *
54 58 : PLy_subtransaction_new(PyObject *self, PyObject *unused)
55 : {
56 : PLySubtransactionObject *ob;
57 :
58 58 : ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);
59 :
60 58 : if (ob == NULL)
61 0 : return NULL;
62 :
63 58 : ob->started = false;
64 58 : ob->exited = false;
65 :
66 58 : return (PyObject *) ob;
67 : }
68 :
69 : /* Python requires a dealloc function to be defined */
70 : static void
71 58 : PLy_subtransaction_dealloc(PyObject *subxact)
72 : {
73 58 : }
74 :
75 : /*
76 : * subxact.__enter__() or subxact.enter()
77 : *
78 : * Start an explicit subtransaction. SPI calls within an explicit
79 : * subtransaction will not start another one, so you can atomically
80 : * execute many SPI calls and still get a controllable exception if
81 : * one of them fails.
82 : */
83 : static PyObject *
84 58 : PLy_subtransaction_enter(PyObject *self, PyObject *unused)
85 : {
86 : PLySubtransactionData *subxactdata;
87 : MemoryContext oldcontext;
88 58 : PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
89 :
90 58 : if (subxact->started)
91 : {
92 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
93 4 : return NULL;
94 : }
95 :
96 54 : if (subxact->exited)
97 : {
98 0 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
99 0 : return NULL;
100 : }
101 :
102 54 : subxact->started = true;
103 54 : oldcontext = CurrentMemoryContext;
104 :
105 : subxactdata = (PLySubtransactionData *)
106 54 : MemoryContextAlloc(TopTransactionContext,
107 : sizeof(PLySubtransactionData));
108 :
109 54 : subxactdata->oldcontext = oldcontext;
110 54 : subxactdata->oldowner = CurrentResourceOwner;
111 :
112 54 : BeginInternalSubTransaction(NULL);
113 :
114 : /* Be sure that cells of explicit_subtransactions list are long-lived */
115 54 : MemoryContextSwitchTo(TopTransactionContext);
116 54 : explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
117 :
118 : /* Caller wants to stay in original memory context */
119 54 : MemoryContextSwitchTo(oldcontext);
120 :
121 54 : Py_INCREF(self);
122 54 : return self;
123 : }
124 :
125 : /*
126 : * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
127 : *
128 : * Exit an explicit subtransaction. exc_type is an exception type, exc
129 : * is the exception object, tb is the traceback. If exc_type is None,
130 : * commit the subtransaction, if not abort it.
131 : *
132 : * The method signature is chosen to allow subtransaction objects to
133 : * be used as context managers as described in
134 : * <http://www.python.org/dev/peps/pep-0343/>.
135 : */
136 : static PyObject *
137 50 : PLy_subtransaction_exit(PyObject *self, PyObject *args)
138 : {
139 : PyObject *type;
140 : PyObject *value;
141 : PyObject *traceback;
142 : PLySubtransactionData *subxactdata;
143 50 : PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
144 :
145 50 : if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
146 0 : return NULL;
147 :
148 50 : if (!subxact->started)
149 : {
150 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
151 4 : return NULL;
152 : }
153 :
154 46 : if (subxact->exited)
155 : {
156 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
157 4 : return NULL;
158 : }
159 :
160 42 : if (explicit_subtransactions == NIL)
161 : {
162 0 : PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
163 0 : return NULL;
164 : }
165 :
166 42 : subxact->exited = true;
167 :
168 42 : if (type != Py_None)
169 : {
170 : /* Abort the inner transaction */
171 22 : RollbackAndReleaseCurrentSubTransaction();
172 : }
173 : else
174 : {
175 20 : ReleaseCurrentSubTransaction();
176 : }
177 :
178 42 : subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
179 42 : explicit_subtransactions = list_delete_first(explicit_subtransactions);
180 :
181 42 : MemoryContextSwitchTo(subxactdata->oldcontext);
182 42 : CurrentResourceOwner = subxactdata->oldowner;
183 42 : pfree(subxactdata);
184 :
185 42 : Py_RETURN_NONE;
186 : }
|