PostgreSQL Source Code  git master
plpy_elog.c
Go to the documentation of this file.
1 /*
2  * reporting Python exceptions as PostgreSQL errors
3  *
4  * src/pl/plpython/plpy_elog.c
5  */
6 
7 #include "postgres.h"
8 
9 #include "lib/stringinfo.h"
10 
11 #include "plpython.h"
12 
13 #include "plpy_elog.h"
14 
15 #include "plpy_main.h"
16 #include "plpy_procedure.h"
17 
18 
19 PyObject *PLy_exc_error = NULL;
20 PyObject *PLy_exc_fatal = NULL;
21 PyObject *PLy_exc_spi_error = NULL;
22 
23 
24 static void PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
25  char **xmsg, char **tbmsg, int *tb_depth);
26 static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
27  char **hint, char **query, int *position,
28  char **schema_name, char **table_name, char **column_name,
29  char **datatype_name, char **constraint_name);
30 static void PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail,
31  char **hint, char **schema_name, char **table_name, char **column_name,
32  char **datatype_name, char **constraint_name);
33 static char *get_source_line(const char *src, int lineno);
34 
35 static void get_string_attr(PyObject *obj, char *attrname, char **str);
36 static bool set_string_attr(PyObject *obj, char *attrname, char *str);
37 
38 /*
39  * Emit a PG error or notice, together with any available info about
40  * the current Python error, previously set by PLy_exception_set().
41  * This should be used to propagate Python errors into PG. If fmt is
42  * NULL, the Python error becomes the primary error message, otherwise
43  * it becomes the detail. If there is a Python traceback, it is put
44  * in the context.
45  */
46 void
47 PLy_elog_impl(int elevel, const char *fmt,...)
48 {
49  int save_errno = errno;
50  char *xmsg;
51  char *tbmsg;
52  int tb_depth;
53  StringInfoData emsg;
54  PyObject *exc,
55  *val,
56  *tb;
57  const char *primary = NULL;
58  int sqlerrcode = 0;
59  char *detail = NULL;
60  char *hint = NULL;
61  char *query = NULL;
62  int position = 0;
63  char *schema_name = NULL;
64  char *table_name = NULL;
65  char *column_name = NULL;
66  char *datatype_name = NULL;
67  char *constraint_name = NULL;
68 
69  PyErr_Fetch(&exc, &val, &tb);
70 
71  if (exc != NULL)
72  {
73  PyErr_NormalizeException(&exc, &val, &tb);
74 
75  if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
76  PLy_get_spi_error_data(val, &sqlerrcode,
77  &detail, &hint, &query, &position,
78  &schema_name, &table_name, &column_name,
79  &datatype_name, &constraint_name);
80  else if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
81  PLy_get_error_data(val, &sqlerrcode, &detail, &hint,
82  &schema_name, &table_name, &column_name,
83  &datatype_name, &constraint_name);
84  else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
85  elevel = FATAL;
86  }
87 
88  /* this releases our refcount on tb! */
89  PLy_traceback(exc, val, tb,
90  &xmsg, &tbmsg, &tb_depth);
91 
92  if (fmt)
93  {
94  initStringInfo(&emsg);
95  for (;;)
96  {
97  va_list ap;
98  int needed;
99 
100  errno = save_errno;
101  va_start(ap, fmt);
102  needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
103  va_end(ap);
104  if (needed == 0)
105  break;
106  enlargeStringInfo(&emsg, needed);
107  }
108  primary = emsg.data;
109 
110  /* Since we have a format string, we cannot have a SPI detail. */
111  Assert(detail == NULL);
112 
113  /* If there's an exception message, it goes in the detail. */
114  if (xmsg)
115  detail = xmsg;
116  }
117  else
118  {
119  if (xmsg)
120  primary = xmsg;
121  }
122 
123  PG_TRY();
124  {
125  ereport(elevel,
126  (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
127  errmsg_internal("%s", primary ? primary : "no exception data"),
128  (detail) ? errdetail_internal("%s", detail) : 0,
129  (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
130  (hint) ? errhint("%s", hint) : 0,
131  (query) ? internalerrquery(query) : 0,
132  (position) ? internalerrposition(position) : 0,
133  (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME,
134  schema_name) : 0,
135  (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME,
136  table_name) : 0,
137  (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME,
138  column_name) : 0,
139  (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME,
140  datatype_name) : 0,
141  (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
142  constraint_name) : 0));
143  }
144  PG_FINALLY();
145  {
146  if (fmt)
147  pfree(emsg.data);
148  if (xmsg)
149  pfree(xmsg);
150  if (tbmsg)
151  pfree(tbmsg);
152  Py_XDECREF(exc);
153  Py_XDECREF(val);
154  }
155  PG_END_TRY();
156 }
157 
158 /*
159  * Extract a Python traceback from the given exception data.
160  *
161  * The exception error message is returned in xmsg, the traceback in
162  * tbmsg (both as palloc'd strings) and the traceback depth in
163  * tb_depth.
164  *
165  * We release refcounts on all the Python objects in the traceback stack,
166  * but not on e or v.
167  */
168 static void
169 PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
170  char **xmsg, char **tbmsg, int *tb_depth)
171 {
172  PyObject *e_type_o;
173  PyObject *e_module_o;
174  char *e_type_s = NULL;
175  char *e_module_s = NULL;
176  PyObject *vob = NULL;
177  char *vstr;
178  StringInfoData xstr;
179  StringInfoData tbstr;
180 
181  /*
182  * if no exception, return nulls
183  */
184  if (e == NULL)
185  {
186  *xmsg = NULL;
187  *tbmsg = NULL;
188  *tb_depth = 0;
189 
190  return;
191  }
192 
193  /*
194  * Format the exception and its value and put it in xmsg.
195  */
196 
197  e_type_o = PyObject_GetAttrString(e, "__name__");
198  e_module_o = PyObject_GetAttrString(e, "__module__");
199  if (e_type_o)
200  e_type_s = PyString_AsString(e_type_o);
201  if (e_type_s)
202  e_module_s = PyString_AsString(e_module_o);
203 
204  if (v && ((vob = PyObject_Str(v)) != NULL))
205  vstr = PyString_AsString(vob);
206  else
207  vstr = "unknown";
208 
209  initStringInfo(&xstr);
210  if (!e_type_s || !e_module_s)
211  {
212  if (PyString_Check(e))
213  /* deprecated string exceptions */
214  appendStringInfoString(&xstr, PyString_AsString(e));
215  else
216  /* shouldn't happen */
217  appendStringInfoString(&xstr, "unrecognized exception");
218  }
219  /* mimics behavior of traceback.format_exception_only */
220  else if (strcmp(e_module_s, "builtins") == 0
221  || strcmp(e_module_s, "__main__") == 0
222  || strcmp(e_module_s, "exceptions") == 0)
223  appendStringInfo(&xstr, "%s", e_type_s);
224  else
225  appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
226  appendStringInfo(&xstr, ": %s", vstr);
227 
228  *xmsg = xstr.data;
229 
230  /*
231  * Now format the traceback and put it in tbmsg.
232  */
233 
234  *tb_depth = 0;
235  initStringInfo(&tbstr);
236  /* Mimic Python traceback reporting as close as possible. */
237  appendStringInfoString(&tbstr, "Traceback (most recent call last):");
238  while (tb != NULL && tb != Py_None)
239  {
240  PyObject *volatile tb_prev = NULL;
241  PyObject *volatile frame = NULL;
242  PyObject *volatile code = NULL;
243  PyObject *volatile name = NULL;
244  PyObject *volatile lineno = NULL;
245  PyObject *volatile filename = NULL;
246 
247  PG_TRY();
248  {
249  /*
250  * Ancient versions of Python (circa 2.3) contain a bug whereby
251  * the fetches below can fail if the error indicator is set.
252  */
253  PyErr_Clear();
254 
255  lineno = PyObject_GetAttrString(tb, "tb_lineno");
256  if (lineno == NULL)
257  elog(ERROR, "could not get line number from Python traceback");
258 
259  frame = PyObject_GetAttrString(tb, "tb_frame");
260  if (frame == NULL)
261  elog(ERROR, "could not get frame from Python traceback");
262 
263  code = PyObject_GetAttrString(frame, "f_code");
264  if (code == NULL)
265  elog(ERROR, "could not get code object from Python frame");
266 
267  name = PyObject_GetAttrString(code, "co_name");
268  if (name == NULL)
269  elog(ERROR, "could not get function name from Python code object");
270 
271  filename = PyObject_GetAttrString(code, "co_filename");
272  if (filename == NULL)
273  elog(ERROR, "could not get file name from Python code object");
274  }
275  PG_CATCH();
276  {
277  Py_XDECREF(frame);
278  Py_XDECREF(code);
279  Py_XDECREF(name);
280  Py_XDECREF(lineno);
281  Py_XDECREF(filename);
282  PG_RE_THROW();
283  }
284  PG_END_TRY();
285 
286  /* The first frame always points at <module>, skip it. */
287  if (*tb_depth > 0)
288  {
290  char *proname;
291  char *fname;
292  char *line;
293  char *plain_filename;
294  long plain_lineno;
295 
296  /*
297  * The second frame points at the internal function, but to mimic
298  * Python error reporting we want to say <module>.
299  */
300  if (*tb_depth == 1)
301  fname = "<module>";
302  else
303  fname = PyString_AsString(name);
304 
305  proname = PLy_procedure_name(exec_ctx->curr_proc);
306  plain_filename = PyString_AsString(filename);
307  plain_lineno = PyInt_AsLong(lineno);
308 
309  if (proname == NULL)
311  &tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
312  plain_lineno - 1, fname);
313  else
315  &tbstr, "\n PL/Python function \"%s\", line %ld, in %s",
316  proname, plain_lineno - 1, fname);
317 
318  /*
319  * function code object was compiled with "<string>" as the
320  * filename
321  */
322  if (exec_ctx->curr_proc && plain_filename != NULL &&
323  strcmp(plain_filename, "<string>") == 0)
324  {
325  /*
326  * If we know the current procedure, append the exact line
327  * from the source, again mimicking Python's traceback.py
328  * module behavior. We could store the already line-split
329  * source to avoid splitting it every time, but producing a
330  * traceback is not the most important scenario to optimize
331  * for. But we do not go as far as traceback.py in reading
332  * the source of imported modules.
333  */
334  line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
335  if (line)
336  {
337  appendStringInfo(&tbstr, "\n %s", line);
338  pfree(line);
339  }
340  }
341  }
342 
343  Py_DECREF(frame);
344  Py_DECREF(code);
345  Py_DECREF(name);
346  Py_DECREF(lineno);
347  Py_DECREF(filename);
348 
349  /* Release the current frame and go to the next one. */
350  tb_prev = tb;
351  tb = PyObject_GetAttrString(tb, "tb_next");
352  Assert(tb_prev != Py_None);
353  Py_DECREF(tb_prev);
354  if (tb == NULL)
355  elog(ERROR, "could not traverse Python traceback");
356  (*tb_depth)++;
357  }
358 
359  /* Return the traceback. */
360  *tbmsg = tbstr.data;
361 
362  Py_XDECREF(e_type_o);
363  Py_XDECREF(e_module_o);
364  Py_XDECREF(vob);
365 }
366 
367 /*
368  * Extract error code from SPIError's sqlstate attribute.
369  */
370 static void
371 PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
372 {
373  PyObject *sqlstate;
374  char *buffer;
375 
376  sqlstate = PyObject_GetAttrString(exc, "sqlstate");
377  if (sqlstate == NULL)
378  return;
379 
380  buffer = PyString_AsString(sqlstate);
381  if (strlen(buffer) == 5 &&
382  strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
383  {
384  *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
385  buffer[3], buffer[4]);
386  }
387 
388  Py_DECREF(sqlstate);
389 }
390 
391 /*
392  * Extract the error data from a SPIError
393  */
394 static void
395 PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
396  char **hint, char **query, int *position,
397  char **schema_name, char **table_name,
398  char **column_name,
399  char **datatype_name, char **constraint_name)
400 {
401  PyObject *spidata;
402 
403  spidata = PyObject_GetAttrString(exc, "spidata");
404 
405  if (spidata != NULL)
406  {
407  PyArg_ParseTuple(spidata, "izzzizzzzz",
408  sqlerrcode, detail, hint, query, position,
409  schema_name, table_name, column_name,
410  datatype_name, constraint_name);
411  }
412  else
413  {
414  /*
415  * If there's no spidata, at least set the sqlerrcode. This can happen
416  * if someone explicitly raises a SPI exception from Python code.
417  */
418  PLy_get_sqlerrcode(exc, sqlerrcode);
419  }
420 
421  Py_XDECREF(spidata);
422 }
423 
424 /*
425  * Extract the error data from an Error.
426  *
427  * Note: position and query attributes are never set for Error so, unlike
428  * PLy_get_spi_error_data, this function doesn't return them.
429  */
430 static void
431 PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint,
432  char **schema_name, char **table_name, char **column_name,
433  char **datatype_name, char **constraint_name)
434 {
435  PLy_get_sqlerrcode(exc, sqlerrcode);
436  get_string_attr(exc, "detail", detail);
437  get_string_attr(exc, "hint", hint);
438  get_string_attr(exc, "schema_name", schema_name);
439  get_string_attr(exc, "table_name", table_name);
440  get_string_attr(exc, "column_name", column_name);
441  get_string_attr(exc, "datatype_name", datatype_name);
442  get_string_attr(exc, "constraint_name", constraint_name);
443 }
444 
445 /*
446  * Get the given source line as a palloc'd string
447  */
448 static char *
449 get_source_line(const char *src, int lineno)
450 {
451  const char *s = NULL;
452  const char *next = src;
453  int current = 0;
454 
455  /* sanity check */
456  if (lineno <= 0)
457  return NULL;
458 
459  while (current < lineno)
460  {
461  s = next;
462  next = strchr(s + 1, '\n');
463  current++;
464  if (next == NULL)
465  break;
466  }
467 
468  if (current != lineno)
469  return NULL;
470 
471  while (*s && isspace((unsigned char) *s))
472  s++;
473 
474  if (next == NULL)
475  return pstrdup(s);
476 
477  /*
478  * Sanity check, next < s if the line was all-whitespace, which should
479  * never happen if Python reported a frame created on that line, but check
480  * anyway.
481  */
482  if (next < s)
483  return NULL;
484 
485  return pnstrdup(s, next - s);
486 }
487 
488 
489 /* call PyErr_SetString with a vprint interface and translation support */
490 void
491 PLy_exception_set(PyObject *exc, const char *fmt,...)
492 {
493  char buf[1024];
494  va_list ap;
495 
496  va_start(ap, fmt);
497  vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
498  va_end(ap);
499 
500  PyErr_SetString(exc, buf);
501 }
502 
503 /* same, with pluralized message */
504 void
506  const char *fmt_singular, const char *fmt_plural,
507  unsigned long n,...)
508 {
509  char buf[1024];
510  va_list ap;
511 
512  va_start(ap, n);
513  vsnprintf(buf, sizeof(buf),
514  dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
515  ap);
516  va_end(ap);
517 
518  PyErr_SetString(exc, buf);
519 }
520 
521 /* set attributes of the given exception to details from ErrorData */
522 void
523 PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
524 {
525  PyObject *args = NULL;
526  PyObject *error = NULL;
527 
528  args = Py_BuildValue("(s)", edata->message);
529  if (!args)
530  goto failure;
531 
532  /* create a new exception with the error message as the parameter */
533  error = PyObject_CallObject(excclass, args);
534  if (!error)
535  goto failure;
536 
537  if (!set_string_attr(error, "sqlstate",
538  unpack_sql_state(edata->sqlerrcode)))
539  goto failure;
540 
541  if (!set_string_attr(error, "detail", edata->detail))
542  goto failure;
543 
544  if (!set_string_attr(error, "hint", edata->hint))
545  goto failure;
546 
547  if (!set_string_attr(error, "query", edata->internalquery))
548  goto failure;
549 
550  if (!set_string_attr(error, "schema_name", edata->schema_name))
551  goto failure;
552 
553  if (!set_string_attr(error, "table_name", edata->table_name))
554  goto failure;
555 
556  if (!set_string_attr(error, "column_name", edata->column_name))
557  goto failure;
558 
559  if (!set_string_attr(error, "datatype_name", edata->datatype_name))
560  goto failure;
561 
562  if (!set_string_attr(error, "constraint_name", edata->constraint_name))
563  goto failure;
564 
565  PyErr_SetObject(excclass, error);
566 
567  Py_DECREF(args);
568  Py_DECREF(error);
569 
570  return;
571 
572 failure:
573  Py_XDECREF(args);
574  Py_XDECREF(error);
575 
576  elog(ERROR, "could not convert error to Python exception");
577 }
578 
579 /* get string value of an object attribute */
580 static void
581 get_string_attr(PyObject *obj, char *attrname, char **str)
582 {
583  PyObject *val;
584 
585  val = PyObject_GetAttrString(obj, attrname);
586  if (val != NULL && val != Py_None)
587  {
588  *str = pstrdup(PyString_AsString(val));
589  }
590  Py_XDECREF(val);
591 }
592 
593 /* set an object attribute to a string value, returns true when the set was
594  * successful
595  */
596 static bool
597 set_string_attr(PyObject *obj, char *attrname, char *str)
598 {
599  int result;
600  PyObject *val;
601 
602  if (str != NULL)
603  {
604  val = PyString_FromString(str);
605  if (!val)
606  return false;
607  }
608  else
609  {
610  val = Py_None;
611  Py_INCREF(Py_None);
612  }
613 
614  result = PyObject_SetAttrString(obj, attrname, val);
615  Py_DECREF(val);
616 
617  return result != -1;
618 }
char * schema_name
Definition: elog.h:399
void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: plpy_elog.c:505
int errhint(const char *fmt,...)
Definition: elog.c:1071
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1197
NameData proname
Definition: pg_proc.h:36
#define PG_DIAG_SCHEMA_NAME
Definition: postgres_ext.h:65
static int32 next
Definition: blutils.c:213
static void error(void)
Definition: sql-dyntest.c:147
#define TEXTDOMAIN
Definition: elog.h:144
int sqlerrcode
Definition: elog.h:391
static bool set_string_attr(PyObject *obj, char *attrname, char *str)
Definition: plpy_elog.c:597
#define PG_DIAG_COLUMN_NAME
Definition: postgres_ext.h:67
#define MAKE_SQLSTATE(ch1, ch2, ch3, ch4, ch5)
Definition: elog.h:62
char * pstrdup(const char *in)
Definition: mcxt.c:1186
#define PG_DIAG_TABLE_NAME
Definition: postgres_ext.h:66
char * unpack_sql_state(int sql_state)
Definition: elog.c:2941
void PLy_elog_impl(int elevel, const char *fmt,...)
Definition: plpy_elog.c:47
int errcode(int sqlerrcode)
Definition: elog.c:608
PyObject * PLy_exc_error
Definition: plpy_elog.c:19
char * internalquery
Definition: elog.h:406
static void PLy_traceback(PyObject *e, PyObject *v, PyObject *tb, char **xmsg, char **tbmsg, int *tb_depth)
Definition: plpy_elog.c:169
PLyExecutionContext * PLy_current_execution_context(void)
Definition: plpy_main.c:414
void PLy_exception_set(PyObject *exc, const char *fmt,...)
Definition: plpy_elog.c:491
int errdetail_internal(const char *fmt,...)
Definition: elog.c:984
void pfree(void *pointer)
Definition: mcxt.c:1056
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
char * PLy_procedure_name(PLyProcedure *proc)
#define ERROR
Definition: elog.h:43
#define vsnprintf
Definition: port.h:191
#define FATAL
Definition: elog.h:52
static void get_string_attr(PyObject *obj, char *attrname, char **str)
Definition: plpy_elog.c:581
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
char * table_name
Definition: elog.h:400
void PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
Definition: plpy_elog.c:523
static char * buf
Definition: pg_test_fsync.c:67
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:283
#define ereport(elevel, rest)
Definition: elog.h:141
PLyProcedure * curr_proc
Definition: plpy_main.h:20
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
char * datatype_name
Definition: elog.h:402
static int elevel
Definition: vacuumlazy.c:143
char * detail
Definition: elog.h:393
static void PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint, char **schema_name, char **table_name, char **column_name, char **datatype_name, char **constraint_name)
Definition: plpy_elog.c:431
#define PG_FINALLY()
Definition: elog.h:339
#define dgettext(d, x)
Definition: c.h:1127
#define PG_DIAG_DATATYPE_NAME
Definition: postgres_ext.h:68
#define PG_DIAG_CONSTRAINT_NAME
Definition: postgres_ext.h:69
#define dngettext(d, s, p, n)
Definition: c.h:1129
char * column_name
Definition: elog.h:401
int internalerrquery(const char *query)
Definition: elog.c:1245
int errmsg_internal(const char *fmt,...)
Definition: elog.c:911
#define PG_CATCH()
Definition: elog.h:332
PyObject * PLy_exc_fatal
Definition: plpy_elog.c:20
#define Assert(condition)
Definition: c.h:733
char * hint
Definition: elog.h:395
#define PG_RE_THROW()
Definition: elog.h:363
static void PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
Definition: plpy_elog.c:371
const char * name
Definition: encode.c:521
static char * filename
Definition: pg_dumpall.c:90
e
Definition: preproc-init.c:82
#define elog(elevel,...)
Definition: elog.h:228
#define errcontext
Definition: elog.h:183
int err_generic_string(int field, const char *str)
Definition: elog.c:1275
#define PG_TRY()
Definition: elog.h:322
char * constraint_name
Definition: elog.h:403
int appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
Definition: stringinfo.c:133
PyObject * PLy_exc_spi_error
Definition: plpy_elog.c:21
long val
Definition: informix.c:684
static char * get_source_line(const char *src, int lineno)
Definition: plpy_elog.c:449
#define PG_END_TRY()
Definition: elog.h:347
char * message
Definition: elog.h:392
static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint, char **query, int *position, char **schema_name, char **table_name, char **column_name, char **datatype_name, char **constraint_name)
Definition: plpy_elog.c:395
int internalerrposition(int cursorpos)
Definition: elog.c:1225