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 "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
2427 peter_e@gmx.net 43 :CBC 58 : PLy_elog_impl(int elevel, const char *fmt,...)
44 : : {
2027 tgl@sss.pgh.pa.us 45 : 58 : int save_errno = errno;
46 : : char *xmsg;
47 : : char *tbmsg;
48 : : int tb_depth;
49 : : StringInfoData emsg;
50 : : PyObject *exc,
51 : : *val,
52 : : *tb;
4501 peter_e@gmx.net 53 : 58 : const char *primary = NULL;
4326 bruce@momjian.us 54 : 58 : int sqlerrcode = 0;
4501 peter_e@gmx.net 55 : 58 : char *detail = NULL;
56 : 58 : char *hint = NULL;
57 : 58 : char *query = NULL;
58 : 58 : int position = 0;
2928 teodor@sigaev.ru 59 : 58 : char *schema_name = NULL;
60 : 58 : char *table_name = NULL;
61 : 58 : char *column_name = NULL;
62 : 58 : char *datatype_name = NULL;
63 : 58 : char *constraint_name = NULL;
64 : :
4501 peter_e@gmx.net 65 : 58 : PyErr_Fetch(&exc, &val, &tb);
66 : :
67 [ + - ]: 58 : if (exc != NULL)
68 : : {
2926 tgl@sss.pgh.pa.us 69 : 58 : PyErr_NormalizeException(&exc, &val, &tb);
70 : :
4501 peter_e@gmx.net 71 [ + + ]: 58 : if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
2926 tgl@sss.pgh.pa.us 72 : 23 : PLy_get_spi_error_data(val, &sqlerrcode,
73 : : &detail, &hint, &query, &position,
74 : : &schema_name, &table_name, &column_name,
75 : : &datatype_name, &constraint_name);
2928 teodor@sigaev.ru 76 [ + + ]: 35 : else if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
77 : 13 : PLy_get_error_data(val, &sqlerrcode, &detail, &hint,
78 : : &schema_name, &table_name, &column_name,
79 : : &datatype_name, &constraint_name);
4501 peter_e@gmx.net 80 [ - + ]: 22 : else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
4501 peter_e@gmx.net 81 :UBC 0 : elevel = FATAL;
82 : : }
83 : :
84 : : /* this releases our refcount on tb! */
2926 tgl@sss.pgh.pa.us 85 :CBC 58 : PLy_traceback(exc, val, tb,
86 : : &xmsg, &tbmsg, &tb_depth);
87 : :
4501 peter_e@gmx.net 88 [ + + ]: 58 : if (fmt)
89 : : {
90 : 4 : initStringInfo(&emsg);
91 : : for (;;)
4501 peter_e@gmx.net 92 :UBC 0 : {
93 : : va_list ap;
94 : : int needed;
95 : :
2027 tgl@sss.pgh.pa.us 96 :CBC 4 : errno = save_errno;
4501 peter_e@gmx.net 97 : 4 : va_start(ap, fmt);
3825 tgl@sss.pgh.pa.us 98 : 4 : needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
4501 peter_e@gmx.net 99 : 4 : va_end(ap);
3825 tgl@sss.pgh.pa.us 100 [ + - ]: 4 : if (needed == 0)
4501 peter_e@gmx.net 101 : 4 : break;
3825 tgl@sss.pgh.pa.us 102 :UBC 0 : enlargeStringInfo(&emsg, needed);
103 : : }
4501 peter_e@gmx.net 104 :CBC 4 : primary = emsg.data;
105 : :
106 : : /* If there's an exception message, it goes in the detail. */
107 [ + - ]: 4 : if (xmsg)
108 : 4 : detail = xmsg;
109 : : }
110 : : else
111 : : {
112 [ + - ]: 54 : if (xmsg)
113 : 54 : primary = xmsg;
114 : : }
115 : :
116 [ + + ]: 58 : PG_TRY();
117 : : {
118 [ + - + + : 58 : 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 : : }
1626 peter@eisentraut.org 137 : 58 : PG_FINALLY();
138 : : {
4501 peter_e@gmx.net 139 [ + + ]: 58 : if (fmt)
140 : 4 : pfree(emsg.data);
141 [ + - ]: 58 : if (xmsg)
142 : 58 : pfree(xmsg);
143 [ + - ]: 58 : if (tbmsg)
144 : 58 : pfree(tbmsg);
2926 tgl@sss.pgh.pa.us 145 : 58 : Py_XDECREF(exc);
146 : 58 : Py_XDECREF(val);
147 : : }
4501 peter_e@gmx.net 148 [ + - ]: 58 : PG_END_TRY();
4501 peter_e@gmx.net 149 :UBC 0 : }
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
2926 tgl@sss.pgh.pa.us 162 :CBC 58 : 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;
4501 peter_e@gmx.net 167 : 58 : char *e_type_s = NULL;
168 : 58 : char *e_module_s = NULL;
169 : 58 : PyObject *vob = NULL;
170 : : char *vstr;
171 : : StringInfoData xstr;
172 : : StringInfoData tbstr;
173 : :
174 : : /*
175 : : * if no exception, return nulls
176 : : */
177 [ - + ]: 58 : if (e == NULL)
178 : : {
4501 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 : : */
189 : :
4501 peter_e@gmx.net 190 :CBC 58 : e_type_o = PyObject_GetAttrString(e, "__name__");
191 : 58 : e_module_o = PyObject_GetAttrString(e, "__module__");
192 [ + - ]: 58 : if (e_type_o)
769 andres@anarazel.de 193 : 58 : e_type_s = PLyUnicode_AsString(e_type_o);
4501 peter_e@gmx.net 194 [ + - ]: 58 : if (e_type_s)
769 andres@anarazel.de 195 : 58 : e_module_s = PLyUnicode_AsString(e_module_o);
196 : :
4501 peter_e@gmx.net 197 [ + - + - ]: 58 : if (v && ((vob = PyObject_Str(v)) != NULL))
769 andres@anarazel.de 198 : 58 : vstr = PLyUnicode_AsString(vob);
199 : : else
4501 peter_e@gmx.net 200 :UBC 0 : vstr = "unknown";
201 : :
4501 peter_e@gmx.net 202 :CBC 58 : initStringInfo(&xstr);
203 [ + - - + ]: 58 : if (!e_type_s || !e_module_s)
204 : : {
205 : : /* shouldn't happen */
769 andres@anarazel.de 206 :UBC 0 : appendStringInfoString(&xstr, "unrecognized exception");
207 : : }
208 : : /* mimics behavior of traceback.format_exception_only */
4501 peter_e@gmx.net 209 [ + + ]:CBC 58 : else if (strcmp(e_module_s, "builtins") == 0
210 [ + - ]: 36 : || strcmp(e_module_s, "__main__") == 0
211 [ - + ]: 36 : || strcmp(e_module_s, "exceptions") == 0)
1277 drowley@postgresql.o 212 : 22 : appendStringInfoString(&xstr, e_type_s);
213 : : else
4501 peter_e@gmx.net 214 : 36 : appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
215 : 58 : appendStringInfo(&xstr, ": %s", vstr);
216 : :
217 : 58 : *xmsg = xstr.data;
218 : :
219 : : /*
220 : : * Now format the traceback and put it in tbmsg.
221 : : */
222 : :
223 : 58 : *tb_depth = 0;
224 : 58 : initStringInfo(&tbstr);
225 : : /* Mimic Python traceback reporting as close as possible. */
226 : 58 : appendStringInfoString(&tbstr, "Traceback (most recent call last):");
227 [ + + + + ]: 181 : while (tb != NULL && tb != Py_None)
228 : : {
229 : 123 : PyObject *volatile tb_prev = NULL;
230 : 123 : PyObject *volatile frame = NULL;
231 : 123 : PyObject *volatile code = NULL;
232 : 123 : PyObject *volatile name = NULL;
233 : 123 : PyObject *volatile lineno = NULL;
234 : 123 : PyObject *volatile filename = NULL;
235 : :
236 [ + - ]: 123 : PG_TRY();
237 : : {
238 : 123 : lineno = PyObject_GetAttrString(tb, "tb_lineno");
239 [ - + ]: 123 : if (lineno == NULL)
4501 peter_e@gmx.net 240 [ # # ]:UBC 0 : elog(ERROR, "could not get line number from Python traceback");
241 : :
4501 peter_e@gmx.net 242 :CBC 123 : frame = PyObject_GetAttrString(tb, "tb_frame");
243 [ - + ]: 123 : if (frame == NULL)
4501 peter_e@gmx.net 244 [ # # ]:UBC 0 : elog(ERROR, "could not get frame from Python traceback");
245 : :
4501 peter_e@gmx.net 246 :CBC 123 : code = PyObject_GetAttrString(frame, "f_code");
247 [ - + ]: 123 : if (code == NULL)
4501 peter_e@gmx.net 248 [ # # ]:UBC 0 : elog(ERROR, "could not get code object from Python frame");
249 : :
4501 peter_e@gmx.net 250 :CBC 123 : name = PyObject_GetAttrString(code, "co_name");
251 [ - + ]: 123 : if (name == NULL)
4501 peter_e@gmx.net 252 [ # # ]:UBC 0 : elog(ERROR, "could not get function name from Python code object");
253 : :
4501 peter_e@gmx.net 254 :CBC 123 : filename = PyObject_GetAttrString(code, "co_filename");
255 [ - + ]: 123 : if (filename == NULL)
4501 peter_e@gmx.net 256 [ # # ]:UBC 0 : elog(ERROR, "could not get file name from Python code object");
257 : : }
258 : 0 : PG_CATCH();
259 : : {
260 : 0 : Py_XDECREF(frame);
261 : 0 : Py_XDECREF(code);
262 : 0 : Py_XDECREF(name);
263 : 0 : Py_XDECREF(lineno);
264 : 0 : Py_XDECREF(filename);
265 : 0 : PG_RE_THROW();
266 : : }
4501 peter_e@gmx.net 267 [ - + ]:CBC 123 : PG_END_TRY();
268 : :
269 : : /* The first frame always points at <module>, skip it. */
270 [ + + ]: 123 : if (*tb_depth > 0)
271 : : {
4326 bruce@momjian.us 272 : 68 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
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 : : */
4501 peter_e@gmx.net 283 [ + + ]: 68 : if (*tb_depth == 1)
284 : 54 : fname = "<module>";
285 : : else
769 andres@anarazel.de 286 : 14 : fname = PLyUnicode_AsString(name);
287 : :
4415 tgl@sss.pgh.pa.us 288 : 68 : proname = PLy_procedure_name(exec_ctx->curr_proc);
769 andres@anarazel.de 289 : 68 : plain_filename = PLyUnicode_AsString(filename);
290 : 68 : plain_lineno = PyLong_AsLong(lineno);
291 : :
4501 peter_e@gmx.net 292 [ + + ]: 68 : if (proname == NULL)
1536 alvherre@alvh.no-ip. 293 : 13 : appendStringInfo(&tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
294 : : plain_lineno - 1, fname);
295 : : else
296 : 55 : 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 : : */
4415 tgl@sss.pgh.pa.us 303 [ + - + - ]: 68 : if (exec_ctx->curr_proc && plain_filename != NULL &&
4501 peter_e@gmx.net 304 [ + - ]: 68 : 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 : : */
4415 tgl@sss.pgh.pa.us 315 : 68 : line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
4501 peter_e@gmx.net 316 [ + - ]: 68 : if (line)
317 : : {
318 : 68 : appendStringInfo(&tbstr, "\n %s", line);
319 : 68 : pfree(line);
320 : : }
321 : : }
322 : : }
323 : :
324 : 123 : Py_DECREF(frame);
325 : 123 : Py_DECREF(code);
326 : 123 : Py_DECREF(name);
327 : 123 : Py_DECREF(lineno);
328 : 123 : Py_DECREF(filename);
329 : :
330 : : /* Release the current frame and go to the next one. */
331 : 123 : tb_prev = tb;
332 : 123 : tb = PyObject_GetAttrString(tb, "tb_next");
333 [ - + ]: 123 : Assert(tb_prev != Py_None);
334 : 123 : Py_DECREF(tb_prev);
335 [ - + ]: 123 : if (tb == NULL)
4501 peter_e@gmx.net 336 [ # # ]:UBC 0 : elog(ERROR, "could not traverse Python traceback");
4501 peter_e@gmx.net 337 :CBC 123 : (*tb_depth)++;
338 : : }
339 : :
340 : : /* Return the traceback. */
341 : 58 : *tbmsg = tbstr.data;
342 : :
343 : 58 : Py_XDECREF(e_type_o);
344 : 58 : Py_XDECREF(e_module_o);
345 : 58 : Py_XDECREF(vob);
346 : : }
347 : :
348 : : /*
349 : : * Extract error code from SPIError's sqlstate attribute.
350 : : */
351 : : static void
2928 teodor@sigaev.ru 352 : 17 : PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
353 : : {
354 : : PyObject *sqlstate;
355 : : char *buffer;
356 : :
4094 heikki.linnakangas@i 357 : 17 : sqlstate = PyObject_GetAttrString(exc, "sqlstate");
358 [ + + ]: 17 : if (sqlstate == NULL)
359 : 4 : return;
360 : :
769 andres@anarazel.de 361 : 13 : buffer = PLyUnicode_AsString(sqlstate);
4094 heikki.linnakangas@i 362 [ + - ]: 13 : if (strlen(buffer) == 5 &&
363 [ + - ]: 13 : strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
364 : : {
365 : 13 : *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
366 : : buffer[3], buffer[4]);
367 : : }
368 : :
369 : 13 : Py_DECREF(sqlstate);
370 : : }
371 : :
372 : : /*
373 : : * Extract the error data from a SPIError
374 : : */
375 : : static void
2928 teodor@sigaev.ru 376 : 23 : 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 : :
4501 peter_e@gmx.net 384 : 23 : spidata = PyObject_GetAttrString(exc, "spidata");
385 : :
4094 heikki.linnakangas@i 386 [ + + ]: 23 : if (spidata != NULL)
387 : : {
2926 tgl@sss.pgh.pa.us 388 : 19 : 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 : : */
2928 teodor@sigaev.ru 399 : 4 : PLy_get_sqlerrcode(exc, sqlerrcode);
400 : : }
401 : :
4501 peter_e@gmx.net 402 : 23 : Py_XDECREF(spidata);
403 : 23 : }
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
2928 teodor@sigaev.ru 412 : 13 : 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 : 13 : PLy_get_sqlerrcode(exc, sqlerrcode);
417 : 13 : get_string_attr(exc, "detail", detail);
418 : 13 : get_string_attr(exc, "hint", hint);
419 : 13 : get_string_attr(exc, "schema_name", schema_name);
420 : 13 : get_string_attr(exc, "table_name", table_name);
421 : 13 : get_string_attr(exc, "column_name", column_name);
422 : 13 : get_string_attr(exc, "datatype_name", datatype_name);
423 : 13 : get_string_attr(exc, "constraint_name", constraint_name);
424 : 13 : }
425 : :
426 : : /*
427 : : * Get the given source line as a palloc'd string
428 : : */
429 : : static char *
4501 peter_e@gmx.net 430 : 68 : get_source_line(const char *src, int lineno)
431 : : {
432 : 68 : const char *s = NULL;
433 : 68 : const char *next = src;
434 : 68 : int current = 0;
435 : :
436 : : /* sanity check */
4415 tgl@sss.pgh.pa.us 437 [ - + ]: 68 : if (lineno <= 0)
4415 tgl@sss.pgh.pa.us 438 :UBC 0 : return NULL;
439 : :
4501 peter_e@gmx.net 440 [ + + ]:CBC 488 : while (current < lineno)
441 : : {
442 : 420 : s = next;
443 : 420 : next = strchr(s + 1, '\n');
444 : 420 : current++;
445 [ - + ]: 420 : if (next == NULL)
4501 peter_e@gmx.net 446 :UBC 0 : break;
447 : : }
448 : :
4501 peter_e@gmx.net 449 [ - + ]:CBC 68 : if (current != lineno)
4501 peter_e@gmx.net 450 :UBC 0 : return NULL;
451 : :
4501 peter_e@gmx.net 452 [ + - + + ]:CBC 318 : while (*s && isspace((unsigned char) *s))
453 : 250 : s++;
454 : :
455 [ - + ]: 68 : if (next == NULL)
4501 peter_e@gmx.net 456 :UBC 0 : 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 : : */
4501 peter_e@gmx.net 463 [ - + ]:CBC 68 : if (next < s)
4501 peter_e@gmx.net 464 :UBC 0 : return NULL;
465 : :
4501 peter_e@gmx.net 466 :CBC 68 : return pnstrdup(s, next - s);
467 : : }
468 : :
469 : :
470 : : /* call PyErr_SetString with a vprint interface and translation support */
471 : : void
472 : 18 : PLy_exception_set(PyObject *exc, const char *fmt,...)
473 : : {
474 : : char buf[1024];
475 : : va_list ap;
476 : :
477 : 18 : va_start(ap, fmt);
478 : 18 : vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
479 : 18 : va_end(ap);
480 : :
481 : 18 : PyErr_SetString(exc, buf);
482 : 18 : }
483 : :
484 : : /* same, with pluralized message */
485 : : void
486 : 1 : PLy_exception_set_plural(PyObject *exc,
487 : : const char *fmt_singular, const char *fmt_plural,
488 : : unsigned long n,...)
489 : : {
490 : : char buf[1024];
491 : : va_list ap;
492 : :
493 : 1 : va_start(ap, n);
494 : 1 : vsnprintf(buf, sizeof(buf),
495 : 1 : dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
496 : : ap);
497 : 1 : va_end(ap);
498 : :
499 : 1 : PyErr_SetString(exc, buf);
500 : 1 : }
501 : :
502 : : /* set attributes of the given exception to details from ErrorData */
503 : : void
2928 teodor@sigaev.ru 504 : 11 : PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
505 : : {
506 : 11 : PyObject *args = NULL;
507 : 11 : PyObject *error = NULL;
508 : :
509 : 11 : args = Py_BuildValue("(s)", edata->message);
510 [ - + ]: 11 : if (!args)
2928 teodor@sigaev.ru 511 :UBC 0 : goto failure;
512 : :
513 : : /* create a new exception with the error message as the parameter */
2928 teodor@sigaev.ru 514 :CBC 11 : error = PyObject_CallObject(excclass, args);
515 [ - + ]: 11 : if (!error)
2928 teodor@sigaev.ru 516 :UBC 0 : goto failure;
517 : :
2928 teodor@sigaev.ru 518 [ - + ]:CBC 11 : if (!set_string_attr(error, "sqlstate",
519 : : unpack_sql_state(edata->sqlerrcode)))
2928 teodor@sigaev.ru 520 :UBC 0 : goto failure;
521 : :
2928 teodor@sigaev.ru 522 [ - + ]:CBC 11 : if (!set_string_attr(error, "detail", edata->detail))
2928 teodor@sigaev.ru 523 :UBC 0 : goto failure;
524 : :
2928 teodor@sigaev.ru 525 [ - + ]:CBC 11 : if (!set_string_attr(error, "hint", edata->hint))
2928 teodor@sigaev.ru 526 :UBC 0 : goto failure;
527 : :
2928 teodor@sigaev.ru 528 [ - + ]:CBC 11 : if (!set_string_attr(error, "query", edata->internalquery))
2928 teodor@sigaev.ru 529 :UBC 0 : goto failure;
530 : :
2928 teodor@sigaev.ru 531 [ - + ]:CBC 11 : if (!set_string_attr(error, "schema_name", edata->schema_name))
2928 teodor@sigaev.ru 532 :UBC 0 : goto failure;
533 : :
2928 teodor@sigaev.ru 534 [ - + ]:CBC 11 : if (!set_string_attr(error, "table_name", edata->table_name))
2928 teodor@sigaev.ru 535 :UBC 0 : goto failure;
536 : :
2928 teodor@sigaev.ru 537 [ - + ]:CBC 11 : if (!set_string_attr(error, "column_name", edata->column_name))
2928 teodor@sigaev.ru 538 :UBC 0 : goto failure;
539 : :
2928 teodor@sigaev.ru 540 [ - + ]:CBC 11 : if (!set_string_attr(error, "datatype_name", edata->datatype_name))
2928 teodor@sigaev.ru 541 :UBC 0 : goto failure;
542 : :
2928 teodor@sigaev.ru 543 [ - + ]:CBC 11 : if (!set_string_attr(error, "constraint_name", edata->constraint_name))
2928 teodor@sigaev.ru 544 :UBC 0 : goto failure;
545 : :
2928 teodor@sigaev.ru 546 :CBC 11 : PyErr_SetObject(excclass, error);
547 : :
548 : 11 : Py_DECREF(args);
549 : 11 : Py_DECREF(error);
550 : :
551 : 11 : return;
552 : :
2928 teodor@sigaev.ru 553 :UBC 0 : failure:
554 : 0 : Py_XDECREF(args);
555 : 0 : Py_XDECREF(error);
556 : :
557 [ # # ]: 0 : elog(ERROR, "could not convert error to Python exception");
558 : : }
559 : :
560 : : /* get string value of an object attribute */
561 : : static void
2928 teodor@sigaev.ru 562 :CBC 91 : get_string_attr(PyObject *obj, char *attrname, char **str)
563 : : {
564 : : PyObject *val;
565 : :
566 : 91 : val = PyObject_GetAttrString(obj, attrname);
567 [ + + + + ]: 91 : if (val != NULL && val != Py_None)
568 : : {
769 andres@anarazel.de 569 : 28 : *str = pstrdup(PLyUnicode_AsString(val));
570 : : }
2928 teodor@sigaev.ru 571 : 91 : Py_XDECREF(val);
572 : 91 : }
573 : :
574 : : /* set an object attribute to a string value, returns true when the set was
575 : : * successful
576 : : */
577 : : static bool
578 : 99 : set_string_attr(PyObject *obj, char *attrname, char *str)
579 : : {
580 : : int result;
581 : : PyObject *val;
582 : :
583 [ + + ]: 99 : if (str != NULL)
584 : : {
769 andres@anarazel.de 585 : 39 : val = PLyUnicode_FromString(str);
2928 teodor@sigaev.ru 586 [ - + ]: 39 : if (!val)
2928 teodor@sigaev.ru 587 :UBC 0 : return false;
588 : : }
589 : : else
590 : : {
2928 teodor@sigaev.ru 591 :CBC 60 : val = Py_None;
592 : 60 : Py_INCREF(Py_None);
593 : : }
594 : :
595 : 99 : result = PyObject_SetAttrString(obj, attrname, val);
596 : 99 : Py_DECREF(val);
597 : :
598 : 99 : return result != -1;
599 : : }
|