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 	.tp_name = "PLySubtransaction",
42 	.tp_basicsize = sizeof(PLySubtransactionObject),
43 	.tp_dealloc = PLy_subtransaction_dealloc,
44 	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
45 	.tp_doc = PLy_subtransaction_doc,
46 	.tp_methods = PLy_subtransaction_methods,
47 };
48 
49 
50 void
51 PLy_subtransaction_init_type(void)
52 {
53 	if (PyType_Ready(&PLy_SubtransactionType) < 0)
54 		elog(ERROR, "could not initialize PLy_SubtransactionType");
55 }
56 
57 /* s = plpy.subtransaction() */
58 PyObject *
59 PLy_subtransaction_new(PyObject *self, PyObject *unused)
60 {
61 	PLySubtransactionObject *ob;
62 
63 	ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);
64 
65 	if (ob == NULL)
66 		return NULL;
67 
68 	ob->started = false;
69 	ob->exited = false;
70 
71 	return (PyObject *) ob;
72 }
73 
74 /* Python requires a dealloc function to be defined */
75 static void
76 PLy_subtransaction_dealloc(PyObject *subxact)
77 {
78 }
79 
80 /*
81  * subxact.__enter__() or subxact.enter()
82  *
83  * Start an explicit subtransaction.  SPI calls within an explicit
84  * subtransaction will not start another one, so you can atomically
85  * execute many SPI calls and still get a controllable exception if
86  * one of them fails.
87  */
88 static PyObject *
89 PLy_subtransaction_enter(PyObject *self, PyObject *unused)
90 {
91 	PLySubtransactionData *subxactdata;
92 	MemoryContext oldcontext;
93 	PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
94 
95 	if (subxact->started)
96 	{
97 		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
98 		return NULL;
99 	}
100 
101 	if (subxact->exited)
102 	{
103 		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
104 		return NULL;
105 	}
106 
107 	subxact->started = true;
108 	oldcontext = CurrentMemoryContext;
109 
110 	subxactdata = (PLySubtransactionData *)
111 		MemoryContextAlloc(TopTransactionContext,
112 						   sizeof(PLySubtransactionData));
113 
114 	subxactdata->oldcontext = oldcontext;
115 	subxactdata->oldowner = CurrentResourceOwner;
116 
117 	BeginInternalSubTransaction(NULL);
118 
119 	/* Be sure that cells of explicit_subtransactions list are long-lived */
120 	MemoryContextSwitchTo(TopTransactionContext);
121 	explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
122 
123 	/* Caller wants to stay in original memory context */
124 	MemoryContextSwitchTo(oldcontext);
125 
126 	Py_INCREF(self);
127 	return self;
128 }
129 
130 /*
131  * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
132  *
133  * Exit an explicit subtransaction. exc_type is an exception type, exc
134  * is the exception object, tb is the traceback.  If exc_type is None,
135  * commit the subtransactiony, if not abort it.
136  *
137  * The method signature is chosen to allow subtransaction objects to
138  * be used as context managers as described in
139  * <http://www.python.org/dev/peps/pep-0343/>.
140  */
141 static PyObject *
142 PLy_subtransaction_exit(PyObject *self, PyObject *args)
143 {
144 	PyObject   *type;
145 	PyObject   *value;
146 	PyObject   *traceback;
147 	PLySubtransactionData *subxactdata;
148 	PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
149 
150 	if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
151 		return NULL;
152 
153 	if (!subxact->started)
154 	{
155 		PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
156 		return NULL;
157 	}
158 
159 	if (subxact->exited)
160 	{
161 		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
162 		return NULL;
163 	}
164 
165 	if (explicit_subtransactions == NIL)
166 	{
167 		PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
168 		return NULL;
169 	}
170 
171 	subxact->exited = true;
172 
173 	if (type != Py_None)
174 	{
175 		/* Abort the inner transaction */
176 		RollbackAndReleaseCurrentSubTransaction();
177 	}
178 	else
179 	{
180 		ReleaseCurrentSubTransaction();
181 	}
182 
183 	subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
184 	explicit_subtransactions = list_delete_first(explicit_subtransactions);
185 
186 	MemoryContextSwitchTo(subxactdata->oldcontext);
187 	CurrentResourceOwner = subxactdata->oldowner;
188 	pfree(subxactdata);
189 
190 	Py_RETURN_NONE;
191 }
192