From 5106bdc45040e4aeb033f0c7f9e48fd3c8944258 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Mon, 20 Jul 2009 08:01:07 +0000 Subject: [PATCH] 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. --- src/pl/plpython/expected/plpython_error.out | 31 +++--- src/pl/plpython/expected/plpython_error_3.out | 31 +++--- .../plpython/expected/plpython_function.out | 30 ++++++ src/pl/plpython/expected/plpython_test.out | 2 + src/pl/plpython/plpython.c | 94 +++++++++++-------- 5 files changed, 127 insertions(+), 61 deletions(-) diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out index faaa79b341..ad2d6315ed 100644 --- a/src/pl/plpython/expected/plpython_error.out +++ b/src/pl/plpython/expected/plpython_error.out @@ -2,17 +2,20 @@ -- the trigger handler once. the errors and subsequent core dump were -- interesting. SELECT invalid_type_uncaught('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_uncaught" -DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_uncaught" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_uncaught" SELECT invalid_type_caught('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_caught" -DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_caught" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_caught" SELECT invalid_type_reraised('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_reraised" -DETAIL: plpy.SPIError: unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_reraised" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_reraised" SELECT valid_type('rick'); valid_type ------------ @@ -23,16 +26,20 @@ SELECT valid_type('rick'); -- Test Unicode error handling. -- 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) +CONTEXT: PL/Python function "unicode_return_error" 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) +CONTEXT: PL/Python function "unicode_trigger_error" SELECT unicode_plan_error1(); -WARNING: PL/Python: in PL/Python function "unicode_plan_error1" -DETAIL: plpy.Error: unrecognized error in PLy_spi_execute_plan -ERROR: PL/Python: PL/Python function "unicode_plan_error1" could not execute plan +WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan +CONTEXT: PL/Python function "unicode_plan_error1" +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) +CONTEXT: PL/Python function "unicode_plan_error1" 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) +CONTEXT: PL/Python function "unicode_plan_error2" diff --git a/src/pl/plpython/expected/plpython_error_3.out b/src/pl/plpython/expected/plpython_error_3.out index 548f7ae3ca..be8d358118 100644 --- a/src/pl/plpython/expected/plpython_error_3.out +++ b/src/pl/plpython/expected/plpython_error_3.out @@ -2,17 +2,20 @@ -- the trigger handler once. the errors and subsequent core dump were -- interesting. SELECT invalid_type_uncaught('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_uncaught" -DETAIL: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_uncaught" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_uncaught" SELECT invalid_type_caught('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_caught" -DETAIL: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_caught" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_caught" SELECT invalid_type_reraised('rick'); -WARNING: PL/Python: in PL/Python function "invalid_type_reraised" -DETAIL: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +CONTEXT: PL/Python function "invalid_type_reraised" ERROR: type "test" does not exist +CONTEXT: PL/Python function "invalid_type_reraised" SELECT valid_type('rick'); valid_type ------------ @@ -23,16 +26,20 @@ SELECT valid_type('rick'); -- Test Unicode error handling. -- 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: : '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'); -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: : '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(); -WARNING: PL/Python: in PL/Python function "unicode_plan_error1" -DETAIL: : unrecognized error in PLy_spi_execute_plan -ERROR: PL/Python: PL/Python function "unicode_plan_error1" could not execute plan +WARNING: PL/Python: : unrecognized error in PLy_spi_execute_plan +CONTEXT: PL/Python function "unicode_plan_error1" +ERROR: PL/Python: could not execute plan DETAIL: : '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(); -ERROR: PL/Python: PL/Python function "unicode_plan_error2" could not execute plan +ERROR: PL/Python: could not execute plan DETAIL: : 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +CONTEXT: PL/Python function "unicode_plan_error2" diff --git a/src/pl/plpython/expected/plpython_function.out b/src/pl/plpython/expected/plpython_function.out index 79e225398e..6dfefdb4a6 100644 --- a/src/pl/plpython/expected/plpython_function.out +++ b/src/pl/plpython/expected/plpython_function.out @@ -136,37 +136,67 @@ BEFORE INSERT OR UPDATE OR DELETE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo'); insert into trigger_test values(1,'insert'); NOTICE: ("TD[args] => ['23', 'skidoo']",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[event] => INSERT',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[level] => ROW',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[name] => show_trigger_data_trig',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ("TD[new] => {'i': 1, 'v': 'insert'}",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[old] => None',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[relid] => bogus:12345',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_name] => trigger_test',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_schema] => public',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[when] => BEFORE',) +CONTEXT: PL/Python function "trigger_data" update trigger_test set v = 'update' where i = 1; NOTICE: ("TD[args] => ['23', 'skidoo']",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[event] => UPDATE',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[level] => ROW',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[name] => show_trigger_data_trig',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ("TD[new] => {'i': 1, 'v': 'update'}",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ("TD[old] => {'i': 1, 'v': 'insert'}",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[relid] => bogus:12345',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_name] => trigger_test',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_schema] => public',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[when] => BEFORE',) +CONTEXT: PL/Python function "trigger_data" delete from trigger_test; NOTICE: ("TD[args] => ['23', 'skidoo']",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[event] => DELETE',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[level] => ROW',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[name] => show_trigger_data_trig',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[new] => None',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ("TD[old] => {'i': 1, 'v': 'update'}",) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[relid] => bogus:12345',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_name] => trigger_test',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[table_schema] => public',) +CONTEXT: PL/Python function "trigger_data" NOTICE: ('TD[when] => BEFORE',) +CONTEXT: PL/Python function "trigger_data" DROP TRIGGER show_trigger_data_trig on trigger_test; diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out index b84660da43..333a9e62d6 100644 --- a/src/pl/plpython/expected/plpython_test.out +++ b/src/pl/plpython/expected/plpython_test.out @@ -38,6 +38,7 @@ SELECT global_test_two(); -- SELECT import_fail(); NOTICE: ('import socket failed -- No module named foosocket',) +CONTEXT: PL/Python function "import_fail" import_fail -------------------- 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 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"; test_return_none | is null ------------------+--------- diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 43c7fce2e5..e68c89080c 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * 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); } +static void +plpython_error_callback(void *arg) +{ + if (PLy_curr_procedure) + errcontext("PL/Python function \"%s\"", PLy_procedure_name(PLy_curr_procedure)); +} + Datum plpython_call_handler(PG_FUNCTION_ARGS) { Datum retval; PLyProcedure *save_curr_proc; PLyProcedure *volatile proc = NULL; + ErrorContextCallback plerrcontext; if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); 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(); { if (CALLED_AS_TRIGGER(fcinfo)) @@ -377,6 +392,9 @@ plpython_call_handler(PG_FUNCTION_ARGS) } PG_END_TRY(); + /* Pop the error context stack */ + error_context_stack = plerrcontext.previous; + PLy_curr_procedure = save_curr_proc; Py_DECREF(proc->me); @@ -545,8 +563,7 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, { plstr = PyObject_Str(plval); if (!plstr) - PLy_elog(ERROR, "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row", - proc->proname); + PLy_elog(ERROR, "could not compute string representation of Python object, while modifying trigger row"); src = PyString_AsString(plstr); modvalues[i] = @@ -942,7 +959,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) fcinfo->isnull = false; plrv_so = PyObject_Str(plrv); 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); rv = InputFunctionCall(&proc->result.out.d.typfunc, plrv_sc, @@ -1061,11 +1078,11 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc) } 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] && 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; } } @@ -2460,9 +2477,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args) if (!PyErr_Occurred()) PLy_exception_set(PLy_exc_spi_error, "unrecognized error in PLy_spi_prepare"); - /* XXX this oughta be replaced with errcontext mechanism */ - PLy_elog(WARNING, "in PL/Python function \"%s\"", - PLy_procedure_name(PLy_curr_procedure)); + PLy_elog(WARNING, NULL); return NULL; } PG_END_TRY(); @@ -2530,8 +2545,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) PyObject *so = PyObject_Str(list); if (!so) - PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan", - PLy_procedure_name(PLy_curr_procedure)); + PLy_elog(ERROR, "could not execute plan"); sv = PyString_AsString(so); PLy_exception_set_plural(PLy_exc_spi_error, "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); if (!so) - PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan", - PLy_procedure_name(PLy_curr_procedure)); + PLy_elog(ERROR, "could not execute plan"); Py_DECREF(elem); PG_TRY(); @@ -2624,9 +2637,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit) if (!PyErr_Occurred()) PLy_exception_set(PLy_exc_error, "unrecognized error in PLy_spi_execute_plan"); - /* XXX this oughta be replaced with errcontext mechanism */ - PLy_elog(WARNING, "in PL/Python function \"%s\"", - PLy_procedure_name(PLy_curr_procedure)); + PLy_elog(WARNING, NULL); return NULL; } PG_END_TRY(); @@ -2671,9 +2682,7 @@ PLy_spi_execute_query(char *query, long limit) if (!PyErr_Occurred()) PLy_exception_set(PLy_exc_spi_error, "unrecognized error in PLy_spi_execute_query"); - /* XXX this oughta be replaced with errcontext mechanism */ - PLy_elog(WARNING, "in PL/Python function \"%s\"", - PLy_procedure_name(PLy_curr_procedure)); + PLy_elog(WARNING, NULL); return NULL; } PG_END_TRY(); @@ -2987,9 +2996,11 @@ PLy_exception_set_plural(PyObject *exc, PyErr_SetString(exc, buf); } -/* Emit a PG error or notice, together with any available info about the - * current Python error. This should be used to propagate Python errors - * into PG. +/* Emit a PG error or notice, together with any available info about + * the current Python error, previously set by PLy_exception_set(). + * 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 PLy_elog(int elevel, const char *fmt,...) @@ -3000,36 +3011,45 @@ PLy_elog(int elevel, const char *fmt,...) xmsg = PLy_traceback(&xlevel); - initStringInfo(&emsg); - for (;;) + if (fmt) { - va_list ap; - bool success; + initStringInfo(&emsg); + for(;;) + { + va_list ap; + bool success; - va_start(ap, fmt); - success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap); - va_end(ap); - if (success) - break; - enlargeStringInfo(&emsg, emsg.maxlen); + va_start(ap, fmt); + success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap); + va_end(ap); + if (success) + break; + enlargeStringInfo(&emsg, emsg.maxlen); + } } PG_TRY(); { - ereport(elevel, - (errmsg("PL/Python: %s", emsg.data), - (xmsg) ? errdetail("%s", xmsg) : 0)); + if (fmt) + ereport(elevel, + (errmsg("PL/Python: %s", emsg.data), + (xmsg) ? errdetail("%s", xmsg) : 0)); + else + ereport(elevel, + (errmsg("PL/Python: %s", xmsg))); } PG_CATCH(); { - pfree(emsg.data); + if (fmt) + pfree(emsg.data); if (xmsg) pfree(xmsg); PG_RE_THROW(); } PG_END_TRY(); - pfree(emsg.data); + if (fmt) + pfree(emsg.data); if (xmsg) pfree(xmsg); }