Enhanced error context support in PL/Python
authorPeter Eisentraut <peter_e@gmx.net>
Tue, 25 Aug 2009 12:44:59 +0000 (12:44 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Tue, 25 Aug 2009 12:44:59 +0000 (12:44 +0000)
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
src/pl/plpython/expected/plpython_trigger.out
src/pl/plpython/expected/plpython_types.out
src/pl/plpython/expected/plpython_unicode.out
src/pl/plpython/expected/plpython_unicode_2.out
src/pl/plpython/expected/plpython_unicode_3.out
src/pl/plpython/expected/plpython_void.out
src/pl/plpython/plpython.c

index 9e4645d56cb49c3c771f4046bfd6e16ca0636419..c8c4f9d5a3cbb02a8d549dff1dd6f7216b8c7d6a 100644 (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"
index b60796dab5b07df6c8405bdfa2bb4e9cd92f9a1a..6be1c9dd0c1e6d904394abbb4d3e0c76cf9c6b63 100644 (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();
index 476f32974be4274859a472492959149479ad6442..19b3c9e47a1a080b620a3c60775ec6d59b1d283b 100644 (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"
index ce19eb980c231f0f20f27d8cd54aa08a8e403b78..d3b6fd1db73aa37bb0ffc6dbec289ba1b7922112 100644 (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"
index 9280fe7dde9882c75a539b3945955efffd47decc..1f393fbef33214c7187760797bbcb2cc496e0b28 100644 (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"
index f058e2b098855b38f55d14ac2c25573be25e829e..620f9f57906360b6bacb4d72bef89ef1332c54a7 100644 (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"
index d067de06c061a2d85fe94a88ecdf080f957e70be..1080d12d6b257439f8f041a880463ac1e3e91c27 100644 (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 
 ------------------+---------
index eec0288e493fcdc0cf4a593e84643eaf289ca064..4aba4ba95adf804ff2861d6f7b885c04bedc16c6 100644 (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);