PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
plpy_cursorobject.c File Reference
#include "postgres.h"
#include <limits.h>
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "plpy_cursorobject.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_planobject.h"
#include "plpy_resultobject.h"
#include "plpy_spi.h"
#include "plpython.h"
#include "utils/memutils.h"
Include dependency graph for plpy_cursorobject.c:

Go to the source code of this file.

Functions

static PyObject * PLy_cursor_query (const char *query)
 
static void PLy_cursor_dealloc (PyObject *arg)
 
static PyObject * PLy_cursor_iternext (PyObject *self)
 
static PyObject * PLy_cursor_fetch (PyObject *self, PyObject *args)
 
static PyObject * PLy_cursor_close (PyObject *self, PyObject *unused)
 
void PLy_cursor_init_type (void)
 
PyObject * PLy_cursor (PyObject *self, PyObject *args)
 
PyObject * PLy_cursor_plan (PyObject *ob, PyObject *args)
 

Variables

static const char PLy_cursor_doc [] = "Wrapper around a PostgreSQL cursor"
 
static PyMethodDef PLy_cursor_methods []
 
static PyTypeObject PLy_CursorType
 

Function Documentation

◆ PLy_cursor()

PyObject * PLy_cursor ( PyObject *  self,
PyObject *  args 
)

Definition at line 56 of file plpy_cursorobject.c.

57{
58 char *query;
59 PyObject *plan;
60 PyObject *planargs = NULL;
61
62 if (PyArg_ParseTuple(args, "s", &query))
63 return PLy_cursor_query(query);
64
65 PyErr_Clear();
66
67 if (PyArg_ParseTuple(args, "O|O", &plan, &planargs))
68 return PLy_cursor_plan(plan, planargs);
69
70 PLy_exception_set(PLy_exc_error, "plpy.cursor expected a query or a plan");
71 return NULL;
72}
#define plan(x)
Definition: pg_regress.c:161
PyObject * PLy_cursor_plan(PyObject *ob, PyObject *args)
static PyObject * PLy_cursor_query(const char *query)
PyObject * PLy_exc_error
Definition: plpy_elog.c:15
void PLy_exception_set(PyObject *exc, const char *fmt,...)
Definition: plpy_elog.c:472

References generate_unaccent_rules::args, plan, PLy_cursor_plan(), PLy_cursor_query(), PLy_exc_error, and PLy_exception_set().

◆ PLy_cursor_close()

static PyObject * PLy_cursor_close ( PyObject *  self,
PyObject *  unused 
)
static

Definition at line 465 of file plpy_cursorobject.c.

466{
468
469 if (!cursor->closed)
470 {
471 Portal portal = GetPortalByName(cursor->portalname);
472
473 if (!PortalIsValid(portal))
474 {
475 PLy_exception_set(PyExc_ValueError,
476 "closing a cursor in an aborted subtransaction");
477 return NULL;
478 }
479
480 UnpinPortal(portal);
481 SPI_cursor_close(portal);
482 cursor->closed = true;
483 }
484
485 Py_RETURN_NONE;
486}
#define PortalIsValid(p)
Definition: portal.h:211
void UnpinPortal(Portal portal)
Definition: portalmem.c:380
Portal GetPortalByName(const char *name)
Definition: portalmem.c:130
void SPI_cursor_close(Portal portal)
Definition: spi.c:1862
Definition: type.h:138

References GetPortalByName(), PLy_exception_set(), PortalIsValid, SPI_cursor_close(), and UnpinPortal().

◆ PLy_cursor_dealloc()

static void PLy_cursor_dealloc ( PyObject *  arg)
static

Definition at line 275 of file plpy_cursorobject.c.

276{
278 Portal portal;
279
281
282 if (!cursor->closed)
283 {
284 portal = GetPortalByName(cursor->portalname);
285
286 if (PortalIsValid(portal))
287 {
288 UnpinPortal(portal);
289 SPI_cursor_close(portal);
290 }
291 cursor->closed = true;
292 }
293 if (cursor->mcxt)
294 {
296 cursor->mcxt = NULL;
297 }
298 arg->ob_type->tp_free(arg);
299}
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
void * arg

References arg, GetPortalByName(), MemoryContextDelete(), PortalIsValid, SPI_cursor_close(), and UnpinPortal().

