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