From 96b0da67ff82268d6d2dc6eeabaa277ba4a2fab4 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 25 Jan 2010 01:58:19 +0000
Subject: [PATCH] Apply Tcl_Init() to the "hold" interpreter created by pltcl.

You might think this is unnecessary since that interpreter is never used
to run code --- but it turns out that's wrong.  As of Tcl 8.5, the "clock"
command (alone among builtin Tcl commands) is partially implemented by
loaded-on-demand Tcl code, which means that it fails if there's not
unknown-command support, and also that it's impossible to run it directly
in a safe interpreter.  The way they get around the latter is that
Tcl_CreateSlave() automatically sets up an alias command that forwards any
execution of "clock" in a safe slave interpreter to its parent interpreter.
Thus, when attempting to execute "clock" in trusted pltcl, the command
actually executes in the "hold" interpreter, where it will fail if
unknown-command support hasn't been introduced by sourcing the standard
init.tcl script, which is done by Tcl_Init().  (This is a pretty dubious
design decision on the Tcl boys' part, if you ask me ... but they didn't.)

Back-patch all the way.  It's not clear that anyone would try to use ancient
versions of pltcl with a recent Tcl, but it's not clear they wouldn't, either.
Also add a regression test using "clock", in branches that have regression
test support for pltcl.

Per recent trouble report from Kyle Bateman.
---
 src/pl/tcl/expected/pltcl_setup.out | 20 ++++++++++++++++++++
 src/pl/tcl/pltcl.c                  |  7 +++++--
 src/pl/tcl/sql/pltcl_setup.sql      | 12 ++++++++++++
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out
index 496cf228dc..e46c1c3e98 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -495,3 +495,23 @@ CREATE OPERATOR CLASS tcl_int4_ops
 	OPERATOR 4  @>=,
 	OPERATOR 5  @>,
 	FUNCTION 1  tcl_int4cmp(int4,int4) ;
+--
+-- Test usage of Tcl's "clock" command.  In recent Tcl versions this
+-- command fails without working "unknown" support, so it's a good canary
+-- for initialization problems.
+--
+create function tcl_date_week(int4,int4,int4) returns text as $$
+    return [clock format [clock scan "$2/$3/$1"] -format "%U"]
+$$ language pltcl immutable;
+select tcl_date_week(2010,1,24);
+ tcl_date_week 
+---------------
+ 04
+(1 row)
+
+select tcl_date_week(2001,10,24);
+ tcl_date_week 
+---------------
+ 42
+(1 row)
+
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 2e8db71275..dfca5852f3 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -2,7 +2,7 @@
  * pltcl.c		- PostgreSQL support for Tcl as
  *				  procedural language (PL)
  *
- *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.128 2009/06/11 14:49:14 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.128.2.1 2010/01/25 01:58:19 tgl Exp $
  *
  **********************************************************************/
 
@@ -304,9 +304,12 @@ _PG_init(void)
 	 ************************************************************/
 	if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
 		elog(ERROR, "could not create \"hold\" interpreter");
+	if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
+		elog(ERROR, "could not initialize \"hold\" interpreter");
 
 	/************************************************************
-	 * Create the two interpreters
+	 * Create the two slave interpreters.  Note: Tcl automatically does
+	 * Tcl_Init on the normal slave, and it's not wanted for the safe slave.
 	 ************************************************************/
 	if ((pltcl_norm_interp =
 		 Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 55ac7e20d5..4a581ed8b1 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -542,3 +542,15 @@ CREATE OPERATOR CLASS tcl_int4_ops
 	OPERATOR 4  @>=,
 	OPERATOR 5  @>,
 	FUNCTION 1  tcl_int4cmp(int4,int4) ;
+
+--
+-- Test usage of Tcl's "clock" command.  In recent Tcl versions this
+-- command fails without working "unknown" support, so it's a good canary
+-- for initialization problems.
+--
+create function tcl_date_week(int4,int4,int4) returns text as $$
+    return [clock format [clock scan "$2/$3/$1"] -format "%U"]
+$$ language pltcl immutable;
+
+select tcl_date_week(2010,1,24);
+select tcl_date_week(2001,10,24);