◆ PLy_cursor_fetch()

static PyObject * PLy_cursor_fetch ( PyObject *  self,
PyObject *  args 
)
static

Definition at line 364 of file plpy_cursorobject.c.

365{
367 int count;
368 PLyResultObject *ret;
370 volatile MemoryContext oldcontext;
371 volatile ResourceOwner oldowner;
372 Portal portal;
373
374 if (!PyArg_ParseTuple(args, "i:fetch", &count))
375 return NULL;
376
377 cursor = (PLyCursorObject *) self;
378
379 if (cursor->closed)
380 {
381 PLy_exception_set(PyExc_ValueError, "fetch from a closed cursor");
382 return NULL;
383 }
384
385 portal = GetPortalByName(cursor->portalname);
386 if (!PortalIsValid(portal))
387 {
388 PLy_exception_set(PyExc_ValueError,
389 "iterating a cursor in an aborted subtransaction");
390 return NULL;
391 }
392
394 if (ret == NULL)
395 return NULL;
396
397 oldcontext = CurrentMemoryContext;
398 oldowner = CurrentResourceOwner;
399
400 PLy_spi_subtransaction_begin(oldcontext, oldowner);
401
402 PG_TRY();
403 {
404 SPI_cursor_fetch(portal, true, count);
405
406 Py_DECREF(ret->status);
407 ret->status = PyLong_FromLong(SPI_OK_FETCH);
408
409 Py_DECREF(ret->nrows);
410 ret->nrows = PyLong_FromUnsignedLongLong(SPI_processed);
411
412 if (SPI_processed != 0)
413 {
414 uint64 i;
415
416 /*
417 * PyList_New() and PyList_SetItem() use Py_ssize_t for list size
418 * and list indices; so we cannot support a result larger than
419 * PY_SSIZE_T_MAX.
420 */
421 if (SPI_processed > (uint64) PY_SSIZE_T_MAX)
423 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
424 errmsg("query result has too many rows to fit in a Python list")));
425
426 Py_DECREF(ret->rows);
427 ret->rows = PyList_New(SPI_processed);
428 if (!ret->rows)
429 {
430 Py_DECREF(ret);
431 ret = NULL;
432 }
433 else
434 {
436 exec_ctx->curr_proc);
437
438 for (i = 0; i < SPI_processed; i++)
439 {
440 PyObject *row = PLy_input_from_tuple(&cursor->result,
443 true);
444
445 PyList_SetItem(ret->rows, i, row);
446 }
447 }
448 }
449
451
452 PLy_spi_subtransaction_commit(oldcontext, oldowner);
453 }
454 PG_CATCH();
455 {
456 PLy_spi_subtransaction_abort(oldcontext, oldowner);
457 return NULL;
458 }
459 PG_END_TRY();
460
461 return (PyObject *) ret;
462}
uint64_t uint64
Definition: c.h:489
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:381
#define ereport(elevel,...)
Definition: elog.h:149
int i
Definition: isn.c:72
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
PLyExecutionContext * PLy_current_execution_context(void)
Definition: plpy_main.c:365
PyObject * PLy_result_new(void)
void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:573
void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:582
void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:565
PyObject * PLy_input_from_tuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc, bool include_generated)
Definition: plpy_typeio.c:134
void PLy_input_setup_tuple(PLyDatumToOb *arg, TupleDesc desc, PLyProcedure *proc)
Definition: plpy_typeio.c:165
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
uint64 SPI_processed
Definition: spi.c:44
SPITupleTable * SPI_tuptable
Definition: spi.c:45
void SPI_cursor_fetch(Portal portal, bool forward, long count)
Definition: spi.c:1806
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:1386
#define SPI_OK_FETCH
Definition: spi.h:84
PLyProcedure * curr_proc
Definition: plpy_main.h:20
PyObject_HEAD PyObject * nrows
TupleDesc tupdesc
Definition: spi.h:25
HeapTuple * vals
Definition: spi.h:26

