From 395fcac29906d22615ba68bd1dfa31daf691979e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 20 Apr 2011 23:19:04 +0300 Subject: [PATCH] Fix PL/Python traceback for error in separate file MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It assumed that the lineno from the traceback always refers to the PL/Python function. If you created a PL/Python function that imports some code, runs it, and that code raises an exception, PLy_traceback would get utterly confused. Now we look at the file name reported with the traceback and only print the source line if it came from the PL/Python function. Jan Urbański --- src/pl/plpython/plpython.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 5e600daa96..8108cfce2c 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -4510,6 +4510,14 @@ get_source_line(const char *src, int lineno) if (next == NULL) return pstrdup(s); + /* + * Sanity check, next < s if the line was all-whitespace, which should + * never happen if Python reported a frame created on that line, but + * check anyway. + */ + if (next < s) + return NULL; + return pnstrdup(s, next - s); } @@ -4606,6 +4614,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) PyObject *volatile code = NULL; PyObject *volatile name = NULL; PyObject *volatile lineno = NULL; + PyObject *volatile filename = NULL; PG_TRY(); { @@ -4624,6 +4633,10 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) name = PyObject_GetAttrString(code, "co_name"); if (name == NULL) elog(ERROR, "could not get function name from Python code object"); + + filename = PyObject_GetAttrString(code, "co_filename"); + if (filename == NULL) + elog(ERROR, "could not get file name from Python code object"); } PG_CATCH(); { @@ -4631,6 +4644,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) Py_XDECREF(code); Py_XDECREF(name); Py_XDECREF(lineno); + Py_XDECREF(filename); PG_RE_THROW(); } PG_END_TRY(); @@ -4641,6 +4655,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) char *proname; char *fname; char *line; + char *plain_filename; long plain_lineno; /* @@ -4653,6 +4668,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) fname = PyString_AsString(name); proname = PLy_procedure_name(PLy_curr_procedure); + plain_filename = PyString_AsString(filename); plain_lineno = PyInt_AsLong(lineno); if (proname == NULL) @@ -4664,7 +4680,9 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) &tbstr, "\n PL/Python function \"%s\", line %ld, in %s", proname, plain_lineno - 1, fname); - if (PLy_curr_procedure) + /* function code object was compiled with "" as the filename */ + if (PLy_curr_procedure && plain_filename != NULL && + strcmp(plain_filename, "") == 0) { /* * If we know the current procedure, append the exact line @@ -4672,7 +4690,8 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) * module behavior. We could store the already line-split * source to avoid splitting it every time, but producing a * traceback is not the most important scenario to optimize - * for. + * for. But we do not go as far as traceback.py in reading + * the source of imported modules. */ line = get_source_line(PLy_curr_procedure->src, plain_lineno); if (line) @@ -4687,6 +4706,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) Py_DECREF(code); Py_DECREF(name); Py_DECREF(lineno); + Py_DECREF(filename); /* Release the current frame and go to the next one. */ tb_prev = tb; -- 2.40.0