Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 395fcac

Browse files
committed
Fix PL/Python traceback for error in separate file
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
1 parent 0262251 commit 395fcac

File tree

1 file changed

+22
-2
lines changed

1 file changed

+22
-2
lines changed

src/pl/plpython/plpython.c

+22-2
Original file line numberDiff line numberDiff line change
@@ -4510,6 +4510,14 @@ get_source_line(const char *src, int lineno)
45104510
if (next == NULL)
45114511
return pstrdup(s);
45124512

4513+
/*
4514+
* Sanity check, next < s if the line was all-whitespace, which should
4515+
* never happen if Python reported a frame created on that line, but
4516+
* check anyway.
4517+
*/
4518+
if (next < s)
4519+
return NULL;
4520+
45134521
return pnstrdup(s, next - s);
45144522
}
45154523

@@ -4606,6 +4614,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46064614
PyObject *volatile code = NULL;
46074615
PyObject *volatile name = NULL;
46084616
PyObject *volatile lineno = NULL;
4617+
PyObject *volatile filename = NULL;
46094618

46104619
PG_TRY();
46114620
{
@@ -4624,13 +4633,18 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46244633
name = PyObject_GetAttrString(code, "co_name");
46254634
if (name == NULL)
46264635
elog(ERROR, "could not get function name from Python code object");
4636+
4637+
filename = PyObject_GetAttrString(code, "co_filename");
4638+
if (filename == NULL)
4639+
elog(ERROR, "could not get file name from Python code object");
46274640
}
46284641
PG_CATCH();
46294642
{
46304643
Py_XDECREF(frame);
46314644
Py_XDECREF(code);
46324645
Py_XDECREF(name);
46334646
Py_XDECREF(lineno);
4647+
Py_XDECREF(filename);
46344648
PG_RE_THROW();
46354649
}
46364650
PG_END_TRY();
@@ -4641,6 +4655,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46414655
char *proname;
46424656
char *fname;
46434657
char *line;
4658+
char *plain_filename;
46444659
long plain_lineno;
46454660

46464661
/*
@@ -4653,6 +4668,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46534668
fname = PyString_AsString(name);
46544669

46554670
proname = PLy_procedure_name(PLy_curr_procedure);
4671+
plain_filename = PyString_AsString(filename);
46564672
plain_lineno = PyInt_AsLong(lineno);
46574673

46584674
if (proname == NULL)
@@ -4664,15 +4680,18 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46644680
&tbstr, "\n PL/Python function \"%s\", line %ld, in %s",
46654681
proname, plain_lineno - 1, fname);
46664682

4667-
if (PLy_curr_procedure)
4683+
/* function code object was compiled with "<string>" as the filename */
4684+
if (PLy_curr_procedure && plain_filename != NULL &&
4685+
strcmp(plain_filename, "<string>") == 0)
46684686
{
46694687
/*
46704688
* If we know the current procedure, append the exact line
46714689
* from the source, again mimicking Python's traceback.py
46724690
* module behavior. We could store the already line-split
46734691
* source to avoid splitting it every time, but producing a
46744692
* traceback is not the most important scenario to optimize
4675-
* for.
4693+
* for. But we do not go as far as traceback.py in reading
4694+
* the source of imported modules.
46764695
*/
46774696
line = get_source_line(PLy_curr_procedure->src, plain_lineno);
46784697
if (line)
@@ -4687,6 +4706,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
46874706
Py_DECREF(code);
46884707
Py_DECREF(name);
46894708
Py_DECREF(lineno);
4709+
Py_DECREF(filename);
46904710

46914711
/* Release the current frame and go to the next one. */
46924712
tb_prev = tb;

0 commit comments

Comments
 (0)