References generate_unaccent_rules::args, PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, ereport, errcode(), errmsg(), ERROR, GetPortalByName(), i, PLyResultObject::nrows, PG_CATCH, PG_END_TRY, PG_TRY, PLy_current_execution_context(), PLy_exception_set(), PLy_input_from_tuple(), PLy_input_setup_tuple(), PLy_result_new(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), PortalIsValid, PLyResultObject::rows, SPI_cursor_fetch(), SPI_freetuptable(), SPI_OK_FETCH, SPI_processed, SPI_tuptable, PLyResultObject::status, SPITupleTable::tupdesc, and SPITupleTable::vals.

◆ PLy_cursor_init_type()

void PLy_cursor_init_type ( void  )

Definition at line 49 of file plpy_cursorobject.c.

50{
51 if (PyType_Ready(&PLy_CursorType) < 0)
52 elog(ERROR, "could not initialize PLy_CursorType");
53}
#define elog(elevel,...)
Definition: elog.h:225
static PyTypeObject PLy_CursorType

References elog, ERROR, and PLy_CursorType.

Referenced by PLy_init_plpy().

◆ PLy_cursor_iternext()

static PyObject * PLy_cursor_iternext ( PyObject *  self)
static

Definition at line 302 of file plpy_cursorobject.c.

303{
305 PyObject *ret;
307 volatile MemoryContext oldcontext;
308 volatile ResourceOwner oldowner;
309 Portal portal;
310
311 cursor = (PLyCursorObject *) self;
312
313 if (cursor->closed)
314 {
315 PLy_exception_set(PyExc_ValueError, "iterating a closed cursor");
316 return NULL;
317 }
318
319 portal = GetPortalByName(cursor->portalname);
320 if (!PortalIsValid(portal))
321 {
322 PLy_exception_set(PyExc_ValueError,
323 "iterating a cursor in an aborted subtransaction");
324 return NULL;
325 }
326
327 oldcontext = CurrentMemoryContext;
328 oldowner = CurrentResourceOwner;
329
330 PLy_spi_subtransaction_begin(oldcontext, oldowner);
331
332 PG_TRY();
333 {
334 SPI_cursor_fetch(portal, true, 1);
335 if (SPI_processed == 0)
336 {
337 PyErr_SetNone(PyExc_StopIteration);
338 ret = NULL;
339 }
340 else
341 {
343 exec_ctx->curr_proc);
344
345 ret = PLy_input_from_tuple(&cursor->result, SPI_tuptable->vals[0],
346 SPI_tuptable->tupdesc, true);
347 }
348
350
351 PLy_spi_subtransaction_commit(oldcontext, oldowner);
352 }
353 PG_CATCH();
354 {
355 PLy_spi_subtransaction_abort(oldcontext, oldowner);
356 return NULL;
357 }
358 PG_END_TRY();
359
360 return ret;
361}

References PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, GetPortalByName(), PG_CATCH, PG_END_TRY, PG_TRY, PLy_current_execution_context(), PLy_exception_set(), PLy_input_from_tuple(), PLy_input_setup_tuple(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), PortalIsValid, SPI_cursor_fetch(), SPI_freetuptable(), SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.

◆ PLy_cursor_plan()

PyObject * PLy_cursor_plan ( PyObject *  ob,
PyObject *  args 
)

Definition at line 139 of file plpy_cursorobject.c.

