From 27c405d61a909d11ded22ed0ac8038810e3f36e9 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Tue, 25 Aug 2009 12:44:59 +0000 Subject: [PATCH] Enhanced error context support in PL/Python Extract the "while creating return value" and "while modifying trigger row" parts of some error messages into another layer of error context. This will simplify the upcoming patch to improve data type support, but it can stand on its own. --- src/pl/plpython/expected/plpython_record.out | 9 +++-- src/pl/plpython/expected/plpython_trigger.out | 12 ++++--- src/pl/plpython/expected/plpython_types.out | 9 +++-- src/pl/plpython/expected/plpython_unicode.out | 10 +++--- .../plpython/expected/plpython_unicode_2.out | 10 +++--- .../plpython/expected/plpython_unicode_3.out | 10 +++--- src/pl/plpython/expected/plpython_void.out | 3 +- src/pl/plpython/plpython.c | 34 +++++++++++++++++-- 8 files changed, 71 insertions(+), 26 deletions(-) diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out index 9e4645d56cb..c8c4f9d5a3c 100644 --- a/src/pl/plpython/expected/plpython_record.out +++ b/src/pl/plpython/expected/plpython_record.out @@ -313,13 +313,15 @@ $$ LANGUAGE plpythonu; SELECT * FROM test_type_record_error1(); ERROR: key "second" not found in mapping HINT: To return null in a column, add the value None to the mapping with the key named after the column. -CONTEXT: PL/Python function "test_type_record_error1" +CONTEXT: while creating return value +PL/Python function "test_type_record_error1" CREATE FUNCTION test_type_record_error2() RETURNS type_record AS $$ return [ 'first' ] $$ LANGUAGE plpythonu; SELECT * FROM test_type_record_error2(); ERROR: length of returned sequence did not match number of columns in row -CONTEXT: PL/Python function "test_type_record_error2" +CONTEXT: while creating return value +PL/Python function "test_type_record_error2" CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$ class type_record: pass type_record.first = 'first' @@ -328,4 +330,5 @@ $$ LANGUAGE plpythonu; SELECT * FROM test_type_record_error3(); ERROR: attribute "second" does not exist in Python object HINT: To return null in a column, let the returned object have an attribute named after column with value None. -CONTEXT: PL/Python function "test_type_record_error3" +CONTEXT: while creating return value +PL/Python function "test_type_record_error3" diff --git a/src/pl/plpython/expected/plpython_trigger.out b/src/pl/plpython/expected/plpython_trigger.out index b60796dab5b..6be1c9dd0c1 100644 --- a/src/pl/plpython/expected/plpython_trigger.out +++ b/src/pl/plpython/expected/plpython_trigger.out @@ -353,7 +353,8 @@ BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE stupid4(); UPDATE trigger_test SET v = 'null' WHERE i = 0; ERROR: TD["new"] deleted, cannot modify row -CONTEXT: PL/Python function "stupid4" +CONTEXT: while modifying trigger row +PL/Python function "stupid4" DROP TRIGGER stupid_trigger4 ON trigger_test; -- TD not a dictionary CREATE FUNCTION stupid5() RETURNS trigger @@ -366,7 +367,8 @@ BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE stupid5(); UPDATE trigger_test SET v = 'null' WHERE i = 0; ERROR: TD["new"] is not a dictionary -CONTEXT: PL/Python function "stupid5" +CONTEXT: while modifying trigger row +PL/Python function "stupid5" DROP TRIGGER stupid_trigger5 ON trigger_test; -- TD not having string keys CREATE FUNCTION stupid6() RETURNS trigger @@ -379,7 +381,8 @@ BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE stupid6(); UPDATE trigger_test SET v = 'null' WHERE i = 0; ERROR: TD["new"] dictionary key at ordinal position 0 is not a string -CONTEXT: PL/Python function "stupid6" +CONTEXT: while modifying trigger row +PL/Python function "stupid6" DROP TRIGGER stupid_trigger6 ON trigger_test; -- TD keys not corresponding to row columns CREATE FUNCTION stupid7() RETURNS trigger @@ -392,7 +395,8 @@ BEFORE UPDATE ON trigger_test FOR EACH ROW EXECUTE PROCEDURE stupid7(); UPDATE trigger_test SET v = 'null' WHERE i = 0; ERROR: key "a" found in TD["new"] does not exist as a column in the triggering row -CONTEXT: PL/Python function "stupid7" +CONTEXT: while modifying trigger row +PL/Python function "stupid7" DROP TRIGGER stupid_trigger7 ON trigger_test; -- calling a trigger function directly SELECT stupid7(); diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out index 476f32974be..19b3c9e47a1 100644 --- a/src/pl/plpython/expected/plpython_types.out +++ b/src/pl/plpython/expected/plpython_types.out @@ -332,7 +332,8 @@ SELECT * FROM test_type_conversion_uint2(100::uint2, -50); INFO: (100, <type 'int'>) CONTEXT: PL/Python function "test_type_conversion_uint2" ERROR: value for domain uint2 violates check constraint "uint2_check" -CONTEXT: PL/Python function "test_type_conversion_uint2" +CONTEXT: while creating return value +PL/Python function "test_type_conversion_uint2" SELECT * FROM test_type_conversion_uint2(null, 1); INFO: (None, <type 'NoneType'>) CONTEXT: PL/Python function "test_type_conversion_uint2" @@ -360,11 +361,13 @@ SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world'); INFO: ('\\x68656c6c6f20776f7264', <type 'str'>) CONTEXT: PL/Python function "test_type_conversion_bytea10" ERROR: value for domain bytea10 violates check constraint "bytea10_check" -CONTEXT: PL/Python function "test_type_conversion_bytea10" +CONTEXT: while creating return value +PL/Python function "test_type_conversion_bytea10" SELECT * FROM test_type_conversion_bytea10(null, 'hello word'); ERROR: value for domain bytea10 violates check constraint "bytea10_check" SELECT * FROM test_type_conversion_bytea10('hello word', null); INFO: ('\\x68656c6c6f20776f7264', <type 'str'>) CONTEXT: PL/Python function "test_type_conversion_bytea10" ERROR: value for domain bytea10 violates check constraint "bytea10_check" -CONTEXT: PL/Python function "test_type_conversion_bytea10" +CONTEXT: while creating return value +PL/Python function "test_type_conversion_bytea10" diff --git a/src/pl/plpython/expected/plpython_unicode.out b/src/pl/plpython/expected/plpython_unicode.out index ce19eb980c2..d3b6fd1db73 100644 --- a/src/pl/plpython/expected/plpython_unicode.out +++ b/src/pl/plpython/expected/plpython_unicode.out @@ -24,13 +24,15 @@ rv = plpy.execute(plan, u"\\x80", 1) return rv[0]["testvalue1"] ' LANGUAGE plpythonu; SELECT unicode_return_error(); -ERROR: PL/Python: could not create string representation of Python object, while creating return value +ERROR: PL/Python: could not create string representation of Python object 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" +CONTEXT: while creating return value +PL/Python function "unicode_return_error" INSERT INTO unicode_test (testvalue) VALUES ('test'); -ERROR: PL/Python: could not compute string representation of Python object, while modifying trigger row +ERROR: PL/Python: could not create string representation of Python object 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" +CONTEXT: while modifying trigger row +PL/Python function "unicode_trigger_error" SELECT unicode_plan_error1(); WARNING: PL/Python: <class 'plpy.Error'>: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan_error1" diff --git a/src/pl/plpython/expected/plpython_unicode_2.out b/src/pl/plpython/expected/plpython_unicode_2.out index 9280fe7dde9..1f393fbef33 100644 --- a/src/pl/plpython/expected/plpython_unicode_2.out +++ b/src/pl/plpython/expected/plpython_unicode_2.out @@ -24,13 +24,15 @@ rv = plpy.execute(plan, u"\\x80", 1) return rv[0]["testvalue1"] ' LANGUAGE plpythonu; SELECT unicode_return_error(); -ERROR: PL/Python: could not create string representation of Python object, while creating return value +ERROR: PL/Python: could not create string representation of Python object DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) -CONTEXT: PL/Python function "unicode_return_error" +CONTEXT: while creating return value +PL/Python function "unicode_return_error" INSERT INTO unicode_test (testvalue) VALUES ('test'); -ERROR: PL/Python: could not compute string representation of Python object, while modifying trigger row +ERROR: PL/Python: could not create string representation of Python object DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) -CONTEXT: PL/Python function "unicode_trigger_error" +CONTEXT: while modifying trigger row +PL/Python function "unicode_trigger_error" SELECT unicode_plan_error1(); WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan_error1" diff --git a/src/pl/plpython/expected/plpython_unicode_3.out b/src/pl/plpython/expected/plpython_unicode_3.out index f058e2b0988..620f9f57906 100644 --- a/src/pl/plpython/expected/plpython_unicode_3.out +++ b/src/pl/plpython/expected/plpython_unicode_3.out @@ -24,13 +24,15 @@ rv = plpy.execute(plan, u"\\x80", 1) return rv[0]["testvalue1"] ' LANGUAGE plpythonu; SELECT unicode_return_error(); -ERROR: PL/Python: could not create string representation of Python object, while creating return value +ERROR: PL/Python: could not create string representation of Python object 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" +CONTEXT: while creating return value +PL/Python function "unicode_return_error" INSERT INTO unicode_test (testvalue) VALUES ('test'); -ERROR: PL/Python: could not compute string representation of Python object, while modifying trigger row +ERROR: PL/Python: could not create string representation of Python object 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" +CONTEXT: while modifying trigger row +PL/Python function "unicode_trigger_error" SELECT unicode_plan_error1(); WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan_error1" diff --git a/src/pl/plpython/expected/plpython_void.out b/src/pl/plpython/expected/plpython_void.out index d067de06c06..1080d12d6b2 100644 --- a/src/pl/plpython/expected/plpython_void.out +++ b/src/pl/plpython/expected/plpython_void.out @@ -20,7 +20,8 @@ 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" +CONTEXT: while creating return value +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 eec0288e493..4aba4ba95ad 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.126 2009/08/25 08:14:42 petere Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.127 2009/08/25 12:44:59 petere Exp $ * ********************************************************************* */ @@ -339,6 +339,20 @@ plpython_error_callback(void *arg) errcontext("PL/Python function \"%s\"", PLy_procedure_name(PLy_curr_procedure)); } +static void +plpython_trigger_error_callback(void *arg) +{ + if (PLy_curr_procedure) + errcontext("while modifying trigger row"); +} + +static void +plpython_return_error_callback(void *arg) +{ + if (PLy_curr_procedure) + errcontext("while creating return value"); +} + Datum plpython_call_handler(PG_FUNCTION_ARGS) { @@ -506,6 +520,11 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, Datum *volatile modvalues; char *volatile modnulls; TupleDesc tupdesc; + ErrorContextCallback plerrcontext; + + plerrcontext.callback = plpython_trigger_error_callback; + plerrcontext.previous = error_context_stack; + error_context_stack = &plerrcontext; plntup = plkeys = platt = plval = plstr = NULL; modattrs = NULL; @@ -563,7 +582,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, while modifying trigger row"); + PLy_elog(ERROR, "could not create string representation of Python object"); src = PyString_AsString(plstr); modvalues[i] = @@ -620,6 +639,8 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, pfree(modvalues); pfree(modnulls); + error_context_stack = plerrcontext.previous; + return rtup; } @@ -811,6 +832,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) PyObject *volatile plrv = NULL; PyObject *volatile plrv_so = NULL; char *plrv_sc; + ErrorContextCallback plerrcontext; PG_TRY(); { @@ -901,6 +923,10 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) } } + plerrcontext.callback = plpython_return_error_callback; + plerrcontext.previous = error_context_stack; + error_context_stack = &plerrcontext; + /* * If the function is declared to return void, the Python return value * must be None. For void-returning functions, we also treat a None @@ -959,7 +985,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, while creating return value"); + PLy_elog(ERROR, "could not create string representation of Python object"); plrv_sc = PyString_AsString(plrv_so); rv = InputFunctionCall(&proc->result.out.d.typfunc, plrv_sc, @@ -977,6 +1003,8 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc) } PG_END_TRY(); + error_context_stack = plerrcontext.previous; + Py_XDECREF(plargs); Py_DECREF(plrv); Py_XDECREF(plrv_so); -- GitLab