* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.98.2.2 2006/01/17 17:33:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.98.2.3 2008/06/17 00:53:04 tgl Exp $
*
**********************************************************************/
#include "utils/syscache.h"
#include "utils/typcache.h"
+#define HAVE_TCL_VERSION(maj,min) \
+ ((TCL_MAJOR_VERSION > maj) || \
+ (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
-#if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
- && TCL_MINOR_VERSION > 0
+/* In Tcl >= 8.0, really not supposed to touch interp->result directly */
+#if !HAVE_TCL_VERSION(8,0)
+#define Tcl_GetStringResult(interp) ((interp)->result)
+#endif
+
+#if defined(UNICODE_CONVERSION) && HAVE_TCL_VERSION(8,1)
#include "mb/pg_wchar.h"
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
+static void throw_tcl_error(Tcl_Interp *interp);
+
static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid);
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
* Check for errors reported by Tcl.
************************************************************/
if (tcl_rc != TCL_OK)
- {
- UTF_BEGIN;
- ereport(ERROR,
- (errmsg("%s", interp->result),
- errcontext("%s",
- UTF_U2E(Tcl_GetVar(interp, "errorInfo",
- TCL_GLOBAL_ONLY)))));
- UTF_END;
- }
+ throw_tcl_error(interp);
/************************************************************
* Disconnect from SPI manager and then create the return
* this must not be allocated in the SPI memory context
* because SPI_finish would free it). But don't try to call
* the result_in_func if we've been told to return a NULL;
- * the contents of interp->result may not be a valid value of
- * the result type in that case.
+ * the Tcl result may not be a valid value of the result type
+ * in that case.
************************************************************/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed");
{
UTF_BEGIN;
retval = FunctionCall3(&prodesc->result_in_func,
- PointerGetDatum(UTF_U2E(interp->result)),
+ PointerGetDatum(UTF_U2E((char *) Tcl_GetStringResult(interp))),
+
ObjectIdGetDatum(prodesc->result_typioparam),
Int32GetDatum(-1));
UTF_END;
Datum *modvalues;
char *modnulls;
int ret_numvals;
+ CONST84 char *result;
CONST84 char **ret_values;
/* Connect to SPI manager */
* Check for errors reported by Tcl.
************************************************************/
if (tcl_rc != TCL_OK)
- {
- UTF_BEGIN;
- ereport(ERROR,
- (errmsg("%s", interp->result),
- errcontext("%s",
- UTF_U2E(Tcl_GetVar(interp, "errorInfo",
- TCL_GLOBAL_ONLY)))));
- UTF_END;
- }
+ throw_tcl_error(interp);
/************************************************************
* The return value from the procedure might be one of
- * the magic strings OK or SKIP or a list from array get
+ * the magic strings OK or SKIP or a list from array get.
+ * We can check for OK or SKIP without worrying about encoding.
************************************************************/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish() failed");
- if (strcmp(interp->result, "OK") == 0)
+ result = Tcl_GetStringResult(interp);
+
+ if (strcmp(result, "OK") == 0)
return rettup;
- if (strcmp(interp->result, "SKIP") == 0)
+ if (strcmp(result, "SKIP") == 0)
return (HeapTuple) NULL;
/************************************************************
* Convert the result value from the Tcl interpreter
* and setup structures for SPI_modifytuple();
************************************************************/
- if (Tcl_SplitList(interp, interp->result,
+ if (Tcl_SplitList(interp, result,
&ret_numvals, &ret_values) != TCL_OK)
+ {
+ UTF_BEGIN;
elog(ERROR, "could not split return value from trigger: %s",
- interp->result);
+ UTF_U2E(Tcl_GetStringResult(interp)));
+ UTF_END;
+ }
/* Use a TRY to ensure ret_values will get freed */
PG_TRY();
}
+/**********************************************************************
+ * throw_tcl_error - ereport an error returned from the Tcl interpreter
+ **********************************************************************/
+static void
+throw_tcl_error(Tcl_Interp *interp)
+{
+ /*
+ * Caution is needed here because Tcl_GetVar could overwrite the
+ * interpreter result (even though it's not really supposed to),
+ * and we can't control the order of evaluation of ereport arguments.
+ * Hence, make real sure we have our own copy of the result string
+ * before invoking Tcl_GetVar.
+ */
+ char *emsg;
+ char *econtext;
+
+ UTF_BEGIN;
+ emsg = pstrdup(UTF_U2E(Tcl_GetStringResult(interp)));
+ UTF_END;
+ UTF_BEGIN;
+ econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
+ TCL_GLOBAL_ONLY));
+ ereport(ERROR,
+ (errmsg("%s", emsg),
+ errcontext("%s", econtext)));
+ UTF_END;
+}
+
+
/**********************************************************************
* compile_pltcl_function - compile (or hopefully just look up) function
*
{
free(prodesc->proname);
free(prodesc);
+ UTF_BEGIN;
elog(ERROR, "could not create internal procedure \"%s\": %s",
- internal_proname, interp->result);
+ internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
+ UTF_END;
}
/************************************************************
if (argc != 3)
{
- Tcl_SetResult(interp, "syntax error - 'elog level msg'",
- TCL_VOLATILE);
+ Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
return TCL_ERROR;
}
return TCL_ERROR;
}
- /************************************************************
- * If elog() throws an error, catch it and return the error to the
- * Tcl interpreter. Note we are assuming that elog() can't have any
+ if (level == ERROR)
+ {
+ /*
+ * We just pass the error back to Tcl. If it's not caught,
+ * it'll eventually get converted to a PG error when we reach
+ * the call handler.
+ */
+ Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+
+ /*
+ * For non-error messages, just pass 'em to elog(). We do not expect
+ * that this will fail, but just on the off chance it does, report the
+ * error back to Tcl. Note we are assuming that elog() can't have any
* internal failures that are so bad as to require a transaction abort.
- ************************************************************/
+ *
+ * This path is also used for FATAL errors, which aren't going to come
+ * back to us at all.
+ */
oldcontext = CurrentMemoryContext;
PG_TRY();
{
FlushErrorState();
/* Pass the error message to Tcl */
- Tcl_SetResult(interp, edata->message, TCL_VOLATILE);
+ UTF_BEGIN;
+ Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
+ UTF_END;
FreeErrorData(edata);
return TCL_ERROR;
************************************************************/
if (argc != 2)
{
- Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE);
+ Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
return TCL_ERROR;
}
************************************************************/
if (argc != 2)
{
- Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE);
+ Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
+ TCL_STATIC);
return TCL_ERROR;
}
if (fcinfo == NULL)
{
Tcl_SetResult(interp, "argisnull cannot be used in triggers",
- TCL_VOLATILE);
+ TCL_STATIC);
return TCL_ERROR;
}
argno--;
if (argno < 0 || argno >= fcinfo->nargs)
{
- Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE);
+ Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
return TCL_ERROR;
}
* Get the requested NULL state
************************************************************/
if (PG_ARGISNULL(argno))
- Tcl_SetResult(interp, "1", TCL_VOLATILE);
+ Tcl_SetResult(interp, "1", TCL_STATIC);
else
- Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ Tcl_SetResult(interp, "0", TCL_STATIC);
return TCL_OK;
}
************************************************************/
if (argc != 1)
{
- Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE);
+ Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
return TCL_ERROR;
}
if (fcinfo == NULL)
{
Tcl_SetResult(interp, "return_null cannot be used in triggers",
- TCL_VOLATILE);
+ TCL_STATIC);
return TCL_ERROR;
}
SPI_restore_connection();
/* Pass the error message to Tcl */
- Tcl_SetResult(interp, edata->message, TCL_VOLATILE);
+ UTF_BEGIN;
+ Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
+ UTF_END;
FreeErrorData(edata);
}
************************************************************/
if (argc < 2)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
{
if (++i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
arrayname = argv[i++];
{
if (++i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
query_idx = i;
if (query_idx >= argc || query_idx + 2 < argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
if (query_idx + 1 < argc)
switch (spi_rc)
{
case SPI_OK_UTILITY:
- Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ Tcl_SetResult(interp, "0", TCL_STATIC);
break;
case SPI_OK_SELINTO:
if (argc != 3)
{
Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
- TCL_VOLATILE);
+ TCL_STATIC);
return TCL_ERROR;
}
ckfree((char *) args);
+ /* qname is ASCII, so no need for encoding conversion */
Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
return TCL_OK;
}
{
if (++i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
arrayname = argv[i++];
{
if (++i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
nulls = argv[i++];
{
if (++i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
************************************************************/
if (i >= argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}
{
Tcl_SetResult(interp,
"length of nulls string doesn't match # of arguments",
- TCL_VOLATILE);
+ TCL_STATIC);
return TCL_ERROR;
}
}
{
if (i >= argc)
{
- Tcl_SetResult(interp, "missing argument list", TCL_VOLATILE);
+ Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
return TCL_ERROR;
}
{
Tcl_SetResult(interp,
"argument list length doesn't match # of arguments for query",
- TCL_VOLATILE);
+ TCL_STATIC);
ckfree((char *) callargs);
return TCL_ERROR;
}
if (i != argc)
{
- Tcl_SetResult(interp, usage, TCL_VOLATILE);
+ Tcl_SetResult(interp, usage, TCL_STATIC);
return TCL_ERROR;
}