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 "executor/spi.h"
11 #include "utils/memutils.h"
12
13 #include "plpython.h"
14
15 #include "plpy_subxactobject.h"
16
17 #include "plpy_elog.h"
18
19
20 List *explicit_subtransactions = NIL;
21
22
23 static void PLy_subtransaction_dealloc(PyObject *subxact);
24 static PyObject *PLy_subtransaction_enter(PyObject *self, PyObject *unused);
25 static PyObject *PLy_subtransaction_exit(PyObject *self, PyObject *args);
26
27 static char PLy_subtransaction_doc[] = {
28 "PostgreSQL subtransaction context manager"
29 };
30
31 static PyMethodDef PLy_subtransaction_methods[] = {
32 {"__enter__", PLy_subtransaction_enter, METH_VARARGS, NULL},
33 {"__exit__", PLy_subtransaction_exit, METH_VARARGS, NULL},
34 /* user-friendly names for Python <2.6 */
35 {"enter", PLy_subtransaction_enter, METH_VARARGS, NULL},
36 {"exit", PLy_subtransaction_exit, METH_VARARGS, NULL},
37 {NULL, NULL, 0, NULL}
38 };
39
40 static PyTypeObject PLy_SubtransactionType = {
41 PyVarObject_HEAD_INIT(NULL, 0)
42 "PLySubtransaction", /* tp_name */
43 sizeof(PLySubtransactionObject), /* tp_size */
44 0, /* tp_itemsize */
45
46 /*
47 * methods
48 */
49 PLy_subtransaction_dealloc, /* tp_dealloc */
50 0, /* tp_print */
51 0, /* tp_getattr */
52 0, /* tp_setattr */
53 0, /* tp_compare */
54 0, /* tp_repr */
55 0, /* tp_as_number */
56 0, /* tp_as_sequence */
57 0, /* tp_as_mapping */
58 0, /* tp_hash */
59 0, /* tp_call */
60 0, /* tp_str */
61 0, /* tp_getattro */
62 0, /* tp_setattro */
63 0, /* tp_as_buffer */
64 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
65 PLy_subtransaction_doc, /* tp_doc */
66 0, /* tp_traverse */
67 0, /* tp_clear */
68 0, /* tp_richcompare */
69 0, /* tp_weaklistoffset */
70 0, /* tp_iter */
71 0, /* tp_iternext */
72 PLy_subtransaction_methods, /* tp_tpmethods */
73 };
74
75
76 void
PLy_subtransaction_init_type(void)77 PLy_subtransaction_init_type(void)
78 {
79 if (PyType_Ready(&PLy_SubtransactionType) < 0)
80 elog(ERROR, "could not initialize PLy_SubtransactionType");
81 }
82
83 /* s = plpy.subtransaction() */
84 PyObject *
PLy_subtransaction_new(PyObject * self,PyObject * unused)85 PLy_subtransaction_new(PyObject *self, PyObject *unused)
86 {
87 PLySubtransactionObject *ob;
88
89 ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);
90
91 if (ob == NULL)
92 return NULL;
93
94 ob->started = false;
95 ob->exited = false;
96
97 return (PyObject *) ob;
98 }
99
100 /* Python requires a dealloc function to be defined */
101 static void
PLy_subtransaction_dealloc(PyObject * subxact)102 PLy_subtransaction_dealloc(PyObject *subxact)
103 {
104 }
105
106 /*
107 * subxact.__enter__() or subxact.enter()
108 *
109 * Start an explicit subtransaction. SPI calls within an explicit
110 * subtransaction will not start another one, so you can atomically
111 * execute many SPI calls and still get a controllable exception if
112 * one of them fails.
113 */
114 static PyObject *
PLy_subtransaction_enter(PyObject * self,PyObject * unused)115 PLy_subtransaction_enter(PyObject *self, PyObject *unused)
116 {
117 PLySubtransactionData *subxactdata;
118 MemoryContext oldcontext;
119 PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
120
121 if (subxact->started)
122 {
123 PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
124 return NULL;
125 }
126
127 if (subxact->exited)
128 {
129 PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
130 return NULL;
131 }
132
133 subxact->started = true;
134 oldcontext = CurrentMemoryContext;
135
136 subxactdata = (PLySubtransactionData *)
137 MemoryContextAlloc(TopTransactionContext,
138 sizeof(PLySubtransactionData));
139
140 subxactdata->oldcontext = oldcontext;
141 subxactdata->oldowner = CurrentResourceOwner;
142
143 BeginInternalSubTransaction(NULL);
144
145 /* Be sure that cells of explicit_subtransactions list are long-lived */
146 MemoryContextSwitchTo(TopTransactionContext);
147 explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
148
149 /* Caller wants to stay in original memory context */
150 MemoryContextSwitchTo(oldcontext);
151
152 Py_INCREF(self);
153 return self;
154 }
155
156 /*
157 * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
158 *
159 * Exit an explicit subtransaction. exc_type is an exception type, exc
160 * is the exception object, tb is the traceback. If exc_type is None,
161 * commit the subtransactiony, if not abort it.
162 *
163 * The method signature is chosen to allow subtransaction objects to
164 * be used as context managers as described in
165 * <http://www.python.org/dev/peps/pep-0343/>.
166 */
167 static PyObject *
PLy_subtransaction_exit(PyObject * self,PyObject * args)168 PLy_subtransaction_exit(PyObject *self, PyObject *args)
169 {
170 PyObject *type;
171 PyObject *value;
172 PyObject *traceback;
173 PLySubtransactionData *subxactdata;
174 PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
175
176 if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
177 return NULL;
178
179 if (!subxact->started)
180 {
181 PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
182 return NULL;
183 }
184
185 if (subxact->exited)
186 {
187 PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
188 return NULL;
189 }
190
191 if (explicit_subtransactions == NIL)
192 {
193 PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
194 return NULL;
195 }
196
197 subxact->exited = true;
198
199 if (type != Py_None)
200 {
201 /* Abort the inner transaction */
202 RollbackAndReleaseCurrentSubTransaction();
203 }
204 else
205 {
206 ReleaseCurrentSubTransaction();
207 }
208
209 subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
210 explicit_subtransactions = list_delete_first(explicit_subtransactions);
211
212 MemoryContextSwitchTo(subxactdata->oldcontext);
213 CurrentResourceOwner = subxactdata->oldowner;
214 pfree(subxactdata);
215
216 /*
217 * AtEOSubXact_SPI() should not have popped any SPI context, but just in
218 * case it did, make sure we remain connected.
219 */
220 SPI_restore_connection();
221
222 Py_INCREF(Py_None);
223 return Py_None;
224 }
225