]> granicus.if.org Git - postgresql/commitdiff
Use errcontext mechanism in PL/Python
authorPeter Eisentraut <peter_e@gmx.net>
Mon, 20 Jul 2009 08:01:07 +0000 (08:01 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Mon, 20 Jul 2009 08:01:07 +0000 (08:01 +0000)
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
src/pl/plpython/expected/plpython_error_3.out
src/pl/plpython/expected/plpython_function.out
src/pl/plpython/expected/plpython_test.out
src/pl/plpython/plpython.c

index faaa79b341db56af55b7a29fc276486f0c3755b0..ad2d6315ed0fa27ebff7aceb1dd31c420de6d016 100644 (file)
@@ -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"
index 548f7ae3ca5ea95b1c4f5f9017d4006592f5bb90..be8d3581184220250fb7ffe025c5284fd4d03069 100644 (file)
@@ -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:  <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
+WARNING:  PL/Python: <class '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:  <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
+WARNING:  PL/Python: <class '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:  <class 'plpy.SPIError'>: unrecognized error in PLy_spi_prepare
+WARNING:  PL/Python: <class '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:  <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"
 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:  <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"
 SELECT unicode_plan_error1();
-WARNING:  PL/Python: in PL/Python function "unicode_plan_error1"
-DETAIL:  <class '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: <class 'plpy.Error'>: unrecognized error in PLy_spi_execute_plan
+CONTEXT:  PL/Python function "unicode_plan_error1"
+ERROR:  PL/Python: could not execute plan
 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_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:  <type '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"
index 79e225398e80d3be8102cef184a47e6c27fc163d..6dfefdb4a61196bf0f108f329a32bc2be2d12666 100644 (file)
@@ -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;
       
index b84660da437ecec8ee42195059dc2782499ca668..333a9e62d6d460cff26285f277a725c38f78baa1 100644 (file)
@@ -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 
 ------------------+---------
index 43c7fce2e5fe388fa5d5a7dcf9abc77d74bc3973..e68c89080c33850f4ce669c3750499888c7f180d 100644 (file)
@@ -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;
-
-               va_start(ap, fmt);
-               success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
-               va_end(ap);
-               if (success)
-                       break;
-               enlargeStringInfo(&emsg, emsg.maxlen);
+               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);
+               }
        }
 
        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);
 }