Use errcontext mechanism in PL/Python

Error messages from PL/Python now always mention the function name in the
CONTEXT: field.  This also obsoletes the few places that tried to do the
same manually.

Regression test files are updated to work with Python 2.4-2.6.  I don't have
access to older versions right now.
This commit is contained in:
Peter Eisentraut 2009-07-20 08:01:07 +00:00
parent 888d3335b1
commit 5106bdc450
5 changed files with 127 additions and 61 deletions

View File

@ -2,17 +2,20 @@
-- the trigger handler once. the errors and subsequent core dump were -- the trigger handler once. the errors and subsequent core dump were
-- interesting. -- interesting.
SELECT invalid_type_uncaught('rick'); SELECT invalid_type_uncaught('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_uncaught" WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_uncaught"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_uncaught"
SELECT invalid_type_caught('rick'); SELECT invalid_type_caught('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_caught" WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_caught"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_caught"
SELECT invalid_type_reraised('rick'); SELECT invalid_type_reraised('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_reraised" WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_reraised"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_reraised"
SELECT valid_type('rick'); SELECT valid_type('rick');
valid_type valid_type
------------ ------------
@ -23,16 +26,20 @@ SELECT valid_type('rick');
-- Test Unicode error handling. -- Test Unicode error handling.
-- --
SELECT unicode_return_error(); SELECT unicode_return_error();
ERROR: PL/Python: could not create string representation of Python object in PL/Python function "unicode_return_error" while creating return value ERROR: PL/Python: could not create string representation of Python object, while creating return value
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_return_error"
INSERT INTO unicode_test (testvalue) VALUES ('test'); INSERT INTO unicode_test (testvalue) VALUES ('test');
ERROR: PL/Python: could not compute string representation of Python object in PL/Python function "unicode_trigger_error" while modifying trigger row ERROR: PL/Python: could not compute string representation of Python object, while modifying trigger row
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_trigger_error"
SELECT unicode_plan_error1(); SELECT unicode_plan_error1();
WARNING: PL/Python: in PL/Python function "unicode_plan_error1" WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan
DETAIL: plpy.Error: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan_error1"
ERROR: PL/Python: PL/Python function "unicode_plan_error1" could not execute plan ERROR: PL/Python: could not execute plan
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_plan_error1"
SELECT unicode_plan_error2(); SELECT unicode_plan_error2();
ERROR: PL/Python: PL/Python function "unicode_plan_error2" could not execute plan ERROR: PL/Python: could not execute plan
DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_plan_error2"

View File

@ -2,17 +2,20 @@
-- the trigger handler once. the errors and subsequent core dump were -- the trigger handler once. the errors and subsequent core dump were
-- interesting. -- interesting.
SELECT invalid_type_uncaught('rick'); SELECT invalid_type_uncaught('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_uncaught" WARNING: PL/Python: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_uncaught"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_uncaught"
SELECT invalid_type_caught('rick'); SELECT invalid_type_caught('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_caught" WARNING: PL/Python: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_caught"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_caught"
SELECT invalid_type_reraised('rick'); SELECT invalid_type_reraised('rick');
WARNING: PL/Python: in PL/Python function "invalid_type_reraised" WARNING: PL/Python: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
DETAIL: <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_reraised"
ERROR: type "test" does not exist ERROR: type "test" does not exist
CONTEXT: PL/Python function "invalid_type_reraised"
SELECT valid_type('rick'); SELECT valid_type('rick');
valid_type valid_type
------------ ------------
@ -23,16 +26,20 @@ SELECT valid_type('rick');
-- Test Unicode error handling. -- Test Unicode error handling.
-- --
SELECT unicode_return_error(); SELECT unicode_return_error();
ERROR: PL/Python: could not create string representation of Python object in PL/Python function "unicode_return_error" while creating return value ERROR: PL/Python: could not create string representation of Python object, while creating return value
DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_return_error"
INSERT INTO unicode_test (testvalue) VALUES ('test'); INSERT INTO unicode_test (testvalue) VALUES ('test');
ERROR: PL/Python: could not compute string representation of Python object in PL/Python function "unicode_trigger_error" while modifying trigger row ERROR: PL/Python: could not compute string representation of Python object, while modifying trigger row
DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_trigger_error"
SELECT unicode_plan_error1(); SELECT unicode_plan_error1();
WARNING: PL/Python: in PL/Python function "unicode_plan_error1" WARNING: PL/Python: <class 'plpy.Error'>: unrecognized error in PLy_spi_execute_plan
DETAIL: <class 'plpy.Error'>: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan_error1"
ERROR: PL/Python: PL/Python function "unicode_plan_error1" could not execute plan ERROR: PL/Python: could not execute plan
DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_plan_error1"
SELECT unicode_plan_error2(); SELECT unicode_plan_error2();
ERROR: PL/Python: PL/Python function "unicode_plan_error2" could not execute plan ERROR: PL/Python: could not execute plan
DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) DETAIL: <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128)
CONTEXT: PL/Python function "unicode_plan_error2"

View File

@ -136,37 +136,67 @@ BEFORE INSERT OR UPDATE OR DELETE ON trigger_test
FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
insert into trigger_test values(1,'insert'); insert into trigger_test values(1,'insert');
NOTICE: ("TD[args] => ['23', 'skidoo']",) NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => INSERT',) NOTICE: ('TD[event] => INSERT',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',) NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig',) NOTICE: ('TD[name] => show_trigger_data_trig',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'insert'}",) NOTICE: ("TD[new] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[old] => None',) NOTICE: ('TD[old] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',) NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',) NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',) NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',) NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
update trigger_test set v = 'update' where i = 1; update trigger_test set v = 'update' where i = 1;
NOTICE: ("TD[args] => ['23', 'skidoo']",) NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => UPDATE',) NOTICE: ('TD[event] => UPDATE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',) NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig',) NOTICE: ('TD[name] => show_trigger_data_trig',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[new] => {'i': 1, 'v': 'update'}",) NOTICE: ("TD[new] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'insert'}",) NOTICE: ("TD[old] => {'i': 1, 'v': 'insert'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',) NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',) NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',) NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',) NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
delete from trigger_test; delete from trigger_test;
NOTICE: ("TD[args] => ['23', 'skidoo']",) NOTICE: ("TD[args] => ['23', 'skidoo']",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[event] => DELETE',) NOTICE: ('TD[event] => DELETE',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[level] => ROW',) NOTICE: ('TD[level] => ROW',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[name] => show_trigger_data_trig',) NOTICE: ('TD[name] => show_trigger_data_trig',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[new] => None',) NOTICE: ('TD[new] => None',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ("TD[old] => {'i': 1, 'v': 'update'}",) NOTICE: ("TD[old] => {'i': 1, 'v': 'update'}",)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[relid] => bogus:12345',) NOTICE: ('TD[relid] => bogus:12345',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_name] => trigger_test',) NOTICE: ('TD[table_name] => trigger_test',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[table_schema] => public',) NOTICE: ('TD[table_schema] => public',)
CONTEXT: PL/Python function "trigger_data"
NOTICE: ('TD[when] => BEFORE',) NOTICE: ('TD[when] => BEFORE',)
CONTEXT: PL/Python function "trigger_data"
DROP TRIGGER show_trigger_data_trig on trigger_test; DROP TRIGGER show_trigger_data_trig on trigger_test;

View File

@ -38,6 +38,7 @@ SELECT global_test_two();
-- --
SELECT import_fail(); SELECT import_fail();
NOTICE: ('import socket failed -- No module named foosocket',) NOTICE: ('import socket failed -- No module named foosocket',)
CONTEXT: PL/Python function "import_fail"
import_fail import_fail
-------------------- --------------------
failed as expected failed as expected
@ -191,6 +192,7 @@ SELECT test_void_func1(), test_void_func1() IS NULL AS "is null";
SELECT test_void_func2(); -- should fail SELECT test_void_func2(); -- should fail
ERROR: PL/Python function with return type "void" did not return None ERROR: PL/Python function with return type "void" did not return None
CONTEXT: PL/Python function "test_void_func2"
SELECT test_return_none(), test_return_none() IS NULL AS "is null"; SELECT test_return_none(), test_return_none() IS NULL AS "is null";
test_return_none | is null test_return_none | is null
------------------+--------- ------------------+---------

View File

@ -1,7 +1,7 @@
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.122 2009/06/11 14:49:14 momjian Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.123 2009/07/20 08:01:06 petere Exp $
* *
********************************************************************* *********************************************************************
*/ */
@ -332,18 +332,33 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
fmgr_info_cxt(functionId, finfo, TopMemoryContext); fmgr_info_cxt(functionId, finfo, TopMemoryContext);
} }
static void
plpython_error_callback(void *arg)
{
if (PLy_curr_procedure)
errcontext("PL/Python function \"%s\"", PLy_procedure_name(PLy_curr_procedure));
}
Datum Datum
plpython_call_handler(PG_FUNCTION_ARGS) plpython_call_handler(PG_FUNCTION_ARGS)
{ {
Datum retval; Datum retval;
PLyProcedure *save_curr_proc; PLyProcedure *save_curr_proc;
PLyProcedure *volatile proc = NULL; PLyProcedure *volatile proc = NULL;
ErrorContextCallback plerrcontext;
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed"); elog(ERROR, "SPI_connect failed");
save_curr_proc = PLy_curr_procedure; save_curr_proc = PLy_curr_procedure;
/*
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpython_error_callback;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
PG_TRY(); PG_TRY();
{ {
if (CALLED_AS_TRIGGER(fcinfo)) if (CALLED_AS_TRIGGER(fcinfo))
@ -377,6 +392,9 @@ plpython_call_handler(PG_FUNCTION_ARGS)
} }
PG_END_TRY(); PG_END_TRY();
/* Pop the error context stack */
error_context_stack = plerrcontext.previous;
PLy_curr_procedure = save_curr_proc; PLy_curr_procedure = save_curr_proc;
Py_DECREF(proc->me); Py_DECREF(proc->me);
@ -545,8 +563,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
{ {
plstr = PyObject_Str(plval); plstr = PyObject_Str(plval);
if (!plstr) if (!plstr)
PLy_elog(ERROR, "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row", PLy_elog(ERROR, "could not compute string representation of Python object, while modifying trigger row");
proc->proname);
src = PyString_AsString(plstr); src = PyString_AsString(plstr);
modvalues[i] = modvalues[i] =
@ -942,7 +959,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
fcinfo->isnull = false; fcinfo->isnull = false;
plrv_so = PyObject_Str(plrv); plrv_so = PyObject_Str(plrv);
if (!plrv_so) if (!plrv_so)
PLy_elog(ERROR, "could not create string representation of Python object in PL/Python function \"%s\" while creating return value", proc->proname); PLy_elog(ERROR, "could not create string representation of Python object, while creating return value");
plrv_sc = PyString_AsString(plrv_so); plrv_sc = PyString_AsString(plrv_so);
rv = InputFunctionCall(&proc->result.out.d.typfunc, rv = InputFunctionCall(&proc->result.out.d.typfunc,
plrv_sc, plrv_sc,
@ -1061,11 +1078,11 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc)
} }
if (PyList_SetItem(args, i, arg) == -1) if (PyList_SetItem(args, i, arg) == -1)
PLy_elog(ERROR, "PyList_SetItem() failed for PL/Python function \"%s\" while setting up arguments", proc->proname); PLy_elog(ERROR, "PyList_SetItem() failed, while setting up arguments");
if (proc->argnames && proc->argnames[i] && if (proc->argnames && proc->argnames[i] &&
PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1) PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1)
PLy_elog(ERROR, "PyDict_SetItemString() failed for PL/Python function \"%s\" while setting up arguments", proc->proname); PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments");
arg = NULL; arg = NULL;
} }
} }
@ -2460,9 +2477,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PLy_exception_set(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"unrecognized error in PLy_spi_prepare"); "unrecognized error in PLy_spi_prepare");
/* XXX this oughta be replaced with errcontext mechanism */ PLy_elog(WARNING, NULL);
PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
PG_END_TRY(); PG_END_TRY();
@ -2530,8 +2545,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
PyObject *so = PyObject_Str(list); PyObject *so = PyObject_Str(list);
if (!so) if (!so)
PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan", PLy_elog(ERROR, "could not execute plan");
PLy_procedure_name(PLy_curr_procedure));
sv = PyString_AsString(so); sv = PyString_AsString(so);
PLy_exception_set_plural(PLy_exc_spi_error, PLy_exception_set_plural(PLy_exc_spi_error,
"Expected sequence of %d argument, got %d: %s", "Expected sequence of %d argument, got %d: %s",
@ -2559,8 +2573,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
{ {
so = PyObject_Str(elem); so = PyObject_Str(elem);
if (!so) if (!so)
PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan", PLy_elog(ERROR, "could not execute plan");
PLy_procedure_name(PLy_curr_procedure));
Py_DECREF(elem); Py_DECREF(elem);
PG_TRY(); PG_TRY();
@ -2624,9 +2637,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PLy_exception_set(PLy_exc_error, PLy_exception_set(PLy_exc_error,
"unrecognized error in PLy_spi_execute_plan"); "unrecognized error in PLy_spi_execute_plan");
/* XXX this oughta be replaced with errcontext mechanism */ PLy_elog(WARNING, NULL);
PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
PG_END_TRY(); PG_END_TRY();
@ -2671,9 +2682,7 @@ PLy_spi_execute_query(char *query, long limit)
if (!PyErr_Occurred()) if (!PyErr_Occurred())
PLy_exception_set(PLy_exc_spi_error, PLy_exception_set(PLy_exc_spi_error,
"unrecognized error in PLy_spi_execute_query"); "unrecognized error in PLy_spi_execute_query");
/* XXX this oughta be replaced with errcontext mechanism */ PLy_elog(WARNING, NULL);
PLy_elog(WARNING, "in PL/Python function \"%s\"",
PLy_procedure_name(PLy_curr_procedure));
return NULL; return NULL;
} }
PG_END_TRY(); PG_END_TRY();
@ -2987,9 +2996,11 @@ PLy_exception_set_plural(PyObject *exc,
PyErr_SetString(exc, buf); PyErr_SetString(exc, buf);
} }
/* Emit a PG error or notice, together with any available info about the /* Emit a PG error or notice, together with any available info about
* current Python error. This should be used to propagate Python errors * the current Python error, previously set by PLy_exception_set().
* into PG. * This should be used to propagate Python errors into PG. If fmt is
* NULL, the Python error becomes the primary error message, otherwise
* it becomes the detail.
*/ */
static void static void
PLy_elog(int elevel, const char *fmt,...) PLy_elog(int elevel, const char *fmt,...)
@ -3000,8 +3011,10 @@ PLy_elog(int elevel, const char *fmt,...)
xmsg = PLy_traceback(&xlevel); xmsg = PLy_traceback(&xlevel);
if (fmt)
{
initStringInfo(&emsg); initStringInfo(&emsg);
for (;;) for(;;)
{ {
va_list ap; va_list ap;
bool success; bool success;
@ -3013,15 +3026,21 @@ PLy_elog(int elevel, const char *fmt,...)
break; break;
enlargeStringInfo(&emsg, emsg.maxlen); enlargeStringInfo(&emsg, emsg.maxlen);
} }
}
PG_TRY(); PG_TRY();
{ {
if (fmt)
ereport(elevel, ereport(elevel,
(errmsg("PL/Python: %s", emsg.data), (errmsg("PL/Python: %s", emsg.data),
(xmsg) ? errdetail("%s", xmsg) : 0)); (xmsg) ? errdetail("%s", xmsg) : 0));
else
ereport(elevel,
(errmsg("PL/Python: %s", xmsg)));
} }
PG_CATCH(); PG_CATCH();
{ {
if (fmt)
pfree(emsg.data); pfree(emsg.data);
if (xmsg) if (xmsg)
pfree(xmsg); pfree(xmsg);
@ -3029,6 +3048,7 @@ PLy_elog(int elevel, const char *fmt,...)
} }
PG_END_TRY(); PG_END_TRY();
if (fmt)
pfree(emsg.data); pfree(emsg.data);
if (xmsg) if (xmsg)
pfree(xmsg); pfree(xmsg);