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