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 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 * 84 PLy_subtransaction_new(PyObject *self, PyObject *unused) 85 { 86 PLySubtransactionObject *ob; clone(&self) -> Self87 88 ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType); 89 90 if (ob == NULL) 91 return NULL; 92 93 ob->started = false; clone_from(&mut self, other: &Self)94 ob->exited = false; 95 96 return (PyObject *) ob; 97 } 98 99 /* Python requires a dealloc function to be defined */ 100 static void 101 PLy_subtransaction_dealloc(PyObject *subxact) 102 { 103 } into_entries(self) -> Vec<Self::Entry>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 * 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 /* with_capacity(n: usize) -> Self156 * 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 * 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_RETURN_NONE; 216 } 217