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