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