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