mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
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:
parent
983d10833e
commit
27c405d61a
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
------------------+---------
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user