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