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
3038 peter_e@gmx.net 44 :CBC 58 : PLy_elog_impl(int elevel, const char *fmt,...)
45 : : {
2638 tgl@sss.pgh.pa.us 46 : 58 : int save_errno = errno;
198 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))
198 tgl@sss.pgh.pa.us 90 :UBC 0 : elevel = FATAL;
91 : : }
92 : :
198 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 (;;)
198 tgl@sss.pgh.pa.us 99 :UBC 0 : {
100 : : va_list ap;
101 : : int needed;
102 : :
198 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;
198 tgl@sss.pgh.pa.us 109 :UBC 0 : enlargeStringInfo(&emsg, needed);
110 : : }
198 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 : :
5112 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 : : }
2237 peter@eisentraut.org 142 : 58 : PG_FINALLY();
143 : : {
198 tgl@sss.pgh.pa.us 144 : 58 : Py_XDECREF(exc);
145 : 58 : Py_XDECREF(val);
54 146 : 58 : Py_XDECREF(tb);
147 : : /* For neatness' sake, also release our string buffers */
5112 peter_e@gmx.net 148 [ + + ]: 58 : if (fmt)
149 : 4 : pfree(emsg.data);
150 [ + - ]: 58 : if (xmsg)
151 : 58 : pfree(xmsg);
152 [ + - ]: 58 : if (tbmsg)
153 : 58 : pfree(tbmsg);
154 : : }
155 [ + - ]: 58 : PG_END_TRY();
5112 peter_e@gmx.net 156 :UBC 0 : }
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 : : */
165 : : static void
3537 tgl@sss.pgh.pa.us 166 :CBC 58 : PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
167 : : char *volatile *xmsg, char *volatile *tbmsg, int *tb_depth)
168 : : {
198 169 : 58 : PyObject *volatile e_type_o = NULL;
170 : 58 : PyObject *volatile e_module_o = NULL;
171 : 58 : PyObject *volatile vob = NULL;
172 : : StringInfoData tbstr;
173 : :
174 : : /*
175 : : * if no exception, return nulls
176 : : */
5112 peter_e@gmx.net 177 [ - + ]: 58 : if (e == NULL)
178 : : {
5112 peter_e@gmx.net 179 :UBC 0 : *xmsg = NULL;
180 : 0 : *tbmsg = NULL;
181 : 0 : *tb_depth = 0;
182 : :
183 : 0 : return;
184 : : }
185 : :
186 : : /*
187 : : * Format the exception and its value and put it in xmsg.
188 : : */
198 tgl@sss.pgh.pa.us 189 [ + - ]:CBC 58 : PG_TRY();
190 : : {
191 : 58 : char *e_type_s = NULL;
192 : 58 : char *e_module_s = NULL;
193 : : const char *vstr;
194 : : StringInfoData xstr;
195 : :
196 : 58 : e_type_o = PyObject_GetAttrString(e, "__name__");
197 : 58 : e_module_o = PyObject_GetAttrString(e, "__module__");
198 [ + - ]: 58 : if (e_type_o)
199 : 58 : e_type_s = PLyUnicode_AsString(e_type_o);
200 [ + - ]: 58 : if (e_module_o)
201 : 58 : e_module_s = PLyUnicode_AsString(e_module_o);
202 : :
203 [ + - + - ]: 58 : if (v && ((vob = PyObject_Str(v)) != NULL))
204 : 58 : vstr = PLyUnicode_AsString(vob);
205 : : else
198 tgl@sss.pgh.pa.us 206 :UBC 0 : vstr = "unknown";
207 : :
198 tgl@sss.pgh.pa.us 208 :CBC 58 : initStringInfo(&xstr);
209 [ + - - + ]: 58 : if (!e_type_s || !e_module_s)
210 : : {
211 : : /* shouldn't happen */
198 tgl@sss.pgh.pa.us 212 :UBC 0 : appendStringInfoString(&xstr, "unrecognized exception");
213 : : }
214 : : /* mimics behavior of traceback.format_exception_only */
198 tgl@sss.pgh.pa.us 215 [ + + ]:CBC 58 : else if (strcmp(e_module_s, "builtins") == 0
216 [ + - ]: 36 : || strcmp(e_module_s, "__main__") == 0
217 [ - + ]: 36 : || strcmp(e_module_s, "exceptions") == 0)
218 : 22 : appendStringInfoString(&xstr, e_type_s);
219 : : else
220 : 36 : appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
221 : 58 : appendStringInfo(&xstr, ": %s", vstr);
222 : :
223 : 58 : *xmsg = xstr.data;
224 : : }
198 tgl@sss.pgh.pa.us 225 :UBC 0 : PG_FINALLY();
226 : : {
198 tgl@sss.pgh.pa.us 227 :CBC 58 : Py_XDECREF(e_type_o);
228 : 58 : Py_XDECREF(e_module_o);
229 : 58 : Py_XDECREF(vob);
230 : : }
231 [ - + ]: 58 : PG_END_TRY();
232 : :
233 : : /*
234 : : * Now format the traceback and put it in tbmsg.
235 : : */
5112 peter_e@gmx.net 236 : 58 : *tb_depth = 0;
237 : 58 : initStringInfo(&tbstr);
238 : : /* Mimic Python traceback reporting as close as possible. */
239 : 58 : appendStringInfoString(&tbstr, "Traceback (most recent call last):");
240 [ + + + + ]: 181 : while (tb != NULL && tb != Py_None)
241 : : {
242 : 123 : PyObject *volatile frame = NULL;
243 : 123 : PyObject *volatile code = NULL;
244 : 123 : PyObject *volatile name = NULL;
245 : 123 : PyObject *volatile lineno = NULL;
246 : 123 : PyObject *volatile filename = NULL;
247 : :
248 [ + - ]: 123 : PG_TRY();
249 : : {
250 : 123 : lineno = PyObject_GetAttrString(tb, "tb_lineno");
251 [ - + ]: 123 : if (lineno == NULL)
5112 peter_e@gmx.net 252 [ # # ]:UBC 0 : elog(ERROR, "could not get line number from Python traceback");
253 : :
5112 peter_e@gmx.net 254 :CBC 123 : frame = PyObject_GetAttrString(tb, "tb_frame");
255 [ - + ]: 123 : if (frame == NULL)
5112 peter_e@gmx.net 256 [ # # ]:UBC 0 : elog(ERROR, "could not get frame from Python traceback");
257 : :
5112 peter_e@gmx.net 258 :CBC 123 : code = PyObject_GetAttrString(frame, "f_code");
259 [ - + ]: 123 : if (code == NULL)
5112 peter_e@gmx.net 260 [ # # ]:UBC 0 : elog(ERROR, "could not get code object from Python frame");
261 : :
5112 peter_e@gmx.net 262 :CBC 123 : name = PyObject_GetAttrString(code, "co_name");
263 [ - + ]: 123 : if (name == NULL)
5112 peter_e@gmx.net 264 [ # # ]:UBC 0 : elog(ERROR, "could not get function name from Python code object");
265 : :
5112 peter_e@gmx.net 266 :CBC 123 : filename = PyObject_GetAttrString(code, "co_filename");
267 [ - + ]: 123 : if (filename == NULL)
5112 peter_e@gmx.net 268 [ # # ]:UBC 0 : elog(ERROR, "could not get file name from Python code object");
269 : :
270 : : /* The first frame always points at <module>, skip it. */
198 tgl@sss.pgh.pa.us 271 [ + + ]:CBC 123 : if (*tb_depth > 0)
272 : : {
273 : 68 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
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 [ + + ]: 68 : if (*tb_depth == 1)
285 : 54 : fname = "<module>";
286 : : else
287 : 14 : fname = PLyUnicode_AsString(name);
288 : :
289 : 68 : proname = PLy_procedure_name(exec_ctx->curr_proc);
290 : 68 : plain_filename = PLyUnicode_AsString(filename);
291 : 68 : plain_lineno = PyLong_AsLong(lineno);
292 : :
293 [ + + ]: 68 : if (proname == NULL)
294 : 13 : appendStringInfo(&tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
295 : : plain_lineno - 1, fname);
296 : : else
297 : 55 : 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 [ + - + - ]: 68 : if (exec_ctx->curr_proc && plain_filename != NULL &&
305 [ + - ]: 68 : 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 : 68 : line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
317 [ + - ]: 68 : if (line)
318 : : {
319 : 68 : appendStringInfo(&tbstr, "\n %s", line);
320 : 68 : pfree(line);
321 : : }
322 : : }
323 : : }
324 : : }
198 tgl@sss.pgh.pa.us 325 :UBC 0 : PG_FINALLY();
326 : : {
198 tgl@sss.pgh.pa.us 327 :CBC 123 : Py_XDECREF(frame);
328 : 123 : Py_XDECREF(code);
329 : 123 : Py_XDECREF(name);
330 : 123 : Py_XDECREF(lineno);
331 : 123 : Py_XDECREF(filename);
332 : : }
333 [ - + ]: 123 : PG_END_TRY();
334 : :
335 : : /* Advance to the next frame. */
5112 peter_e@gmx.net 336 : 123 : tb = PyObject_GetAttrString(tb, "tb_next");
337 [ - + ]: 123 : if (tb == NULL)
5112 peter_e@gmx.net 338 [ # # ]:UBC 0 : 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 : :
5112 peter_e@gmx.net 350 :CBC 123 : (*tb_depth)++;
351 : : }
352 : :
353 : : /* Return the traceback. */
354 : 58 : *tbmsg = tbstr.data;
355 : : }
356 : :
357 : : /*
358 : : * Extract error code from SPIError's sqlstate attribute.
359 : : */
360 : : static void
3539 teodor@sigaev.ru 361 : 17 : PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
362 : : {
363 : : PyObject *sqlstate;
364 : : char *buffer;
365 : :
4705 heikki.linnakangas@i 366 : 17 : sqlstate = PyObject_GetAttrString(exc, "sqlstate");
367 [ + + ]: 17 : if (sqlstate == NULL)
368 : 4 : return;
369 : :
1380 andres@anarazel.de 370 : 13 : buffer = PLyUnicode_AsString(sqlstate);
4705 heikki.linnakangas@i 371 [ + - ]: 13 : if (strlen(buffer) == 5 &&
372 [ + - ]: 13 : strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
373 : : {
374 : 13 : *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 : : */
388 : : static void
3539 teodor@sigaev.ru 389 : 23 : PLy_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 : :
5112 peter_e@gmx.net 397 : 23 : spidata = PyObject_GetAttrString(exc, "spidata");
398 : :
4705 heikki.linnakangas@i 399 [ + + ]: 23 : if (spidata != NULL)
400 : : {
3537 tgl@sss.pgh.pa.us 401 : 19 : 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 : : */
3539 teodor@sigaev.ru 412 : 4 : PLy_get_sqlerrcode(exc, sqlerrcode);
413 : : }
414 : :
5112 peter_e@gmx.net 415 : 23 : Py_XDECREF(spidata);
416 : 23 : }
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 : : */
429 : : static void
3539 teodor@sigaev.ru 430 : 13 : PLy_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 : 13 : PLy_get_sqlerrcode(exc, sqlerrcode);
435 : 13 : get_string_attr(exc, "detail", detail);
436 : 13 : get_string_attr(exc, "hint", hint);
437 : 13 : get_string_attr(exc, "schema_name", schema_name);
438 : 13 : get_string_attr(exc, "table_name", table_name);
439 : 13 : get_string_attr(exc, "column_name", column_name);
440 : 13 : get_string_attr(exc, "datatype_name", datatype_name);
441 : 13 : get_string_attr(exc, "constraint_name", constraint_name);
442 : 13 : }
443 : :
444 : : /*
445 : : * Get the given source line as a palloc'd string
446 : : */
447 : : static char *
5112 peter_e@gmx.net 448 : 68 : get_source_line(const char *src, int lineno)
449 : : {
450 : 68 : const char *s = NULL;
451 : 68 : const char *next = src;
452 : 68 : int current = 0;
453 : :
454 : : /* sanity check */
5026 tgl@sss.pgh.pa.us 455 [ - + ]: 68 : if (lineno <= 0)
5026 tgl@sss.pgh.pa.us 456 :UBC 0 : return NULL;
457 : :
5112 peter_e@gmx.net 458 [ + + ]:CBC 488 : while (current < lineno)
459 : : {
460 : 420 : s = next;
461 : 420 : next = strchr(s + 1, '\n');
462 : 420 : current++;
463 [ - + ]: 420 : if (next == NULL)
5112 peter_e@gmx.net 464 :UBC 0 : break;
465 : : }
466 : :
5112 peter_e@gmx.net 467 [ - + ]:CBC 68 : if (current != lineno)
5112 peter_e@gmx.net 468 :UBC 0 : return NULL;
469 : :
5112 peter_e@gmx.net 470 [ + - + + ]:CBC 318 : while (*s && isspace((unsigned char) *s))
471 : 250 : s++;
472 : :
473 [ - + ]: 68 : if (next == NULL)
5112 peter_e@gmx.net 474 :UBC 0 : 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 : : */
5112 peter_e@gmx.net 481 [ - + ]:CBC 68 : if (next < s)
5112 peter_e@gmx.net 482 :UBC 0 : return NULL;
483 : :
5112 peter_e@gmx.net 484 :CBC 68 : return pnstrdup(s, next - s);
485 : : }
486 : :
487 : :
488 : : /* call PyErr_SetString with a vprint interface and translation support */
489 : : void
490 : 18 : PLy_exception_set(PyObject *exc, const char *fmt,...)
491 : : {
492 : : char buf[1024];
493 : : va_list ap;
494 : :
495 : 18 : va_start(ap, fmt);
496 : 18 : vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
497 : 18 : va_end(ap);
498 : :
499 : 18 : PyErr_SetString(exc, buf);
500 : 18 : }
501 : :
502 : : /* same, with pluralized message */
503 : : void
504 : 1 : PLy_exception_set_plural(PyObject *exc,
505 : : const char *fmt_singular, const char *fmt_plural,
506 : : unsigned long n,...)
507 : : {
508 : : char buf[1024];
509 : : va_list ap;
510 : :
511 : 1 : va_start(ap, n);
512 : 1 : vsnprintf(buf, sizeof(buf),
513 : 1 : dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
514 : : ap);
515 : 1 : va_end(ap);
516 : :
517 : 1 : PyErr_SetString(exc, buf);
518 : 1 : }
519 : :
520 : : /* set attributes of the given exception to details from ErrorData */
521 : : void
3539 teodor@sigaev.ru 522 : 11 : PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
523 : : {
524 : 11 : PyObject *args = NULL;
525 : 11 : PyObject *error = NULL;
526 : :
527 : 11 : args = Py_BuildValue("(s)", edata->message);
528 [ - + ]: 11 : if (!args)
3539 teodor@sigaev.ru 529 :UBC 0 : goto failure;
530 : :
531 : : /* create a new exception with the error message as the parameter */
3539 teodor@sigaev.ru 532 :CBC 11 : error = PyObject_CallObject(excclass, args);
533 [ - + ]: 11 : if (!error)
3539 teodor@sigaev.ru 534 :UBC 0 : goto failure;
535 : :
3539 teodor@sigaev.ru 536 [ - + ]:CBC 11 : if (!set_string_attr(error, "sqlstate",
537 : : unpack_sql_state(edata->sqlerrcode)))
3539 teodor@sigaev.ru 538 :UBC 0 : goto failure;
539 : :
3539 teodor@sigaev.ru 540 [ - + ]:CBC 11 : if (!set_string_attr(error, "detail", edata->detail))
3539 teodor@sigaev.ru 541 :UBC 0 : goto failure;
542 : :
3539 teodor@sigaev.ru 543 [ - + ]:CBC 11 : if (!set_string_attr(error, "hint", edata->hint))
3539 teodor@sigaev.ru 544 :UBC 0 : goto failure;
545 : :
3539 teodor@sigaev.ru 546 [ - + ]:CBC 11 : if (!set_string_attr(error, "query", edata->internalquery))
3539 teodor@sigaev.ru 547 :UBC 0 : goto failure;
548 : :
3539 teodor@sigaev.ru 549 [ - + ]:CBC 11 : if (!set_string_attr(error, "schema_name", edata->schema_name))
3539 teodor@sigaev.ru 550 :UBC 0 : goto failure;
551 : :
3539 teodor@sigaev.ru 552 [ - + ]:CBC 11 : if (!set_string_attr(error, "table_name", edata->table_name))
3539 teodor@sigaev.ru 553 :UBC 0 : goto failure;
554 : :
3539 teodor@sigaev.ru 555 [ - + ]:CBC 11 : if (!set_string_attr(error, "column_name", edata->column_name))
3539 teodor@sigaev.ru 556 :UBC 0 : goto failure;
557 : :
3539 teodor@sigaev.ru 558 [ - + ]:CBC 11 : if (!set_string_attr(error, "datatype_name", edata->datatype_name))
3539 teodor@sigaev.ru 559 :UBC 0 : goto failure;
560 : :
3539 teodor@sigaev.ru 561 [ - + ]:CBC 11 : if (!set_string_attr(error, "constraint_name", edata->constraint_name))
3539 teodor@sigaev.ru 562 :UBC 0 : goto failure;
563 : :
3539 teodor@sigaev.ru 564 :CBC 11 : PyErr_SetObject(excclass, error);
565 : :
566 : : Py_DECREF(args);
567 : : Py_DECREF(error);
568 : :
569 : 11 : return;
570 : :
3539 teodor@sigaev.ru 571 :UBC 0 : failure:
572 : 0 : Py_XDECREF(args);
573 : 0 : Py_XDECREF(error);
574 : :
575 [ # # ]: 0 : elog(ERROR, "could not convert error to Python exception");
576 : : }
577 : :
578 : : /* get string value of an object attribute */
579 : : static void
3539 teodor@sigaev.ru 580 :CBC 91 : get_string_attr(PyObject *obj, char *attrname, char **str)
581 : : {
582 : : PyObject *val;
583 : :
584 : 91 : val = PyObject_GetAttrString(obj, attrname);
585 [ + + + + ]: 91 : if (val != NULL && val != Py_None)
586 : : {
55 tgl@sss.pgh.pa.us 587 :GNC 28 : *str = PLyUnicode_AsString(val);
588 : : }
3539 teodor@sigaev.ru 589 :CBC 91 : Py_XDECREF(val);
590 : 91 : }
591 : :
592 : : /* set an object attribute to a string value, returns true when the set was
593 : : * successful
594 : : */
595 : : static bool
596 : 99 : set_string_attr(PyObject *obj, char *attrname, char *str)
597 : : {
598 : : int result;
599 : : PyObject *val;
600 : :
601 [ + + ]: 99 : if (str != NULL)
602 : : {
1380 andres@anarazel.de 603 : 39 : val = PLyUnicode_FromString(str);
3539 teodor@sigaev.ru 604 [ - + ]: 39 : if (!val)
3539 teodor@sigaev.ru 605 :UBC 0 : return false;
606 : : }
607 : : else
608 : : {
3539 teodor@sigaev.ru 609 [ + - ]:CBC 60 : val = Py_None;
610 : : Py_INCREF(Py_None);
611 : : }
612 : :
613 : 99 : result = PyObject_SetAttrString(obj, attrname, val);
614 : : Py_DECREF(val);
615 : :
616 : 99 : return result != -1;
617 : : }
|