PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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/htup_details.h"
12 #include "access/xact.h"
13 #include "catalog/pg_type.h"
14 #include "executor/spi.h"
15 #include "mb/pg_wchar.h"
16 #include "parser/parse_type.h"
17 #include "utils/memutils.h"
18 #include "utils/syscache.h"
19 
20 #include "plpython.h"
21 
22 #include "plpy_spi.h"
23 
24 #include "plpy_elog.h"
25 #include "plpy_main.h"
26 #include "plpy_planobject.h"
27 #include "plpy_plpymodule.h"
28 #include "plpy_procedure.h"
29 #include "plpy_resultobject.h"
30 
31 
32 static PyObject *PLy_spi_execute_query(char *query, long limit);
33 static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
34 static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
35  uint64 rows, int status);
36 static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
37 
38 
39 /* prepare(query="select * from foo")
40  * prepare(query="select * from foo where bar = $1", params=["text"])
41  * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
42  */
43 PyObject *
44 PLy_spi_prepare(PyObject *self, PyObject *args)
45 {
46  PLyPlanObject *plan;
47  PyObject *list = NULL;
48  PyObject *volatile optr = NULL;
49  char *query;
50  volatile MemoryContext oldcontext;
51  volatile ResourceOwner oldowner;
52  volatile int nargs;
53 
54  if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
55  return NULL;
56 
57  if (list && (!PySequence_Check(list)))
58  {
59  PLy_exception_set(PyExc_TypeError,
60  "second argument of plpy.prepare must be a sequence");
61  return NULL;
62  }
63 
64  if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
65  return NULL;
66 
68  "PL/Python plan context",
70  oldcontext = MemoryContextSwitchTo(plan->mcxt);
71 
72  nargs = list ? PySequence_Length(list) : 0;
73 
74  plan->nargs = nargs;
75  plan->types = nargs ? palloc(sizeof(Oid) * nargs) : NULL;
76  plan->values = nargs ? palloc(sizeof(Datum) * nargs) : NULL;
77  plan->args = nargs ? palloc(sizeof(PLyTypeInfo) * nargs) : NULL;
78 
79  MemoryContextSwitchTo(oldcontext);
80 
81  oldcontext = CurrentMemoryContext;
82  oldowner = CurrentResourceOwner;
83 
84  PLy_spi_subtransaction_begin(oldcontext, oldowner);
85 
86  PG_TRY();
87  {
88  int i;
90 
91  /*
92  * the other loop might throw an exception, if PLyTypeInfo member
93  * isn't properly initialized the Py_DECREF(plan) will go boom
94  */
95  for (i = 0; i < nargs; i++)
96  {
97  PLy_typeinfo_init(&plan->args[i], plan->mcxt);
98  plan->values[i] = PointerGetDatum(NULL);
99  }
100 
101  for (i = 0; i < nargs; i++)
102  {
103  char *sptr;
104  HeapTuple typeTup;
105  Oid typeId;
106  int32 typmod;
107 
108  optr = PySequence_GetItem(list, i);
109  if (PyString_Check(optr))
110  sptr = PyString_AsString(optr);
111  else if (PyUnicode_Check(optr))
112  sptr = PLyUnicode_AsString(optr);
113  else
114  {
115  ereport(ERROR,
116  (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
117  sptr = NULL; /* keep compiler quiet */
118  }
119 
120  /********************************************************
121  * Resolve argument type names and then look them up by
122  * oid in the system cache, and remember the required
123  *information for input conversion.
124  ********************************************************/
125 
126  parseTypeString(sptr, &typeId, &typmod, false);
127 
128  typeTup = SearchSysCache1(TYPEOID,
129  ObjectIdGetDatum(typeId));
130  if (!HeapTupleIsValid(typeTup))
131  elog(ERROR, "cache lookup failed for type %u", typeId);
132 
133  Py_DECREF(optr);
134 
135  /*
136  * set optr to NULL, so we won't try to unref it again in case of
137  * an error
138  */
139  optr = NULL;
140 
141  plan->types[i] = typeId;
142  PLy_output_datum_func(&plan->args[i], typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes);
143  ReleaseSysCache(typeTup);
144  }
145 
146  pg_verifymbstr(query, strlen(query), false);
147  plan->plan = SPI_prepare(query, plan->nargs, plan->types);
148  if (plan->plan == NULL)
149  elog(ERROR, "SPI_prepare failed: %s",
151 
152  /* transfer plan from procCxt to topCxt */
153  if (SPI_keepplan(plan->plan))
154  elog(ERROR, "SPI_keepplan failed");
155 
156  PLy_spi_subtransaction_commit(oldcontext, oldowner);
157  }
158  PG_CATCH();
159  {
160  Py_DECREF(plan);
161  Py_XDECREF(optr);
162 
163  PLy_spi_subtransaction_abort(oldcontext, oldowner);
164  return NULL;
165  }
166  PG_END_TRY();
167 
168  Assert(plan->plan != NULL);
169  return (PyObject *) plan;
170 }
171 
172 /* execute(query="select * from foo", limit=5)
173  * execute(plan=plan, values=(foo, bar), limit=5)
174  */
175 PyObject *
176 PLy_spi_execute(PyObject *self, PyObject *args)
177 {
178  char *query;
179  PyObject *plan;
180  PyObject *list = NULL;
181  long limit = 0;
182 
183  if (PyArg_ParseTuple(args, "s|l", &query, &limit))
184  return PLy_spi_execute_query(query, limit);
185 
186  PyErr_Clear();
187 
188  if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
189  is_PLyPlanObject(plan))
190  return PLy_spi_execute_plan(plan, list, limit);
191 
192  PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
193  return NULL;
194 }
195 
196 static PyObject *
197 PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
198 {
199  volatile int nargs;
200  int i,
201  rv;
202  PLyPlanObject *plan;
203  volatile MemoryContext oldcontext;
204  volatile ResourceOwner oldowner;
205  PyObject *ret;
206 
207  if (list != NULL)
208  {
209  if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
210  {
211  PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
212  return NULL;
213  }
214  nargs = PySequence_Length(list);
215  }
216  else
217  nargs = 0;
218 
219  plan = (PLyPlanObject *) ob;
220 
221  if (nargs != plan->nargs)
222  {
223  char *sv;
224  PyObject *so = PyObject_Str(list);
225 
226  if (!so)
227  PLy_elog(ERROR, "could not execute plan");
228  sv = PyString_AsString(so);
229  PLy_exception_set_plural(PyExc_TypeError,
230  "Expected sequence of %d argument, got %d: %s",
231  "Expected sequence of %d arguments, got %d: %s",
232  plan->nargs,
233  plan->nargs, nargs, sv);
234  Py_DECREF(so);
235 
236  return NULL;
237  }
238 
239  oldcontext = CurrentMemoryContext;
240  oldowner = CurrentResourceOwner;
241 
242  PLy_spi_subtransaction_begin(oldcontext, oldowner);
243 
244  PG_TRY();
245  {
247  char *volatile nulls;
248  volatile int j;
249 
250  if (nargs > 0)
251  nulls = palloc(nargs * sizeof(char));
252  else
253  nulls = NULL;
254 
255  for (j = 0; j < nargs; j++)
256  {
257  PyObject *elem;
258 
259  elem = PySequence_GetItem(list, j);
260  if (elem != Py_None)
261  {
262  PG_TRY();
263  {
264  plan->values[j] =
265  plan->args[j].out.d.func(&(plan->args[j].out.d),
266  -1,
267  elem,
268  false);
269  }
270  PG_CATCH();
271  {
272  Py_DECREF(elem);
273  PG_RE_THROW();
274  }
275  PG_END_TRY();
276 
277  Py_DECREF(elem);
278  nulls[j] = ' ';
279  }
280  else
281  {
282  Py_DECREF(elem);
283  plan->values[j] =
284  InputFunctionCall(&(plan->args[j].out.d.typfunc),
285  NULL,
286  plan->args[j].out.d.typioparam,
287  -1);
288  nulls[j] = 'n';
289  }
290  }
291 
292  rv = SPI_execute_plan(plan->plan, plan->values, nulls,
293  exec_ctx->curr_proc->fn_readonly, limit);
295 
296  if (nargs > 0)
297  pfree(nulls);
298 
299  PLy_spi_subtransaction_commit(oldcontext, oldowner);
300  }
301  PG_CATCH();
302  {
303  int k;
304 
305  /*
306  * cleanup plan->values array
307  */
308  for (k = 0; k < nargs; k++)
309  {
310  if (!plan->args[k].out.d.typbyval &&
311  (plan->values[k] != PointerGetDatum(NULL)))
312  {
313  pfree(DatumGetPointer(plan->values[k]));
314  plan->values[k] = PointerGetDatum(NULL);
315  }
316  }
317 
318  PLy_spi_subtransaction_abort(oldcontext, oldowner);
319  return NULL;
320  }
321  PG_END_TRY();
322 
323  for (i = 0; i < nargs; i++)
324  {
325  if (!plan->args[i].out.d.typbyval &&
326  (plan->values[i] != PointerGetDatum(NULL)))
327  {
328  pfree(DatumGetPointer(plan->values[i]));
329  plan->values[i] = PointerGetDatum(NULL);
330  }
331  }
332 
333  if (rv < 0)
334  {
336  "SPI_execute_plan failed: %s",
338  return NULL;
339  }
340 
341  return ret;
342 }
343 
344 static PyObject *
345 PLy_spi_execute_query(char *query, long limit)
346 {
347  int rv;
348  volatile MemoryContext oldcontext;
349  volatile ResourceOwner oldowner;
350  PyObject *ret = NULL;
351 
352  oldcontext = CurrentMemoryContext;
353  oldowner = CurrentResourceOwner;
354 
355  PLy_spi_subtransaction_begin(oldcontext, oldowner);
356 
357  PG_TRY();
358  {
360 
361  pg_verifymbstr(query, strlen(query), false);
362  rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
364 
365  PLy_spi_subtransaction_commit(oldcontext, oldowner);
366  }
367  PG_CATCH();
368  {
369  PLy_spi_subtransaction_abort(oldcontext, oldowner);
370  return NULL;
371  }
372  PG_END_TRY();
373 
374  if (rv < 0)
375  {
376  Py_XDECREF(ret);
378  "SPI_execute failed: %s",
380  return NULL;
381  }
382 
383  return ret;
384 }
385 
386 static PyObject *
388 {
389  PLyResultObject *result;
390  volatile MemoryContext oldcontext;
391 
392  result = (PLyResultObject *) PLy_result_new();
393  Py_DECREF(result->status);
394  result->status = PyInt_FromLong(status);
395 
396  if (status > 0 && tuptable == NULL)
397  {
398  Py_DECREF(result->nrows);
399  result->nrows = (rows > (uint64) LONG_MAX) ?
400  PyFloat_FromDouble((double) rows) :
401  PyInt_FromLong((long) rows);
402  }
403  else if (status > 0 && tuptable != NULL)
404  {
406  MemoryContext cxt;
407 
408  Py_DECREF(result->nrows);
409  result->nrows = (rows > (uint64) LONG_MAX) ?
410  PyFloat_FromDouble((double) rows) :
411  PyInt_FromLong((long) rows);
412 
414  "PL/Python temp context",
416  PLy_typeinfo_init(&args, cxt);
417 
418  oldcontext = CurrentMemoryContext;
419  PG_TRY();
420  {
421  MemoryContext oldcontext2;
422 
423  if (rows)
424  {
425  uint64 i;
426 
427  /*
428  * PyList_New() and PyList_SetItem() use Py_ssize_t for list
429  * size and list indices; so we cannot support a result larger
430  * than PY_SSIZE_T_MAX.
431  */
432  if (rows > (uint64) PY_SSIZE_T_MAX)
433  ereport(ERROR,
434  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
435  errmsg("query result has too many rows to fit in a Python list")));
436 
437  Py_DECREF(result->rows);
438  result->rows = PyList_New(rows);
439 
440  PLy_input_tuple_funcs(&args, tuptable->tupdesc);
441  for (i = 0; i < rows; i++)
442  {
443  PyObject *row = PLyDict_FromTuple(&args,
444  tuptable->vals[i],
445  tuptable->tupdesc);
446 
447  PyList_SetItem(result->rows, i, row);
448  }
449  }
450 
451  /*
452  * Save tuple descriptor for later use by result set metadata
453  * functions. Save it in TopMemoryContext so that it survives
454  * outside of an SPI context. We trust that PLy_result_dealloc()
455  * will clean it up when the time is right. (Do this as late as
456  * possible, to minimize the number of ways the tupdesc could get
457  * leaked due to errors.)
458  */
460  result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
461  MemoryContextSwitchTo(oldcontext2);
462  }
463  PG_CATCH();
464  {
465  MemoryContextSwitchTo(oldcontext);
466  MemoryContextDelete(cxt);
467  Py_DECREF(result);
468  PG_RE_THROW();
469  }
470  PG_END_TRY();
471 
472  MemoryContextDelete(cxt);
473  SPI_freetuptable(tuptable);
474  }
475 
476  return (PyObject *) result;
477 }
478 
479 /*
480  * Utilities for running SPI functions in subtransactions.
481  *
482  * Usage:
483  *
484  * MemoryContext oldcontext = CurrentMemoryContext;
485  * ResourceOwner oldowner = CurrentResourceOwner;
486  *
487  * PLy_spi_subtransaction_begin(oldcontext, oldowner);
488  * PG_TRY();
489  * {
490  * <call SPI functions>
491  * PLy_spi_subtransaction_commit(oldcontext, oldowner);
492  * }
493  * PG_CATCH();
494  * {
495  * <do cleanup>
496  * PLy_spi_subtransaction_abort(oldcontext, oldowner);
497  * return NULL;
498  * }
499  * PG_END_TRY();
500  *
501  * These utilities take care of restoring connection to the SPI manager and
502  * setting a Python exception in case of an abort.
503  */
504 void
506 {
508  /* Want to run inside function's memory context */
509  MemoryContextSwitchTo(oldcontext);
510 }
511 
512 void
514 {
515  /* Commit the inner transaction, return to outer xact context */
517  MemoryContextSwitchTo(oldcontext);
518  CurrentResourceOwner = oldowner;
519 }
520 
521 void
523 {
524  ErrorData *edata;
525  PLyExceptionEntry *entry;
526  PyObject *exc;
527 
528  /* Save error info */
529  MemoryContextSwitchTo(oldcontext);
530  edata = CopyErrorData();
531  FlushErrorState();
532 
533  /* Abort the inner transaction */
535  MemoryContextSwitchTo(oldcontext);
536  CurrentResourceOwner = oldowner;
537 
538  /* Look up the correct exception */
539  entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
540  HASH_FIND, NULL);
541 
542  /*
543  * This could be a custom error code, if that's the case fallback to
544  * SPIError
545  */
546  exc = entry ? entry->exc : PLy_exc_spi_error;
547  /* Make Python raise the exception */
548  PLy_spi_exception_set(exc, edata);
549  FreeErrorData(edata);
550 }
551 
552 /*
553  * Raise a SPIError, passing in it more error details, like the
554  * internal query and error position.
555  */
556 static void
557 PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
558 {
559  PyObject *args = NULL;
560  PyObject *spierror = NULL;
561  PyObject *spidata = NULL;
562 
563  args = Py_BuildValue("(s)", edata->message);
564  if (!args)
565  goto failure;
566 
567  /* create a new SPI exception with the error message as the parameter */
568  spierror = PyObject_CallObject(excclass, args);
569  if (!spierror)
570  goto failure;
571 
572  spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
573  edata->internalquery, edata->internalpos,
574  edata->schema_name, edata->table_name, edata->column_name,
575  edata->datatype_name, edata->constraint_name);
576  if (!spidata)
577  goto failure;
578 
579  if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
580  goto failure;
581 
582  PyErr_SetObject(excclass, spierror);
583 
584  Py_DECREF(args);
585  Py_DECREF(spierror);
586  Py_DECREF(spidata);
587  return;
588 
589 failure:
590  Py_XDECREF(args);
591  Py_XDECREF(spierror);
592  Py_XDECREF(spidata);
593  elog(ERROR, "could not convert SPI error to Python exception");
594 }
FmgrInfo typfunc
Definition: plpy_typeio.h:58
char * schema_name
Definition: elog.h:349
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
Definition: plpy_spi.c:557
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:141
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: plpy_elog.c:514
void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:522
void PLy_typeinfo_init(PLyTypeInfo *arg, MemoryContext mcxt)
Definition: plpy_typeio.c:70
int sqlerrcode
Definition: elog.h:342
void PLy_output_datum_func(PLyTypeInfo *arg, HeapTuple typeTup, Oid langid, List *trftypes)
Definition: plpy_typeio.c:96
ErrorData * CopyErrorData(void)
Definition: elog.c:1497
#define PointerGetDatum(X)
Definition: postgres.h:564
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:481
void PLy_elog(int elevel, const char *fmt,...)
Definition: plpy_elog.c:47
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4121
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
SPITupleTable * SPI_tuptable
Definition: spi.c:41
int errcode(int sqlerrcode)
Definition: elog.c:575
void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
Definition: plpy_typeio.c:105
PyObject * PLy_exc_error
Definition: plpy_elog.c:19
char * internalquery
Definition: elog.h:356
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:885
static PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
Definition: plpy_spi.c:197
unsigned int Oid
Definition: postgres_ext.h:31
PLyExecutionContext * PLy_current_execution_context(void)
Definition: plpy_main.c:398
void PLy_exception_set(PyObject *exc, const char *fmt,...)
Definition: plpy_elog.c:500
HeapTuple * vals
Definition: spi.h:27
bool is_PLyPlanObject(PyObject *ob)
PLyObToDatumFunc func
Definition: plpy_typeio.h:57
void FlushErrorState(void)
Definition: elog.c:1587
uint64 SPI_processed
Definition: spi.c:39
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
signed int int32
Definition: c.h:253
char * PLyUnicode_AsString(PyObject *unicode)
Definition: plpy_util.c:94
int SPI_result
Definition: spi.c:42
void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:505
void pfree(void *pointer)
Definition: mcxt.c:992
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1551
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
PLyObToDatum d
Definition: plpy_typeio.h:77
PyObject * PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
Definition: plpy_typeio.c:280
PLyTypeInfo * args
int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition: spi.c:338
const char * SPI_result_code_string(int code)
Definition: spi.c:1509
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:145
char * table_name
Definition: elog.h:350
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:559
PLyTypeOutput out
Definition: plpy_typeio.h:87
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4155
int internalpos
Definition: elog.h:355
PyObject_HEAD SPIPlanPtr plan
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
#define ereport(elevel, rest)
Definition: elog.h:122
MemoryContext TopMemoryContext
Definition: mcxt.c:43
PLyProcedure * curr_proc
Definition: plpy_main.h:20
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:969
char * datatype_name
Definition: elog.h:352
char * detail
Definition: elog.h:344
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
Definition: plpy_spi.c:387
MemoryContext mcxt
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:440
PyObject * PLy_plan_new(void)
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
TupleDesc tupdesc
Definition: spi.h:26
void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok)
Definition: parse_type.c:781
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1882
char * column_name
Definition: elog.h:351
#define PG_CATCH()
Definition: elog.h:293
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:671
PyObject * PLy_spi_execute(PyObject *self, PyObject *args)
Definition: plpy_spi.c:176
char * hint
Definition: elog.h:346
void BeginInternalSubTransaction(char *name)
Definition: xact.c:4051
#define PG_RE_THROW()
Definition: elog.h:314
HTAB * PLy_spi_exceptions
#define PY_SSIZE_T_MAX
Definition: plpython.h:66
static PyObject * PLy_spi_execute_query(char *query, long limit)
Definition: plpy_spi.c:345
#define DatumGetPointer(X)
Definition: postgres.h:557
PyObject * PLy_result_new(void)
tuple list
Definition: sort-test.py:11
void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: plpy_spi.c:513
void * palloc(Size size)
Definition: mcxt.c:891
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
PyObject_HEAD PyObject * nrows
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
Definition: wchar.c:1866
#define elog
Definition: elog.h:219
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:222
#define PG_TRY()
Definition: elog.h:284
PyObject * exc
Definition: plpy_spi.h:17
char * constraint_name
Definition: elog.h:353
PyObject * PLy_exc_spi_error
Definition: plpy_elog.c:21
#define PG_END_TRY()
Definition: elog.h:300
char * message
Definition: elog.h:343
PyObject * PLy_spi_prepare(PyObject *self, PyObject *args)
Definition: plpy_spi.c:44
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:303