PostgreSQL Source Code git master
plpy_spi.c
Go to the documentation of this file.
1/*
2 * interface to SPI functions
3 *
4 * src/pl/plpython/plpy_spi.c
5 */
6
7#include "postgres.h"
8
9#include <limits.h>
10
11#include "access/xact.h"
12#include "catalog/pg_type.h"
13#include "executor/spi.h"
14#include "mb/pg_wchar.h"
15#include "parser/parse_type.h"
16#include "plpy_elog.h"
17#include "plpy_main.h"
18#include "plpy_planobject.h"
19#include "plpy_plpymodule.h"
20#include "plpy_resultobject.h"
21#include "plpy_spi.h"
22#include "plpython.h"
23#include "utils/memutils.h"
24
25static PyObject *PLy_spi_execute_query(char *query, long limit);
26static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
27 uint64 rows, int status);
28static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
29
30
31/* prepare(query="select * from foo")
32 * prepare(query="select * from foo where bar = $1", params=["text"])
33 * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
34 */
35PyObject *
36PLy_spi_prepare(PyObject *self, PyObject *args)
37{
39 PyObject *list = NULL;
40 PyObject *volatile optr = NULL;
41 char *query;
43 volatile MemoryContext oldcontext;
44 volatile ResourceOwner oldowner;
45 volatile int nargs;
46
47 if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
48 return NULL;
49
50 if (list && (!PySequence_Check(list)))
51 {
52 PLy_exception_set(PyExc_TypeError,
53 "second argument of plpy.prepare must be a sequence");
54 return NULL;
55 }
56
57 if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
58 return NULL;
59
61 "PL/Python plan context",
63 oldcontext = MemoryContextSwitchTo(plan->mcxt);
64
65 nargs = list ? PySequence_Length(list) : 0;
66
67 plan->nargs = nargs;
68 plan->types = nargs ? palloc0(sizeof(Oid) * nargs) : NULL;
69 plan->args = nargs ? palloc0(sizeof(PLyObToDatum) * nargs) : NULL;
70
71 MemoryContextSwitchTo(oldcontext);
72
73 oldcontext = CurrentMemoryContext;
74 oldowner = CurrentResourceOwner;
75
76 PLy_spi_subtransaction_begin(oldcontext, oldowner);
77
78 PG_TRY();
79 {
80 int i;
81
82 for (i = 0; i < nargs; i++)
83 {
84 char *sptr;
85 Oid typeId;
86 int32 typmod;
87
88 optr = PySequence_GetItem(list, i);
89 if (PyUnicode_Check(optr))
90 sptr = PLyUnicode_AsString(optr);
91 else
92 {
94 (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
95 sptr = NULL; /* keep compiler quiet */
96 }
97
98 /********************************************************
99 * Resolve argument type names and then look them up by
100 * oid in the system cache, and remember the required
101 *information for input conversion.
102 ********************************************************/
103
104 (void) parseTypeString(sptr, &typeId, &typmod, NULL);
105
106 Py_DECREF(optr);
107
108 /*
109 * set optr to NULL, so we won't try to unref it again in case of
110 * an error
111 */
112 optr = NULL;
113
114 plan->types[i] = typeId;
115 PLy_output_setup_func(&plan->args[i], plan->mcxt,
116 typeId, typmod,
117 exec_ctx->curr_proc);
118 }
119
120 pg_verifymbstr(query, strlen(query), false);
121 plan->plan = SPI_prepare(query, plan->nargs, plan->types);
122 if (plan->plan == NULL)
123 elog(ERROR, "SPI_prepare failed: %s",
125
126 /* transfer plan from procCxt to topCxt */
127 if (SPI_keepplan(plan->plan))
128 elog(ERROR, "SPI_keepplan failed");
129
130 PLy_spi_subtransaction_commit(oldcontext, oldowner);
131 }
132 PG_CATCH();
133 {
134 Py_DECREF(plan);
135 Py_XDECREF(optr);
136
137 PLy_spi_subtransaction_abort(oldcontext, oldowner);
138 return NULL;
139 }
140 PG_END_TRY();
141
142 Assert(plan->plan != NULL);
143 return (PyObject *) plan;
144}
145
146/* execute(query="select * from foo", limit=5)
147 * execute(plan=plan, values=(foo, bar), limit=5)
148 */
149PyObject *
150PLy_spi_execute(PyObject *self, PyObject *args)
151{
152 char *query;
153 PyObject *plan;
154 PyObject *list = NULL;
155 long limit = 0;
156
157 if (PyArg_ParseTuple(args, "s|l", &query, &limit))
158 return PLy_spi_execute_query(query, limit);
159
160 PyErr_Clear();
161
162 if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
164 return PLy_spi_execute_plan(plan, list, limit);
165
166 PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
167 return NULL;
168}
169
170PyObject *
171PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
172{
173 volatile int nargs;
174 int rv;
176 volatile MemoryContext oldcontext;
177 volatile ResourceOwner oldowner;
178 PyObject *ret;
179
180 if (list != NULL)
181 {
182 if (!PySequence_Check(list) || PyUnicode_Check(list))
183 {
184 PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
185 return NULL;
186 }
187 nargs = PySequence_Length(list);
188 }
189 else
190 nargs = 0;
191
192 plan = (PLyPlanObject *) ob;
193
194 if (nargs != plan->nargs)
195 {
196 char *sv;
197 PyObject *so = PyObject_Str(list);
198
199 if (!so)
200 PLy_elog(ERROR, "could not execute plan");
201 sv = PLyUnicode_AsString(so);
202 PLy_exception_set_plural(PyExc_TypeError,
203 "Expected sequence of %d argument, got %d: %s",
204 "Expected sequence of %d arguments, got %d: %s",
205 plan->nargs,
206 plan->nargs, nargs, sv);
207 Py_DECREF(so);
208
209 return NULL;
210 }
211
212 oldcontext = CurrentMemoryContext;
213 oldowner = CurrentResourceOwner;
214
215 PLy_spi_subtransaction_begin(oldcontext, oldowner);
216
217 PG_TRY();
218 {
220 MemoryContext tmpcontext;
221 Datum *volatile values;
222 char *volatile nulls;
223 volatile int j;
224
225 /*
226 * Converted arguments and associated cruft will be in this context,
227 * which is local to our subtransaction.
228 */
230 "PL/Python temporary context",
232 MemoryContextSwitchTo(tmpcontext);
233
234 if (nargs > 0)
235 {
236 values = (Datum *) palloc(nargs * sizeof(Datum));
237 nulls = (char *) palloc(nargs * sizeof(char));
238 }
239 else
240 {
241 values = NULL;
242 nulls = NULL;
243 }
244
245 for (j = 0; j < nargs; j++)
246 {
247 PLyObToDatum *arg = &plan->args[j];
248 PyObject *elem;
249
250 elem = PySequence_GetItem(list, j);
251 PG_TRY(2);
252 {
253 bool isnull;
254
255 values[j] = PLy_output_convert(arg, elem, &isnull);
256 nulls[j] = isnull ? 'n' : ' ';
257 }
258 PG_FINALLY(2);
259 {
260 Py_DECREF(elem);
261 }
262 PG_END_TRY(2);
263 }
264
265 MemoryContextSwitchTo(oldcontext);
266
267 rv = SPI_execute_plan(plan->plan, values, nulls,
268 exec_ctx->curr_proc->fn_readonly, limit);
270
271 MemoryContextDelete(tmpcontext);
272 PLy_spi_subtransaction_commit(oldcontext, oldowner);
273 }
274 PG_CATCH();
275 {
276 /* Subtransaction abort will remove the tmpcontext */
277 PLy_spi_subtransaction_abort(oldcontext, oldowner);
278 return NULL;
279 }
280 PG_END_TRY();
281
282 if (rv < 0)
283 {
285 "SPI_execute_plan failed: %s",
287 return NULL;
288 }
289
290 return ret;
291}
292
293static PyObject *
294PLy_spi_execute_query(char *query, long limit)
295{
296 int rv;
297 volatile MemoryContext oldcontext;
298 volatile ResourceOwner oldowner;
299 PyObject *ret = NULL;
300
301 oldcontext = CurrentMemoryContext;
302 oldowner = CurrentResourceOwner;
303
304 PLy_spi_subtransaction_begin(oldcontext, oldowner);
305
306 PG_TRY();
307 {
309
310 pg_verifymbstr(query, strlen(query), false);
311 rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
313
314 PLy_spi_subtransaction_commit(oldcontext, oldowner);
315 }
316 PG_CATCH();
317 {
318 PLy_spi_subtransaction_abort(oldcontext, oldowner);
319 return NULL;
320 }
321 PG_END_TRY();
322
323 if (rv < 0)
324 {
325 Py_XDECREF(ret);
327 "SPI_execute failed: %s",
329 return NULL;
330 }
331
332 return ret;
333}
334
335static PyObject *
337{
338 PLyResultObject *result;
340 volatile MemoryContext oldcontext;
341
342 result = (PLyResultObject *) PLy_result_new();
343 if (!result)
344 {
345 SPI_freetuptable(tuptable);
346 return NULL;
347 }
348 Py_DECREF(result->status);
349 result->status = PyLong_FromLong(status);
350
351 if (status > 0 && tuptable == NULL)
352 {
353 Py_DECREF(result->nrows);
354 result->nrows = PyLong_FromUnsignedLongLong(rows);
355 }
356 else if (status > 0 && tuptable != NULL)
357 {
358 PLyDatumToOb ininfo;
359 MemoryContext cxt;
360
361 Py_DECREF(result->nrows);
362 result->nrows = PyLong_FromUnsignedLongLong(rows);
363
365 "PL/Python temp context",
367
368 /* Initialize for converting result tuples to Python */
369 PLy_input_setup_func(&ininfo, cxt, RECORDOID, -1,
370 exec_ctx->curr_proc);
371
372 oldcontext = CurrentMemoryContext;
373 PG_TRY();
374 {
375 MemoryContext oldcontext2;
376
377 if (rows)
378 {
379 uint64 i;
380
381 /*
382 * PyList_New() and PyList_SetItem() use Py_ssize_t for list
383 * size and list indices; so we cannot support a result larger
384 * than PY_SSIZE_T_MAX.
385 */
386 if (rows > (uint64) PY_SSIZE_T_MAX)
388 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
389 errmsg("query result has too many rows to fit in a Python list")));
390
391 Py_DECREF(result->rows);
392 result->rows = PyList_New(rows);
393 if (result->rows)
394 {
395 PLy_input_setup_tuple(&ininfo, tuptable->tupdesc,
396 exec_ctx->curr_proc);
397
398 for (i = 0; i < rows; i++)
399 {
400 PyObject *row = PLy_input_from_tuple(&ininfo,
401 tuptable->vals[i],
402 tuptable->tupdesc,
403 true);
404
405 PyList_SetItem(result->rows, i, row);
406 }
407 }
408 }
409
410 /*
411 * Save tuple descriptor for later use by result set metadata
412 * functions. Save it in TopMemoryContext so that it survives
413 * outside of an SPI context. We trust that PLy_result_dealloc()
414 * will clean it up when the time is right. (Do this as late as
415 * possible, to minimize the number of ways the tupdesc could get
416 * leaked due to errors.)
417 */
419 result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
420 MemoryContextSwitchTo(oldcontext2);
421 }
422 PG_CATCH();
423 {
424 MemoryContextSwitchTo(oldcontext);
426 Py_DECREF(result);
427 PG_RE_THROW();
428 }
429 PG_END_TRY();
430
432 SPI_freetuptable(tuptable);
433
434 /* in case PyList_New() failed above */
435 if (!result->rows)
436 {
437 Py_DECREF(result);
438 result = NULL;
439 }
440 }
441
442 return (PyObject *) result;
443}
444
445PyObject *
446PLy_commit(PyObject *self, PyObject *args)
447{
450
451 PG_TRY();
452 {
453 SPI_commit();
454
455 /* was cleared at transaction end, reset pointer */
456 exec_ctx->scratch_ctx = NULL;
457 }
458 PG_CATCH();
459 {
460 ErrorData *edata;
461 PLyExceptionEntry *entry;
462 PyObject *exc;
463
464 /* Save error info */
465 MemoryContextSwitchTo(oldcontext);
466 edata = CopyErrorData();
468
469 /* was cleared at transaction end, reset pointer */
470 exec_ctx->scratch_ctx = NULL;
471
472 /* Look up the correct exception */
473 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
474 HASH_FIND, NULL);
475
476 /*
477 * This could be a custom error code, if that's the case fallback to
478 * SPIError
479 */
480 exc = entry ? entry->exc : PLy_exc_spi_error;
481 /* Make Python raise the exception */
482 PLy_spi_exception_set(exc, edata);
483 FreeErrorData(edata);
484
485 return NULL;
486 }
487 PG_END_TRY();
488
489 Py_RETURN_NONE;
490}
491
492PyObject *
493PLy_rollback(PyObject *self, PyObject *args)
494{
497
498 PG_TRY();
499 {
500 SPI_rollback();
501
502 /* was cleared at transaction end, reset pointer */
503 exec_ctx->scratch_ctx = NULL;
504 }
505 PG_CATCH();
506 {
507 ErrorData *edata;
508 PLyExceptionEntry *entry;
509 PyObject *exc;
510
511 /* Save error info */
512 MemoryContextSwitchTo(oldcontext);
513 edata = CopyErrorData();
515
516 /* was cleared at transaction end, reset pointer */
517 exec_ctx->scratch_ctx = NULL;
518
519 /* Look up the correct exception */
520 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
521 HASH_FIND, NULL);
522
523 /*
524 * This could be a custom error code, if that's the case fallback to
525 * SPIError
526 */
527 exc = entry ? entry->exc : PLy_exc_spi_error;
528 /* Make Python raise the exception */
529 PLy_spi_exception_set(exc, edata);
530 FreeErrorData(edata);
531
532 return NULL;
533 }
534 PG_END_TRY();
535
536 Py_RETURN_NONE;
537}
538
539/*
540 * Utilities for running SPI functions in subtransactions.
541 *
542 * Usage:
543 *
544 * MemoryContext oldcontext = CurrentMemoryContext;
545 * ResourceOwner oldowner = CurrentResourceOwner;
546 *
547 * PLy_spi_subtransaction_begin(oldcontext, oldowner);
548 * PG_TRY();
549 * {
550 * <call SPI functions>
551 * PLy_spi_subtransaction_commit(oldcontext, oldowner);
552 * }
553 * PG_CATCH();
554 * {
555 * <do cleanup>
556 * PLy_spi_subtransaction_abort(oldcontext, oldowner);
557 * return NULL;
558 * }
559 * PG_END_TRY();
560 *
561 * These utilities take care of restoring connection to the SPI manager and
562 * setting a Python exception in case of an abort.
563 */
564void
566{
568 /* Want to run inside function's memory context */
569 MemoryContextSwitchTo(oldcontext);
570}
571
572void
574{
575 /* Commit the inner transaction, return to outer xact context */
577 MemoryContextSwitchTo(oldcontext);
578 CurrentResourceOwner = oldowner;
579}
580
581void
583{
584 ErrorData *edata;
585 PLyExceptionEntry *entry;
586 PyObject *exc;
587
588 /* Save error info */
589 MemoryContextSwitchTo(oldcontext);
590 edata = CopyErrorData();
592
593 /* Abort the inner transaction */
595 MemoryContextSwitchTo(oldcontext);
596 CurrentResourceOwner = oldowner;
597
598 /* Look up the correct exception */
599 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
600 HASH_FIND, NULL);
601
602 /*
603 * This could be a custom error code, if that's the case fallback to
604 * SPIError
605 */
606 exc = entry ? entry->exc : PLy_exc_spi_error;
607 /* Make Python raise the exception */
608 PLy_spi_exception_set(exc, edata);
609 FreeErrorData(edata);
610}
611
612/*
613 * Raise a SPIError, passing in it more error details, like the
614 * internal query and error position.
615 */
616static void
617PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
618{
619 PyObject *args = NULL;
620 PyObject *spierror = NULL;
621 PyObject *spidata = NULL;
622
623 args = Py_BuildValue("(s)", edata->message);
624 if (!args)
625 goto failure;
626
627 /* create a new SPI exception with the error message as the parameter */
628 spierror = PyObject_CallObject(excclass, args);
629 if (!spierror)
630 goto failure;
631
632 spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
633 edata->internalquery, edata->internalpos,
634 edata->schema_name, edata->table_name, edata->column_name,
635 edata->datatype_name, edata->constraint_name);
636 if (!spidata)
637 goto failure;
638
639 if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
640 goto failure;
641
642 PyErr_SetObject(excclass, spierror);
643
644 Py_DECREF(args);
645 Py_DECREF(spierror);
646 Py_DECREF(spidata);
647 return;
648
649failure:
650 Py_XDECREF(args);
651 Py_XDECREF(spierror);
652 Py_XDECREF(spidata);
653 elog(ERROR, "could not convert SPI error to Python exception");
654}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
int32_t int32
Definition: c.h:498
uint64_t uint64
Definition: c.h:503
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1818
ErrorData * CopyErrorData(void)
Definition: elog.c:1746
void FlushErrorState(void)
Definition: elog.c:1867
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define PG_RE_THROW()
Definition: elog.h:404
#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 elog(elevel,...)
Definition: elog.h:225
#define PG_FINALLY(...)
Definition: elog.h:388
#define ereport(elevel,...)
Definition: elog.h:149
Assert(PointerIsAligned(start, uint64))
@ HASH_FIND
Definition: hsearch.h:113
int j
Definition: isn.c:75
int i
Definition: isn.c:74
#define PLy_elog
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
Definition: mbutils.c:1556
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * palloc(Size size)
Definition: mcxt.c:1317
MemoryContext CurTransactionContext
Definition: mcxt.c:155
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
#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
bool parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, Node *escontext)
Definition: parse_type.c:785
void * arg
#define plan(x)
Definition: pg_regress.c:161
PyObject * PLy_exc_error
Definition: plpy_elog.c:15
PyObject * PLy_exc_spi_error
Definition: plpy_elog.c:17
void PLy_exception_set(PyObject *exc, const char *fmt,...)
Definition: plpy_elog.c:472
void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: plpy_elog.c:486
PLyExecutionContext * PLy_current_execution_context(void)
Definition: plpy_main.c:365
PyObject * PLy_plan_new(void)
bool is_PLyPlanObject(PyObject *ob)
HTAB * PLy_spi_exceptions
PyObject * PLy_result_new(void)
void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:573
PyObject * PLy_spi_prepare(PyObject *self, PyObject *args)
Definition: plpy_spi.c:36
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
Definition: plpy_spi.c:336
PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
Definition: plpy_spi.c:171
PyObject * PLy_commit(PyObject *self, PyObject *args)
Definition: plpy_spi.c:446
PyObject * PLy_rollback(PyObject *self, PyObject *args)
Definition: plpy_spi.c:493
static PyObject * PLy_spi_execute_query(char *query, long limit)
Definition: plpy_spi.c:294
PyObject * PLy_spi_execute(PyObject *self, PyObject *args)
Definition: plpy_spi.c:150
void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:582
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
Definition: plpy_spi.c:617
void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:565
void PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt, Oid typeOid, int32 typmod, PLyProcedure *proc)
Definition: plpy_typeio.c:296
void PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, int32 typmod, PLyProcedure *proc)
Definition: plpy_typeio.c:418
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
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
uintptr_t Datum
Definition: postgres.h:69
unsigned int Oid
Definition: postgres_ext.h:32
ResourceOwner CurrentResourceOwner
Definition: resowner.c:173
void SPI_commit(void)
Definition: spi.c:321
uint64 SPI_processed
Definition: spi.c:44
const char * SPI_result_code_string(int code)
Definition: spi.c:1974
SPITupleTable * SPI_tuptable
Definition: spi.c:45
int SPI_result
Definition: spi.c:46
int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition: spi.c:673
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:1387
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:861
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:977
void SPI_rollback(void)
Definition: spi.c:414
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:597
int internalpos
Definition: elog.h:444
char * schema_name
Definition: elog.h:438
char * internalquery
Definition: elog.h:445
int sqlerrcode
Definition: elog.h:430
char * datatype_name
Definition: elog.h:441
char * detail
Definition: elog.h:432
char * table_name
Definition: elog.h:439
char * message
Definition: elog.h:431
char * hint
Definition: elog.h:434
char * constraint_name
Definition: elog.h:442
char * column_name
Definition: elog.h:440
PyObject * exc
Definition: plpy_spi.h:21
PLyProcedure * curr_proc
Definition: plpy_main.h:20
MemoryContext scratch_ctx
Definition: plpy_main.h:21
PyObject_HEAD PyObject * nrows
TupleDesc tupdesc
Definition: spi.h:25
HeapTuple * vals
Definition: spi.h:26
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:234
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4694
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4796
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4768