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 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 46 : PLy_subtransaction_init_type(void)
59 : {
60 46 : PLy_SubtransactionType = (PyTypeObject *) PyType_FromSpec(&PLySubtransaction_spec);
61 46 : if (!PLy_SubtransactionType)
62 0 : elog(ERROR, "could not initialize PLy_SubtransactionType");
63 46 : }
64 :
65 : /* s = plpy.subtransaction() */
66 : PyObject *
67 58 : PLy_subtransaction_new(PyObject *self, PyObject *unused)
68 : {
69 : PLySubtransactionObject *ob;
70 :
71 58 : ob = PyObject_New(PLySubtransactionObject, PLy_SubtransactionType);
72 58 : 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 58 : ob->started = false;
80 58 : ob->exited = false;
81 :
82 58 : 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 58 : PLy_subtransaction_enter(PyObject *self, PyObject *unused)
95 : {
96 : PLySubtransactionData *subxactdata;
97 : MemoryContext oldcontext;
98 58 : PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
99 :
100 58 : if (subxact->started)
101 : {
102 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
103 4 : return NULL;
104 : }
105 :
106 54 : if (subxact->exited)
107 : {
108 0 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
109 0 : return NULL;
110 : }
111 :
112 54 : subxact->started = true;
113 54 : oldcontext = CurrentMemoryContext;
114 :
115 : subxactdata = (PLySubtransactionData *)
116 54 : MemoryContextAlloc(TopTransactionContext,
117 : sizeof(PLySubtransactionData));
118 :
119 54 : subxactdata->oldcontext = oldcontext;
120 54 : subxactdata->oldowner = CurrentResourceOwner;
121 :
122 54 : BeginInternalSubTransaction(NULL);
123 :
124 : /* Be sure that cells of explicit_subtransactions list are long-lived */
125 54 : MemoryContextSwitchTo(TopTransactionContext);
126 54 : explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
127 :
128 : /* Caller wants to stay in original memory context */
129 54 : MemoryContextSwitchTo(oldcontext);
130 :
131 54 : Py_INCREF(self);
132 54 : 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 50 : PLy_subtransaction_exit(PyObject *self, PyObject *args)
148 : {
149 : PyObject *type;
150 : PyObject *value;
151 : PyObject *traceback;
152 : PLySubtransactionData *subxactdata;
153 50 : PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
154 :
155 50 : if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
156 0 : return NULL;
157 :
158 50 : if (!subxact->started)
159 : {
160 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
161 4 : return NULL;
162 : }
163 :
164 46 : if (subxact->exited)
165 : {
166 4 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
167 4 : return NULL;
168 : }
169 :
170 42 : 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 42 : subxact->exited = true;
177 :
178 42 : if (type != Py_None)
179 : {
180 : /* Abort the inner transaction */
181 22 : RollbackAndReleaseCurrentSubTransaction();
182 : }
183 : else
184 : {
185 20 : ReleaseCurrentSubTransaction();
186 : }
187 :
188 42 : subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
189 42 : explicit_subtransactions = list_delete_first(explicit_subtransactions);
190 :
191 42 : MemoryContextSwitchTo(subxactdata->oldcontext);
192 42 : CurrentResourceOwner = subxactdata->oldowner;
193 42 : pfree(subxactdata);
194 :
195 42 : Py_RETURN_NONE;
196 : }
|