Fix PL/Python traceback for error in separate file
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 20 Apr 2011 20:19:04 +0000 (23:19 +0300)
committerPeter Eisentraut <peter_e@gmx.net>
Wed, 20 Apr 2011 20:19:04 +0000 (23:19 +0300)
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

index 5e600daa96ae2159512b366b41197c31383ee7f8..8108cfce2c045d49b8a9cd5e716a7831cad9a95a 100644 (file)
@@ -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 "<string>" as the filename */
+                       if (PLy_curr_procedure && plain_filename != NULL &&
+                               strcmp(plain_filename, "<string>") == 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;