[go: up one dir, main page]

Fix pl/tcl's handling of errors from Tcl_ListObjGetElements().
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Jun 2024 22:02:13 +0000 (18:02 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Jun 2024 22:02:13 +0000 (18:02 -0400)
In a procedure or function returning tuple, we use that function to
parse the Tcl script's result, which is supposed to be a Tcl list.
If it isn't, you get an error.  Commit 26abb50c4 incautiously
supposed that we could use throw_tcl_error() to report such an error.
That doesn't actually work, because low-level functions like
Tcl_ListObjGetElements() don't fill Tcl's errorInfo variable.
The result is either a null-pointer-dereference crash or emission
of misleading context information describing the previous Tcl error.

Back off to just reporting the interpreter's result string, and
improve throw_tcl_error()'s comment to explain when to use it.

Also, although the similar code in pltcl_trigger_handler() avoided
this mistake, it was using a fairly confusing wording of the
error message.  Improve that while we're here.

Per report from A. Kozhemyakin.  Back-patch to all supported
branches.

Erik Wienhold and Tom Lane

Discussion: https://postgr.es/m/6a2a1c40-2b2c-4a33-8b72-243c0766fcda@postgrespro.ru

src/pl/tcl/expected/pltcl_call.out
src/pl/tcl/pltcl.c
src/pl/tcl/sql/pltcl_call.sql

index e4498375ec1a059b1a7005d3d3f655a4ef743500..7bb5dffe5d7eddb22c80e354ab586cab2aaa7fbb 100644 (file)
@@ -66,6 +66,14 @@ END
 $$;
 NOTICE:  a: 10
 NOTICE:  _a: 10, _b: 20
+-- syntax error in result tuple
+CREATE PROCEDURE test_proc10(INOUT a text)
+LANGUAGE pltcl
+AS $$
+return [list a {$a + $a}])
+$$;
+CALL test_proc10('abc');
+ERROR:  could not parse function return value: list element in braces followed by ")" instead of space
 DROP PROCEDURE test_proc1;
 DROP PROCEDURE test_proc2;
 DROP PROCEDURE test_proc3;
index 11f1ff19139a3644f14689ccbeeeb9bbf8a4576c..ecf54b9894261b0002368dbd7b1bd77f975498a3 100644 (file)
@@ -1027,7 +1027,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
        /* Convert function result to tuple */
        resultObj = Tcl_GetObjResult(interp);
        if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
-           throw_tcl_error(interp, prodesc->user_proname);
+           ereport(ERROR,
+                   (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
+                    errmsg("could not parse function return value: %s",
+                           utf_u2e(Tcl_GetStringResult(interp)))));
 
        tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
                                       call_state);
@@ -1293,7 +1296,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
                               &result_Objc, &result_Objv) != TCL_OK)
        ereport(ERROR,
                (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
-                errmsg("could not split return value from trigger: %s",
+                errmsg("could not parse trigger return value: %s",
                        utf_u2e(Tcl_GetStringResult(interp)))));
 
    /* Convert function result to tuple */
@@ -1356,6 +1359,10 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
 
 /**********************************************************************
  * throw_tcl_error - ereport an error returned from the Tcl interpreter
+ *
+ * Caution: use this only to report errors returned by Tcl_EvalObjEx() or
+ * other variants of Tcl_Eval().  Other functions may not fill "errorInfo",
+ * so it could be unset or even contain details from some previous error.
  **********************************************************************/
 static void
 throw_tcl_error(Tcl_Interp *interp, const char *proname)
index 37efbdefc2315a33e15994fe2b5fdc401ccec4a1..fe64361c732841c8c0a5396824c6c61ea72f0f68 100644 (file)
@@ -71,6 +71,17 @@ END
 $$;
 
 
+-- syntax error in result tuple
+
+CREATE PROCEDURE test_proc10(INOUT a text)
+LANGUAGE pltcl
+AS $$
+return [list a {$a + $a}])
+$$;
+
+CALL test_proc10('abc');
+
+
 DROP PROCEDURE test_proc1;
 DROP PROCEDURE test_proc2;
 DROP PROCEDURE test_proc3;