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