140{
142 volatile int nargs;
145 volatile MemoryContext oldcontext;
146 volatile ResourceOwner oldowner;
147
148 if (args)
149 {
150 if (!PySequence_Check(args) || PyUnicode_Check(args))
151 {
152 PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument");
153 return NULL;
154 }
155 nargs = PySequence_Length(args);
156 }
157 else
158 nargs = 0;
159
160 plan = (PLyPlanObject *) ob;
161
162 if (nargs != plan->nargs)
163 {
164 char *sv;
165 PyObject *so = PyObject_Str(args);
166
167 if (!so)
168 PLy_elog(ERROR, "could not execute plan");
169 sv = PLyUnicode_AsString(so);
170 PLy_exception_set_plural(PyExc_TypeError,
171 "Expected sequence of %d argument, got %d: %s",
172 "Expected sequence of %d arguments, got %d: %s",
173 plan->nargs,
174 plan->nargs, nargs, sv);
175 Py_DECREF(so);
176
177 return NULL;
178 }
179
180 if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL)
181 return NULL;
182 cursor->portalname = NULL;
183 cursor->closed = false;
185 "PL/Python cursor context",
187
188 /* Initialize for converting result tuples to Python */
189 PLy_input_setup_func(&cursor->result, cursor->mcxt,
190 RECORDOID, -1,
191 exec_ctx->curr_proc);
192
193 oldcontext = CurrentMemoryContext;
194 oldowner = CurrentResourceOwner;
195
196 PLy_spi_subtransaction_begin(oldcontext, oldowner);
197
198 PG_TRY();
199 {
200 Portal portal;
201 MemoryContext tmpcontext;
202 Datum *volatile values;
203 char *volatile nulls;
204 volatile int j;
205
206 /*
207 * Converted arguments and associated cruft will be in this context,
208 * which is local to our subtransaction.
209 */
211 "PL/Python temporary context",
213 MemoryContextSwitchTo(tmpcontext);
214
215 if (nargs > 0)
216 {
217 values = (Datum *) palloc(nargs * sizeof(Datum));
218 nulls = (char *) palloc(nargs * sizeof(char));
219 }
220 else
221 {
222 values = NULL;
223 nulls = NULL;
224 }
225
226 for (j = 0; j < nargs; j++)
227 {
228 PLyObToDatum *arg = &plan->args[j];
229 PyObject *elem;
230
231 elem = PySequence_GetItem(args, j);
232 PG_TRY(2);
233 {
234 bool isnull;
235
236 values[j] = PLy_output_convert(arg, elem, &isnull);
237 nulls[j] = isnull ? 'n' : ' ';
238 }
239 PG_FINALLY(2);
240 {
241 Py_DECREF(elem);
242 }
243 PG_END_TRY(2);
244 }
245
246 MemoryContextSwitchTo(oldcontext);
247
248 portal = SPI_cursor_open(NULL, plan->plan, values, nulls,
249 exec_ctx->curr_proc->fn_readonly);
250 if (portal == NULL)
251 elog(ERROR, "SPI_cursor_open() failed: %s",
253
254 cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
255
256 PinPortal(portal);
257
258 MemoryContextDelete(tmpcontext);
259 PLy_spi_subtransaction_commit(oldcontext, oldowner);
260 }
261 PG_CATCH();
262 {
263 Py_DECREF(cursor);
264 /* Subtransaction abort will remove the tmpcontext */
265 PLy_spi_subtransaction_abort(oldcontext, oldowner);
266 return NULL;
267 }
268 PG_END_TRY();
269
270 Assert(cursor->portalname != NULL);
271 return (PyObject *) cursor;
272}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define Assert(condition)
Definition: c.h:815
#define PG_FINALLY(...)
Definition: elog.h:388
int j
Definition: isn.c:73
#define PLy_elog
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1683
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * palloc(Size size)
Definition: mcxt.c:1317
MemoryContext CurTransactionContext
Definition: mcxt.c:155
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: plpy_elog.c:486
void PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, int32 typmod, PLyProcedure *proc)
Definition: plpy_typeio.c:418
Datum PLy_output_convert(PLyObToDatum *arg, PyObject *val, bool *isnull)
Definition: plpy_typeio.c:120
char * PLyUnicode_AsString(PyObject *unicode)
Definition: plpy_util.c:82
void PinPortal(Portal portal)
Definition: portalmem.c:371
uintptr_t Datum
Definition: postgres.h:69
const char * SPI_result_code_string(int code)
Definition: spi.c:1972
int SPI_result
Definition: spi.c:46
Portal SPI_cursor_open(const char *name, SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only)
Definition: spi.c:1445
const char * name
Definition: portal.h:118

References ALLOCSET_DEFAULT_SIZES, ALLOCSET_SMALL_SIZES, AllocSetContextCreate, arg, generate_unaccent_rules::args, Assert, PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, CurTransactionContext, elog, ERROR, PLyProcedure::fn_readonly, j, MemoryContextDelete(), MemoryContextStrdup(), MemoryContextSwitchTo(), PortalData::name, palloc(), PG_CATCH, PG_END_TRY, PG_FINALLY, PG_TRY, PinPortal(), plan, PLy_current_execution_context(), PLy_CursorType, PLy_elog, PLy_exception_set(), PLy_exception_set_plural(), PLy_input_setup_func(), PLy_output_convert(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), PLyUnicode_AsString(), SPI_cursor_open(), SPI_result, SPI_result_code_string(), TopMemoryContext, and values.

