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.
This commit is contained in:
Peter Eisentraut 2009-08-25 12:44:59 +00:00
parent 983d10833e
commit 27c405d61a
8 changed files with 71 additions and 26 deletions

View File

@ -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"

View File

@ -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();

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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
------------------+---------

View File

@ -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);