LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_subxactobject.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 47 54 87.0 %
Date: 2025-04-02 15:15:55 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "plpy_elog.h"
      11             : #include "plpy_subxactobject.h"
      12             : #include "plpython.h"
      13             : #include "utils/memutils.h"
      14             : 
      15             : List       *explicit_subtransactions = NIL;
      16             : 
      17             : 
      18             : static PyObject *PLy_subtransaction_enter(PyObject *self, PyObject *unused);
      19             : static PyObject *PLy_subtransaction_exit(PyObject *self, PyObject *args);
      20             : 
      21             : static char PLy_subtransaction_doc[] =
      22             : "PostgreSQL subtransaction context manager";
      23             : 
      24             : static PyMethodDef PLy_subtransaction_methods[] = {
      25             :     {"__enter__", PLy_subtransaction_enter, METH_VARARGS, NULL},
      26             :     {"__exit__", PLy_subtransaction_exit, METH_VARARGS, NULL},
      27             :     /* user-friendly names for Python <2.6 */
      28             :     {"enter", PLy_subtransaction_enter, METH_VARARGS, NULL},
      29             :     {"exit", PLy_subtransaction_exit, METH_VARARGS, NULL},
      30             :     {NULL, NULL, 0, NULL}
      31             : };
      32             : 
      33             : static PyType_Slot PLySubtransaction_slots[] =
      34             : {
      35             :     {
      36             :         Py_tp_doc, (char *) PLy_subtransaction_doc
      37             :     },
      38             :     {
      39             :         Py_tp_methods, PLy_subtransaction_methods
      40             :     },
      41             :     {
      42             :         0, NULL
      43             :     }
      44             : };
      45             : 
      46             : static PyType_Spec PLySubtransaction_spec =
      47             : {
      48             :     .name = "PLySubtransaction",
      49             :         .basicsize = sizeof(PLySubtransactionObject),
      50             :         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
      51             :         .slots = PLySubtransaction_slots,
      52             : };
      53             : 
      54             : static PyTypeObject *PLy_SubtransactionType;
      55             : 
      56             : 
      57             : void
      58          46 : PLy_subtransaction_init_type(void)
      59             : {
      60          46 :     PLy_SubtransactionType = (PyTypeObject *) PyType_FromSpec(&PLySubtransaction_spec);
      61          46 :     if (!PLy_SubtransactionType)
      62           0 :         elog(ERROR, "could not initialize PLy_SubtransactionType");
      63          46 : }
      64             : 
      65             : /* s = plpy.subtransaction() */
      66             : PyObject *
      67          58 : PLy_subtransaction_new(PyObject *self, PyObject *unused)
      68             : {
      69             :     PLySubtransactionObject *ob;
      70             : 
      71          58 :     ob = PyObject_New(PLySubtransactionObject, PLy_SubtransactionType);
      72          58 :     if (ob == NULL)
      73           0 :         return NULL;
      74             : #if PY_VERSION_HEX < 0x03080000
      75             :     /* Workaround for Python issue 35810; no longer necessary in Python 3.8 */
      76             :     Py_INCREF(PLy_SubtransactionType);
      77             : #endif
      78             : 
      79          58 :     ob->started = false;
      80          58 :     ob->exited = false;
      81             : 
      82          58 :     return (PyObject *) ob;
      83             : }
      84             : 
      85             : /*
      86             :  * subxact.__enter__() or subxact.enter()
      87             :  *
      88             :  * Start an explicit subtransaction.  SPI calls within an explicit
      89             :  * subtransaction will not start another one, so you can atomically
      90             :  * execute many SPI calls and still get a controllable exception if
      91             :  * one of them fails.
      92             :  */
      93             : static PyObject *
      94          58 : PLy_subtransaction_enter(PyObject *self, PyObject *unused)
      95             : {
      96             :     PLySubtransactionData *subxactdata;
      97             :     MemoryContext oldcontext;
      98          58 :     PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
      99             : 
     100          58 :     if (subxact->started)
     101             :     {
     102           4 :         PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
     103           4 :         return NULL;
     104             :     }
     105             : 
     106          54 :     if (subxact->exited)
     107             :     {
     108           0 :         PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
     109           0 :         return NULL;
     110             :     }
     111             : 
     112          54 :     subxact->started = true;
     113          54 :     oldcontext = CurrentMemoryContext;
     114             : 
     115             :     subxactdata = (PLySubtransactionData *)
     116          54 :         MemoryContextAlloc(TopTransactionContext,
     117             :                            sizeof(PLySubtransactionData));
     118             : 
     119          54 :     subxactdata->oldcontext = oldcontext;
     120          54 :     subxactdata->oldowner = CurrentResourceOwner;
     121             : 
     122          54 :     BeginInternalSubTransaction(NULL);
     123             : 
     124             :     /* Be sure that cells of explicit_subtransactions list are long-lived */
     125          54 :     MemoryContextSwitchTo(TopTransactionContext);
     126          54 :     explicit_subtransactions = lcons(subxactdata, explicit_subtransactions);
     127             : 
     128             :     /* Caller wants to stay in original memory context */
     129          54 :     MemoryContextSwitchTo(oldcontext);
     130             : 
     131          54 :     Py_INCREF(self);
     132          54 :     return self;
     133             : }
     134             : 
     135             : /*
     136             :  * subxact.__exit__(exc_type, exc, tb) or subxact.exit(exc_type, exc, tb)
     137             :  *
     138             :  * Exit an explicit subtransaction. exc_type is an exception type, exc
     139             :  * is the exception object, tb is the traceback.  If exc_type is None,
     140             :  * commit the subtransaction, if not abort it.
     141             :  *
     142             :  * The method signature is chosen to allow subtransaction objects to
     143             :  * be used as context managers as described in
     144             :  * <http://www.python.org/dev/peps/pep-0343/>.
     145             :  */
     146             : static PyObject *
     147          50 : PLy_subtransaction_exit(PyObject *self, PyObject *args)
     148             : {
     149             :     PyObject   *type;
     150             :     PyObject   *value;
     151             :     PyObject   *traceback;
     152             :     PLySubtransactionData *subxactdata;
     153          50 :     PLySubtransactionObject *subxact = (PLySubtransactionObject *) self;
     154             : 
     155          50 :     if (!PyArg_ParseTuple(args, "OOO", &type, &value, &traceback))
     156           0 :         return NULL;
     157             : 
     158          50 :     if (!subxact->started)
     159             :     {
     160           4 :         PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
     161           4 :         return NULL;
     162             :     }
     163             : 
     164          46 :     if (subxact->exited)
     165             :     {
     166           4 :         PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
     167           4 :         return NULL;
     168             :     }
     169             : 
     170          42 :     if (explicit_subtransactions == NIL)
     171             :     {
     172           0 :         PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
     173           0 :         return NULL;
     174             :     }
     175             : 
     176          42 :     subxact->exited = true;
     177             : 
     178          42 :     if (type != Py_None)
     179             :     {
     180             :         /* Abort the inner transaction */
     181          22 :         RollbackAndReleaseCurrentSubTransaction();
     182             :     }
     183             :     else
     184             :     {
     185          20 :         ReleaseCurrentSubTransaction();
     186             :     }
     187             : 
     188          42 :     subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
     189          42 :     explicit_subtransactions = list_delete_first(explicit_subtransactions);
     190             : 
     191          42 :     MemoryContextSwitchTo(subxactdata->oldcontext);
     192          42 :     CurrentResourceOwner = subxactdata->oldowner;
     193          42 :     pfree(subxactdata);
     194             : 
     195          42 :     Py_RETURN_NONE;
     196             : }

Generated by: LCOV version 1.14