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