Referenced by PLy_cursor(), and PLy_plan_cursor().

◆ PLy_cursor_query()

static PyObject * PLy_cursor_query ( const char *  query)
static

Definition at line 76 of file plpy_cursorobject.c.

77{
80 volatile MemoryContext oldcontext;
81 volatile ResourceOwner oldowner;
82
83 if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL)
84 return NULL;
85 cursor->portalname = NULL;
86 cursor->closed = false;
88 "PL/Python cursor context",
90
91 /* Initialize for converting result tuples to Python */
92 PLy_input_setup_func(&cursor->result, cursor->mcxt,
93 RECORDOID, -1,
94 exec_ctx->curr_proc);
95
96 oldcontext = CurrentMemoryContext;
97 oldowner = CurrentResourceOwner;
98
99 PLy_spi_subtransaction_begin(oldcontext, oldowner);
100
101 PG_TRY();
102 {
104 Portal portal;
105
106 pg_verifymbstr(query, strlen(query), false);
107
108 plan = SPI_prepare(query, 0, NULL);
109 if (plan == NULL)
110 elog(ERROR, "SPI_prepare failed: %s",
112
113 portal = SPI_cursor_open(NULL, plan, NULL, NULL,
114 exec_ctx->curr_proc->fn_readonly);
116
117 if (portal == NULL)
118 elog(ERROR, "SPI_cursor_open() failed: %s",
120
121 cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name);
122
123 PinPortal(portal);
124
125 PLy_spi_subtransaction_commit(oldcontext, oldowner);
126 }
127 PG_CATCH();
128 {
129 PLy_spi_subtransaction_abort(oldcontext, oldowner);
130 return NULL;
131 }
132 PG_END_TRY();
133
134 Assert(cursor->portalname != NULL);
135 return (PyObject *) cursor;
136}
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
Definition: mbutils.c:1556
int SPI_freeplan(SPIPlanPtr plan)
Definition: spi.c:1025
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:860

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, elog, ERROR, PLyProcedure::fn_readonly, MemoryContextStrdup(), PortalData::name, PG_CATCH, PG_END_TRY, PG_TRY, pg_verifymbstr(), PinPortal(), plan, PLy_current_execution_context(), PLy_CursorType, PLy_input_setup_func(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), SPI_cursor_open(), SPI_freeplan(), SPI_prepare(), SPI_result, SPI_result_code_string(), and TopMemoryContext.

Referenced by PLy_cursor().

Variable Documentation

◆ PLy_cursor_doc

const char PLy_cursor_doc[] = "Wrapper around a PostgreSQL cursor"
static

Definition at line 28 of file plpy_cursorobject.c.

◆ PLy_cursor_methods

PyMethodDef PLy_cursor_methods[]
static
Initial value:
= {
{"fetch", PLy_cursor_fetch, METH_VARARGS, NULL},
{"close", PLy_cursor_close, METH_NOARGS, NULL},
{NULL, NULL, 0, NULL}
}
static PyObject * PLy_cursor_fetch(PyObject *self, PyObject *args)
static PyObject * PLy_cursor_close(PyObject *self, PyObject *unused)

Definition at line 30 of file plpy_cursorobject.c.

◆ PLy_CursorType

PyTypeObject PLy_CursorType
static
Initial value:
= {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "PLyCursor",
.tp_basicsize = sizeof(PLyCursorObject),
.tp_dealloc = PLy_cursor_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = PLy_cursor_doc,
.tp_iter = PyObject_SelfIter,
.tp_iternext = PLy_cursor_iternext,
.tp_methods = PLy_cursor_methods,
}
static PyMethodDef PLy_cursor_methods[]
static PyObject * PLy_cursor_iternext(PyObject *self)
static void PLy_cursor_dealloc(PyObject *arg)
static const char PLy_cursor_doc[]
struct PLyCursorObject PLyCursorObject

Definition at line 36 of file plpy_cursorobject.c.

Referenced by PLy_cursor_init_type(), PLy_cursor_plan(), and PLy_cursor_query().