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