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