Age Owner Branch data TLA 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
5011 peter_e@gmx.net 58 :CBC 23 : PLy_subtransaction_init_type(void)
59 : : {
178 peter@eisentraut.org 60 : 23 : PLy_SubtransactionType = (PyTypeObject *) PyType_FromSpec(&PLySubtransaction_spec);
61 [ - + ]: 23 : if (!PLy_SubtransactionType)
5011 peter_e@gmx.net 62 [ # # ]:UBC 0 : elog(ERROR, "could not initialize PLy_SubtransactionType");
5011 peter_e@gmx.net 63 :CBC 23 : }
64 : :
65 : : /* s = plpy.subtransaction() */
66 : : PyObject *
67 : 29 : PLy_subtransaction_new(PyObject *self, PyObject *unused)
68 : : {
69 : : PLySubtransactionObject *ob;
70 : :
178 peter@eisentraut.org 71 : 29 : ob = PyObject_New(PLySubtransactionObject, PLy_SubtransactionType);
5011 peter_e@gmx.net 72 [ - + ]: 29 : if (ob == NULL)
5011 peter_e@gmx.net 73 :UBC 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 : :
5011 peter_e@gmx.net 79 :CBC 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 : : {
5011 peter_e@gmx.net 108 :UBC 0 : PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
109 : 0 : return NULL;
110 : : }
111 : :
5011 peter_e@gmx.net 112 :CBC 27 : subxact->started = true;
113 : 27 : oldcontext = CurrentMemoryContext;
114 : :
115 : : subxactdata = (PLySubtransactionData *)
3593 tgl@sss.pgh.pa.us 116 : 27 : MemoryContextAlloc(TopTransactionContext,
117 : : sizeof(PLySubtransactionData));
118 : :
5011 peter_e@gmx.net 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 */
3593 tgl@sss.pgh.pa.us 125 : 27 : MemoryContextSwitchTo(TopTransactionContext);
5011 peter_e@gmx.net 126 : 27 : explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
127 : :
128 : : /* Caller wants to stay in original memory context */
3593 tgl@sss.pgh.pa.us 129 : 27 : MemoryContextSwitchTo(oldcontext);
130 : :
131 : : Py_INCREF(self);
5011 peter_e@gmx.net 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))
5011 peter_e@gmx.net 156 :UBC 0 : return NULL;
157 : :
5011 peter_e@gmx.net 158 [ + + ]:CBC 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 : : {
5011 peter_e@gmx.net 172 :UBC 0 : PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
173 : 0 : return NULL;
174 : : }
175 : :
5011 peter_e@gmx.net 176 :CBC 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;
3593 tgl@sss.pgh.pa.us 193 : 21 : pfree(subxactdata);
194 : :
2899 peter_e@gmx.net 195 : 21 : Py_RETURN_NONE;
196 : : }
|