LCOV - differential code coverage report
Current view: top level - src/pl/plpython - plpy_cursorobject.c (source / functions) Coverage Total Hit UBC CBC
Current: d36b728949bf4e37ada1cd23e0f2aaa94f609a70 vs 52e118fe2f7e3381bdaa479816a7f72eda2ae517 Lines: 88.7 % 203 180 23 180
Current Date: 2026-06-29 16:15:13 +0200 Functions: 100.0 % 8 8 8
Baseline: lcov-20260630-baseline Branches: 63.0 % 100 63 37 63
Baseline Date: 2026-06-29 13:01:57 +0200 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
[..1] days: 100.0 % 2 2 2
(360..) days: 88.6 % 201 178 23 178
Function coverage date bins:
(360..) days: 100.0 % 8 8 8
Branch coverage date bins:
[..1] days: 100.0 % 2 2 2
(360..) days: 62.2 % 98 61 37 61

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * the PLyCursor class
                                  3                 :                :  *
                                  4                 :                :  * src/pl/plpython/plpy_cursorobject.c
                                  5                 :                :  */
                                  6                 :                : 
                                  7                 :                : #include "postgres.h"
                                  8                 :                : 
                                  9                 :                : #include <limits.h>
                                 10                 :                : 
                                 11                 :                : #include "catalog/pg_type.h"
                                 12                 :                : #include "mb/pg_wchar.h"
                                 13                 :                : #include "plpy_cursorobject.h"
                                 14                 :                : #include "plpy_elog.h"
                                 15                 :                : #include "plpy_main.h"
                                 16                 :                : #include "plpy_planobject.h"
                                 17                 :                : #include "plpy_resultobject.h"
                                 18                 :                : #include "plpy_spi.h"
                                 19                 :                : #include "plpy_util.h"
                                 20                 :                : #include "utils/memutils.h"
                                 21                 :                : 
                                 22                 :                : static PyObject *PLy_cursor_query(const char *query);
                                 23                 :                : static void PLy_cursor_dealloc(PLyCursorObject *self);
                                 24                 :                : static PyObject *PLy_cursor_iternext(PyObject *self);
                                 25                 :                : static PyObject *PLy_cursor_fetch(PyObject *self, PyObject *args);
                                 26                 :                : static PyObject *PLy_cursor_close(PyObject *self, PyObject *unused);
                                 27                 :                : 
                                 28                 :                : static const char PLy_cursor_doc[] = "Wrapper around a PostgreSQL cursor";
                                 29                 :                : 
                                 30                 :                : static PyMethodDef PLy_cursor_methods[] = {
                                 31                 :                :     {"fetch", PLy_cursor_fetch, METH_VARARGS, NULL},
                                 32                 :                :     {"close", PLy_cursor_close, METH_NOARGS, NULL},
                                 33                 :                :     {NULL, NULL, 0, NULL}
                                 34                 :                : };
                                 35                 :                : 
                                 36                 :                : static PyType_Slot PLyCursor_slots[] =
                                 37                 :                : {
                                 38                 :                :     {
                                 39                 :                :         Py_tp_dealloc, PLy_cursor_dealloc
                                 40                 :                :     },
                                 41                 :                :     {
                                 42                 :                :         Py_tp_doc, (char *) PLy_cursor_doc
                                 43                 :                :     },
                                 44                 :                :     {
                                 45                 :                :         Py_tp_iter, PyObject_SelfIter
                                 46                 :                :     },
                                 47                 :                :     {
                                 48                 :                :         Py_tp_iternext, PLy_cursor_iternext
                                 49                 :                :     },
                                 50                 :                :     {
                                 51                 :                :         Py_tp_methods, PLy_cursor_methods
                                 52                 :                :     },
                                 53                 :                :     {
                                 54                 :                :         0, NULL
                                 55                 :                :     }
                                 56                 :                : };
                                 57                 :                : 
                                 58                 :                : static PyType_Spec PLyCursor_spec =
                                 59                 :                : {
                                 60                 :                :     .name = "PLyCursor",
                                 61                 :                :     .basicsize = sizeof(PLyCursorObject),
                                 62                 :                :     .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
                                 63                 :                :     .slots = PLyCursor_slots,
                                 64                 :                : };
                                 65                 :                : 
                                 66                 :                : static PyTypeObject *PLy_CursorType;
                                 67                 :                : 
                                 68                 :                : void
 5308 peter_e@gmx.net            69                 :CBC          23 : PLy_cursor_init_type(void)
                                 70                 :                : {
  475 peter@eisentraut.org       71                 :             23 :     PLy_CursorType = (PyTypeObject *) PyType_FromSpec(&PLyCursor_spec);
                                 72         [ -  + ]:             23 :     if (!PLy_CursorType)
 5308 peter_e@gmx.net            73         [ #  # ]:UBC           0 :         elog(ERROR, "could not initialize PLy_CursorType");
 5308 peter_e@gmx.net            74                 :CBC          23 : }
                                 75                 :                : 
                                 76                 :                : PyObject *
                                 77                 :             19 : PLy_cursor(PyObject *self, PyObject *args)
                                 78                 :                : {
                                 79                 :                :     char       *query;
                                 80                 :                :     PyObject   *plan;
                                 81                 :             19 :     PyObject   *planargs = NULL;
                                 82                 :                : 
                                 83         [ +  + ]:             19 :     if (PyArg_ParseTuple(args, "s", &query))
                                 84                 :             14 :         return PLy_cursor_query(query);
                                 85                 :                : 
                                 86                 :              5 :     PyErr_Clear();
                                 87                 :                : 
                                 88         [ +  - ]:              5 :     if (PyArg_ParseTuple(args, "O|O", &plan, &planargs))
                                 89                 :              5 :         return PLy_cursor_plan(plan, planargs);
                                 90                 :                : 
 5308 peter_e@gmx.net            91                 :UBC           0 :     PLy_exception_set(PLy_exc_error, "plpy.cursor expected a query or a plan");
                                 92                 :              0 :     return NULL;
                                 93                 :                : }
                                 94                 :                : 
                                 95                 :                : 
                                 96                 :                : static PyObject *
 5308 peter_e@gmx.net            97                 :CBC          14 : PLy_cursor_query(const char *query)
                                 98                 :                : {
                                 99                 :                :     PLyCursorObject *cursor;
 3148 tgl@sss.pgh.pa.us         100                 :             14 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
                                101                 :                :     volatile MemoryContext oldcontext;
                                102                 :                :     volatile ResourceOwner oldowner;
                                103                 :                : 
  475 peter@eisentraut.org      104         [ -  + ]:             14 :     if ((cursor = PyObject_New(PLyCursorObject, PLy_CursorType)) == NULL)
 5308 peter_e@gmx.net           105                 :UBC           0 :         return NULL;
                                106                 :                : #if PY_VERSION_HEX < 0x03080000
                                107                 :                :     /* Workaround for Python issue 35810; no longer necessary in Python 3.8 */
                                108                 :                :     Py_INCREF(PLy_CursorType);
                                109                 :                : #endif
 5308 peter_e@gmx.net           110                 :CBC          14 :     cursor->portalname = NULL;
                                111                 :             14 :     cursor->closed = false;
 3890 tgl@sss.pgh.pa.us         112                 :             14 :     cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
                                113                 :                :                                          "PL/Python cursor context",
                                114                 :                :                                          ALLOCSET_DEFAULT_SIZES);
                                115                 :                : 
                                116                 :                :     /* Initialize for converting result tuples to Python */
 3148                           117                 :             14 :     PLy_input_setup_func(&cursor->result, cursor->mcxt,
                                118                 :                :                          RECORDOID, -1,
                                119                 :             14 :                          exec_ctx->curr_proc);
                                120                 :                : 
 5308 peter_e@gmx.net           121                 :             14 :     oldcontext = CurrentMemoryContext;
                                122                 :             14 :     oldowner = CurrentResourceOwner;
                                123                 :                : 
                                124                 :             14 :     PLy_spi_subtransaction_begin(oldcontext, oldowner);
                                125                 :                : 
                                126         [ +  - ]:             14 :     PG_TRY();
                                127                 :                :     {
                                128                 :                :         SPIPlanPtr  plan;
                                129                 :                :         Portal      portal;
                                130                 :                : 
                                131                 :             14 :         pg_verifymbstr(query, strlen(query), false);
                                132                 :                : 
                                133                 :             14 :         plan = SPI_prepare(query, 0, NULL);
                                134         [ -  + ]:             14 :         if (plan == NULL)
 5308 peter_e@gmx.net           135         [ #  # ]:UBC           0 :             elog(ERROR, "SPI_prepare failed: %s",
                                136                 :                :                  SPI_result_code_string(SPI_result));
                                137                 :                : 
 5308 peter_e@gmx.net           138                 :CBC          28 :         portal = SPI_cursor_open(NULL, plan, NULL, NULL,
 5222 tgl@sss.pgh.pa.us         139                 :             14 :                                  exec_ctx->curr_proc->fn_readonly);
 5308 peter_e@gmx.net           140                 :             14 :         SPI_freeplan(plan);
                                141                 :                : 
                                142         [ -  + ]:             14 :         if (portal == NULL)
 5285 peter_e@gmx.net           143         [ #  # ]:UBC           0 :             elog(ERROR, "SPI_cursor_open() failed: %s",
                                144                 :                :                  SPI_result_code_string(SPI_result));
                                145                 :                : 
 3890 tgl@sss.pgh.pa.us         146                 :CBC          14 :         cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
                                147                 :                : 
 3122 peter_e@gmx.net           148                 :             14 :         PinPortal(portal);
                                149                 :                : 
 5308                           150                 :             14 :         PLy_spi_subtransaction_commit(oldcontext, oldowner);
                                151                 :                :     }
 5308 peter_e@gmx.net           152                 :UBC           0 :     PG_CATCH();
                                153                 :                :     {
                                154                 :              0 :         PLy_spi_subtransaction_abort(oldcontext, oldowner);
                                155                 :              0 :         return NULL;
                                156                 :                :     }
 5308 peter_e@gmx.net           157         [ -  + ]:CBC          14 :     PG_END_TRY();
                                158                 :                : 
                                159         [ -  + ]:             14 :     Assert(cursor->portalname != NULL);
                                160                 :             14 :     return (PyObject *) cursor;
                                161                 :                : }
                                162                 :                : 
                                163                 :                : PyObject *
                                164                 :              6 : PLy_cursor_plan(PyObject *ob, PyObject *args)
                                165                 :                : {
                                166                 :                :     PLyCursorObject *cursor;
                                167                 :                :     volatile int nargs;
                                168                 :                :     PLyPlanObject *plan;
 3148 tgl@sss.pgh.pa.us         169                 :              6 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
                                170                 :                :     volatile MemoryContext oldcontext;
                                171                 :                :     volatile ResourceOwner oldowner;
                                172                 :                : 
 5308 peter_e@gmx.net           173         [ +  + ]:              6 :     if (args)
                                174                 :                :     {
 1576 andres@anarazel.de        175   [ +  -  -  + ]:              4 :         if (!PySequence_Check(args) || PyUnicode_Check(args))
                                176                 :                :         {
 5308 peter_e@gmx.net           177                 :UBC           0 :             PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument");
                                178                 :              0 :             return NULL;
                                179                 :                :         }
 5308 peter_e@gmx.net           180                 :CBC           4 :         nargs = PySequence_Length(args);
                                181                 :                :     }
                                182                 :                :     else
                                183                 :              2 :         nargs = 0;
                                184                 :                : 
                                185                 :              6 :     plan = (PLyPlanObject *) ob;
                                186                 :                : 
                                187         [ +  + ]:              6 :     if (nargs != plan->nargs)
                                188                 :                :     {
                                189                 :                :         char       *sv;
                                190                 :              1 :         PyObject   *so = PyObject_Str(args);
                                191                 :                : 
                                192         [ -  + ]:              1 :         if (!so)
 5308 peter_e@gmx.net           193                 :UBC           0 :             PLy_elog(ERROR, "could not execute plan");
 1576 andres@anarazel.de        194                 :CBC           1 :         sv = PLyUnicode_AsString(so);
 5308 peter_e@gmx.net           195                 :              1 :         PLy_exception_set_plural(PyExc_TypeError,
                                196                 :                :                                  "Expected sequence of %d argument, got %d: %s",
                                197                 :                :                                  "Expected sequence of %d arguments, got %d: %s",
                                198                 :              1 :                                  plan->nargs,
                                199                 :                :                                  plan->nargs, nargs, sv);
                                200                 :                :         Py_DECREF(so);
                                201                 :                : 
                                202                 :              1 :         return NULL;
                                203                 :                :     }
                                204                 :                : 
  475 peter@eisentraut.org      205         [ -  + ]:              5 :     if ((cursor = PyObject_New(PLyCursorObject, PLy_CursorType)) == NULL)
 5308 peter_e@gmx.net           206                 :UBC           0 :         return NULL;
                                207                 :                : #if PY_VERSION_HEX < 0x03080000
                                208                 :                :     /* Workaround for Python issue 35810; no longer necessary in Python 3.8 */
                                209                 :                :     Py_INCREF(PLy_CursorType);
                                210                 :                : #endif
 5308 peter_e@gmx.net           211                 :CBC           5 :     cursor->portalname = NULL;
                                212                 :              5 :     cursor->closed = false;
 3890 tgl@sss.pgh.pa.us         213                 :              5 :     cursor->mcxt = AllocSetContextCreate(TopMemoryContext,
                                214                 :                :                                          "PL/Python cursor context",
                                215                 :                :                                          ALLOCSET_DEFAULT_SIZES);
                                216                 :                : 
                                217                 :                :     /* Initialize for converting result tuples to Python */
 3148                           218                 :              5 :     PLy_input_setup_func(&cursor->result, cursor->mcxt,
                                219                 :                :                          RECORDOID, -1,
                                220                 :              5 :                          exec_ctx->curr_proc);
                                221                 :                : 
 5308 peter_e@gmx.net           222                 :              5 :     oldcontext = CurrentMemoryContext;
                                223                 :              5 :     oldowner = CurrentResourceOwner;
                                224                 :                : 
                                225                 :              5 :     PLy_spi_subtransaction_begin(oldcontext, oldowner);
                                226                 :                : 
                                227         [ +  + ]:              5 :     PG_TRY();
                                228                 :                :     {
                                229                 :                :         Portal      portal;
                                230                 :                :         MemoryContext tmpcontext;
                                231                 :                :         Datum      *volatile values;
                                232                 :                :         char       *volatile nulls;
                                233                 :                :         volatile int j;
                                234                 :                : 
                                235                 :                :         /*
                                236                 :                :          * Converted arguments and associated cruft will be in this context,
                                237                 :                :          * which is local to our subtransaction.
                                238                 :                :          */
  535 tgl@sss.pgh.pa.us         239                 :              5 :         tmpcontext = AllocSetContextCreate(CurTransactionContext,
                                240                 :                :                                            "PL/Python temporary context",
                                241                 :                :                                            ALLOCSET_SMALL_SIZES);
                                242                 :              5 :         MemoryContextSwitchTo(tmpcontext);
                                243                 :                : 
 5308 peter_e@gmx.net           244         [ +  + ]:              5 :         if (nargs > 0)
                                245                 :                :         {
  535 tgl@sss.pgh.pa.us         246                 :              3 :             values = (Datum *) palloc(nargs * sizeof(Datum));
                                247                 :              3 :             nulls = (char *) palloc(nargs * sizeof(char));
                                248                 :                :         }
                                249                 :                :         else
                                250                 :                :         {
                                251                 :              2 :             values = NULL;
 5308 peter_e@gmx.net           252                 :              2 :             nulls = NULL;
                                253                 :                :         }
                                254                 :                : 
                                255         [ +  + ]:              7 :         for (j = 0; j < nargs; j++)
                                256                 :                :         {
 3148 tgl@sss.pgh.pa.us         257                 :              3 :             PLyObToDatum *arg = &plan->args[j];
                                258                 :                :             PyObject   *elem;
                                259                 :                : 
 5308 peter_e@gmx.net           260                 :              3 :             elem = PySequence_GetItem(args, j);
                                261                 :                : 
                                262                 :                :             /* PySequence_GetItem() can return NULL, with an exception set */
    1 rguo@postgresql.org       263         [ +  + ]:              3 :             if (elem == NULL)
                                264                 :              1 :                 PLy_elog(ERROR, "could not get element %d from sequence", j);
                                265                 :                : 
 1362 drowley@postgresql.o      266         [ +  - ]:              2 :             PG_TRY(2);
                                267                 :                :             {
                                268                 :                :                 bool        isnull;
                                269                 :                : 
  535 tgl@sss.pgh.pa.us         270                 :              2 :                 values[j] = PLy_output_convert(arg, elem, &isnull);
 3148                           271         [ -  + ]:              2 :                 nulls[j] = isnull ? 'n' : ' ';
                                272                 :                :             }
 1362 drowley@postgresql.o      273                 :              2 :             PG_FINALLY(2);
                                274                 :                :             {
                                275                 :                :                 Py_DECREF(elem);
                                276                 :                :             }
                                277         [ -  + ]:              2 :             PG_END_TRY(2);
                                278                 :                :         }
                                279                 :                : 
  535 tgl@sss.pgh.pa.us         280                 :              4 :         MemoryContextSwitchTo(oldcontext);
                                281                 :                : 
                                282                 :              8 :         portal = SPI_cursor_open(NULL, plan->plan, values, nulls,
 5222                           283                 :              4 :                                  exec_ctx->curr_proc->fn_readonly);
 5308 peter_e@gmx.net           284         [ -  + ]:              4 :         if (portal == NULL)
 5285 peter_e@gmx.net           285         [ #  # ]:UBC           0 :             elog(ERROR, "SPI_cursor_open() failed: %s",
                                286                 :                :                  SPI_result_code_string(SPI_result));
                                287                 :                : 
 3890 tgl@sss.pgh.pa.us         288                 :CBC           4 :         cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
                                289                 :                : 
 3122 peter_e@gmx.net           290                 :              4 :         PinPortal(portal);
                                291                 :                : 
  535 tgl@sss.pgh.pa.us         292                 :              4 :         MemoryContextDelete(tmpcontext);
 5308 peter_e@gmx.net           293                 :              4 :         PLy_spi_subtransaction_commit(oldcontext, oldowner);
                                294                 :                :     }
                                295                 :              1 :     PG_CATCH();
                                296                 :                :     {
                                297                 :                :         Py_DECREF(cursor);
                                298                 :                :         /* Subtransaction abort will remove the tmpcontext */
                                299                 :              1 :         PLy_spi_subtransaction_abort(oldcontext, oldowner);
                                300                 :              1 :         return NULL;
                                301                 :                :     }
                                302         [ -  + ]:              4 :     PG_END_TRY();
                                303                 :                : 
                                304         [ -  + ]:              4 :     Assert(cursor->portalname != NULL);
                                305                 :              4 :     return (PyObject *) cursor;
                                306                 :                : }
                                307                 :                : 
                                308                 :                : static void
  475 peter@eisentraut.org      309                 :             19 : PLy_cursor_dealloc(PLyCursorObject *self)
                                310                 :                : {
                                311                 :                : #if PY_VERSION_HEX >= 0x03080000
                                312                 :             19 :     PyTypeObject *tp = Py_TYPE(self);
                                313                 :                : #endif
                                314                 :                :     Portal      portal;
                                315                 :                : 
                                316         [ +  + ]:             19 :     if (!self->closed)
                                317                 :                :     {
                                318                 :             16 :         portal = GetPortalByName(self->portalname);
                                319                 :                : 
 5308 peter_e@gmx.net           320         [ +  + ]:             16 :         if (PortalIsValid(portal))
                                321                 :                :         {
 3122                           322                 :             12 :             UnpinPortal(portal);
 5308                           323                 :             12 :             SPI_cursor_close(portal);
                                324                 :                :         }
  475 peter@eisentraut.org      325                 :             16 :         self->closed = true;
                                326                 :                :     }
                                327         [ +  - ]:             19 :     if (self->mcxt)
                                328                 :                :     {
                                329                 :             19 :         MemoryContextDelete(self->mcxt);
                                330                 :             19 :         self->mcxt = NULL;
                                331                 :                :     }
                                332                 :                : 
                                333                 :             19 :     PyObject_Free(self);
                                334                 :                : #if PY_VERSION_HEX >= 0x03080000
                                335                 :                :     /* This was not needed before Python 3.8 (Python issue 35810) */
                                336                 :                :     Py_DECREF(tp);
                                337                 :                : #endif
 5308 peter_e@gmx.net           338                 :             19 : }
                                339                 :                : 
                                340                 :                : static PyObject *
                                341                 :             36 : PLy_cursor_iternext(PyObject *self)
                                342                 :                : {
                                343                 :                :     PLyCursorObject *cursor;
                                344                 :                :     PyObject   *ret;
 3148 tgl@sss.pgh.pa.us         345                 :             36 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
                                346                 :                :     volatile MemoryContext oldcontext;
                                347                 :                :     volatile ResourceOwner oldowner;
                                348                 :                :     Portal      portal;
                                349                 :                : 
 5308 peter_e@gmx.net           350                 :             36 :     cursor = (PLyCursorObject *) self;
                                351                 :                : 
                                352         [ +  + ]:             36 :     if (cursor->closed)
                                353                 :                :     {
                                354                 :              1 :         PLy_exception_set(PyExc_ValueError, "iterating a closed cursor");
                                355                 :              1 :         return NULL;
                                356                 :                :     }
                                357                 :                : 
                                358                 :             35 :     portal = GetPortalByName(cursor->portalname);
                                359         [ -  + ]:             35 :     if (!PortalIsValid(portal))
                                360                 :                :     {
 5308 peter_e@gmx.net           361                 :UBC           0 :         PLy_exception_set(PyExc_ValueError,
                                362                 :                :                           "iterating a cursor in an aborted subtransaction");
                                363                 :              0 :         return NULL;
                                364                 :                :     }
                                365                 :                : 
 5308 peter_e@gmx.net           366                 :CBC          35 :     oldcontext = CurrentMemoryContext;
                                367                 :             35 :     oldowner = CurrentResourceOwner;
                                368                 :                : 
                                369                 :             35 :     PLy_spi_subtransaction_begin(oldcontext, oldowner);
                                370                 :                : 
                                371         [ +  + ]:             35 :     PG_TRY();
                                372                 :                :     {
                                373                 :             35 :         SPI_cursor_fetch(portal, true, 1);
                                374         [ +  + ]:             34 :         if (SPI_processed == 0)
                                375                 :                :         {
                                376                 :              8 :             PyErr_SetNone(PyExc_StopIteration);
                                377                 :              8 :             ret = NULL;
                                378                 :                :         }
                                379                 :                :         else
                                380                 :                :         {
 3148 tgl@sss.pgh.pa.us         381                 :             26 :             PLy_input_setup_tuple(&cursor->result, SPI_tuptable->tupdesc,
                                382                 :             26 :                                   exec_ctx->curr_proc);
                                383                 :                : 
                                384                 :             26 :             ret = PLy_input_from_tuple(&cursor->result, SPI_tuptable->vals[0],
 2649 peter@eisentraut.org      385                 :             26 :                                        SPI_tuptable->tupdesc, true);
                                386                 :                :         }
                                387                 :                : 
 5308 peter_e@gmx.net           388                 :             34 :         SPI_freetuptable(SPI_tuptable);
                                389                 :                : 
                                390                 :             34 :         PLy_spi_subtransaction_commit(oldcontext, oldowner);
                                391                 :                :     }
                                392                 :              1 :     PG_CATCH();
                                393                 :                :     {
                                394                 :              1 :         PLy_spi_subtransaction_abort(oldcontext, oldowner);
                                395                 :              1 :         return NULL;
                                396                 :                :     }
                                397         [ -  + ]:             34 :     PG_END_TRY();
                                398                 :                : 
                                399                 :             34 :     return ret;
                                400                 :                : }
                                401                 :                : 
                                402                 :                : static PyObject *
                                403                 :             13 : PLy_cursor_fetch(PyObject *self, PyObject *args)
                                404                 :                : {
                                405                 :                :     PLyCursorObject *cursor;
                                406                 :                :     int         count;
                                407                 :                :     PLyResultObject *ret;
 3148 tgl@sss.pgh.pa.us         408                 :             13 :     PLyExecutionContext *exec_ctx = PLy_current_execution_context();
                                409                 :                :     volatile MemoryContext oldcontext;
                                410                 :                :     volatile ResourceOwner oldowner;
                                411                 :                :     Portal      portal;
                                412                 :                : 
 3533 peter_e@gmx.net           413         [ -  + ]:             13 :     if (!PyArg_ParseTuple(args, "i:fetch", &count))
 5308 peter_e@gmx.net           414                 :UBC           0 :         return NULL;
                                415                 :                : 
 5308 peter_e@gmx.net           416                 :CBC          13 :     cursor = (PLyCursorObject *) self;
                                417                 :                : 
                                418         [ +  + ]:             13 :     if (cursor->closed)
                                419                 :                :     {
                                420                 :              1 :         PLy_exception_set(PyExc_ValueError, "fetch from a closed cursor");
                                421                 :              1 :         return NULL;
                                422                 :                :     }
                                423                 :                : 
                                424                 :             12 :     portal = GetPortalByName(cursor->portalname);
                                425         [ +  + ]:             12 :     if (!PortalIsValid(portal))
                                426                 :                :     {
                                427                 :              2 :         PLy_exception_set(PyExc_ValueError,
                                428                 :                :                           "iterating a cursor in an aborted subtransaction");
                                429                 :              2 :         return NULL;
                                430                 :                :     }
                                431                 :                : 
                                432                 :             10 :     ret = (PLyResultObject *) PLy_result_new();
                                433         [ -  + ]:             10 :     if (ret == NULL)
 5308 peter_e@gmx.net           434                 :UBC           0 :         return NULL;
                                435                 :                : 
 5308 peter_e@gmx.net           436                 :CBC          10 :     oldcontext = CurrentMemoryContext;
                                437                 :             10 :     oldowner = CurrentResourceOwner;
                                438                 :                : 
                                439                 :             10 :     PLy_spi_subtransaction_begin(oldcontext, oldowner);
                                440                 :                : 
                                441         [ +  - ]:             10 :     PG_TRY();
                                442                 :                :     {
                                443                 :             10 :         SPI_cursor_fetch(portal, true, count);
                                444                 :                : 
                                445                 :             10 :         Py_DECREF(ret->status);
 1576 andres@anarazel.de        446                 :             10 :         ret->status = PyLong_FromLong(SPI_OK_FETCH);
                                447                 :                : 
 5308 peter_e@gmx.net           448                 :             10 :         Py_DECREF(ret->nrows);
 3083                           449                 :             10 :         ret->nrows = PyLong_FromUnsignedLongLong(SPI_processed);
                                450                 :                : 
 5308                           451         [ +  + ]:             10 :         if (SPI_processed != 0)
                                452                 :                :         {
                                453                 :                :             uint64      i;
                                454                 :                : 
                                455                 :                :             /*
                                456                 :                :              * PyList_New() and PyList_SetItem() use Py_ssize_t for list size
                                457                 :                :              * and list indices; so we cannot support a result larger than
                                458                 :                :              * PY_SSIZE_T_MAX.
                                459                 :                :              */
 3762 tgl@sss.pgh.pa.us         460         [ -  + ]:              7 :             if (SPI_processed > (uint64) PY_SSIZE_T_MAX)
 3762 tgl@sss.pgh.pa.us         461         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                462                 :                :                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                463                 :                :                          errmsg("query result has too many rows to fit in a Python list")));
                                464                 :                : 
 5308 peter_e@gmx.net           465                 :CBC           7 :             Py_DECREF(ret->rows);
                                466                 :              7 :             ret->rows = PyList_New(SPI_processed);
 3164                           467         [ -  + ]:              7 :             if (!ret->rows)
                                468                 :                :             {
                                469                 :                :                 Py_DECREF(ret);
 3164 peter_e@gmx.net           470                 :UBC           0 :                 ret = NULL;
                                471                 :                :             }
                                472                 :                :             else
                                473                 :                :             {
 3164 peter_e@gmx.net           474                 :CBC           7 :                 PLy_input_setup_tuple(&cursor->result, SPI_tuptable->tupdesc,
                                475                 :              7 :                                       exec_ctx->curr_proc);
                                476                 :                : 
                                477         [ +  + ]:             44 :                 for (i = 0; i < SPI_processed; i++)
                                478                 :                :                 {
                                479                 :             74 :                     PyObject   *row = PLy_input_from_tuple(&cursor->result,
                                480                 :             37 :                                                            SPI_tuptable->vals[i],
 2649 peter@eisentraut.org      481                 :             37 :                                                            SPI_tuptable->tupdesc,
                                482                 :                :                                                            true);
                                483                 :                : 
 3164 peter_e@gmx.net           484                 :             37 :                     PyList_SetItem(ret->rows, i, row);
                                485                 :                :                 }
                                486                 :                :             }
                                487                 :                :         }
                                488                 :                : 
 5308                           489                 :             10 :         SPI_freetuptable(SPI_tuptable);
                                490                 :                : 
                                491                 :             10 :         PLy_spi_subtransaction_commit(oldcontext, oldowner);
                                492                 :                :     }
 5308 peter_e@gmx.net           493                 :UBC           0 :     PG_CATCH();
                                494                 :                :     {
                                495                 :              0 :         PLy_spi_subtransaction_abort(oldcontext, oldowner);
                                496                 :              0 :         return NULL;
                                497                 :                :     }
 5308 peter_e@gmx.net           498         [ -  + ]:CBC          10 :     PG_END_TRY();
                                499                 :                : 
                                500                 :             10 :     return (PyObject *) ret;
                                501                 :                : }
                                502                 :                : 
                                503                 :                : static PyObject *
                                504                 :              5 : PLy_cursor_close(PyObject *self, PyObject *unused)
                                505                 :                : {
                                506                 :              5 :     PLyCursorObject *cursor = (PLyCursorObject *) self;
                                507                 :                : 
                                508         [ +  + ]:              5 :     if (!cursor->closed)
                                509                 :                :     {
 5133 bruce@momjian.us          510                 :              4 :         Portal      portal = GetPortalByName(cursor->portalname);
                                511                 :                : 
 5308 peter_e@gmx.net           512         [ +  + ]:              4 :         if (!PortalIsValid(portal))
                                513                 :                :         {
                                514                 :              1 :             PLy_exception_set(PyExc_ValueError,
                                515                 :                :                               "closing a cursor in an aborted subtransaction");
                                516                 :              1 :             return NULL;
                                517                 :                :         }
                                518                 :                : 
 3122                           519                 :              3 :         UnpinPortal(portal);
 5308                           520                 :              3 :         SPI_cursor_close(portal);
                                521                 :              3 :         cursor->closed = true;
                                522                 :                :     }
                                523                 :                : 
 3196                           524                 :              4 :     Py_RETURN_NONE;
                                525                 :                : }
        

Generated by: LCOV version 2.0-1