diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 5c3cecd840df48c2ecead3632d8f4b2ea51a4dfb..d471e6da00ec287b47922af2073d8f6f1d98bfda 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -167,6 +167,11 @@ su - postgres
       recent <productname>Perl</productname> versions, but it was not
       in earlier versions, and in any case it is the choice of whomever
       installed Perl at your site.
+      If you intend to make more than incidental use of
+      <application>PL/Perl</application>, you should ensure that the
+      <productname>Perl</productname> installation was built with the
+      <literal>usemultiplicity</> option enabled (<literal>perl -V</>
+      will show whether this is the case).
      </para>
 
      <para>
diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml
index 864b53d67a8fd1453231ea3c9a4ba93b3c5ef341..d2584623b5a966d2d37c071f2259694e9a31a679 100644
--- a/doc/src/sgml/plperl.sgml
+++ b/doc/src/sgml/plperl.sgml
@@ -41,7 +41,7 @@
    <para>
     Users of source packages must specially enable the build of
     PL/Perl during the installation process.  (Refer to <xref
-    linkend="install-short"> for more information.)  Users of
+    linkend="installation"> for more information.)  Users of
     binary packages might find PL/Perl in a separate subpackage.
    </para>
   </note>
@@ -101,7 +101,7 @@ $$ LANGUAGE plperl;
    most convenient to use dollar quoting (see <xref
    linkend="sql-syntax-dollar-quoting">) for the string constant.
    If you choose to use escape string syntax <literal>E''</>,
-   you must double the single quote marks (<literal>'</>) and backslashes
+   you must double any single quote marks (<literal>'</>) and backslashes
    (<literal>\</>) used in the body of the function
    (see <xref linkend="sql-syntax-strings">).
   </para>
@@ -829,10 +829,20 @@ $$ LANGUAGE plperl;
   </para>
 
   <para>
-    The <varname>%_SHARED</varname> variable and other global state within
-    the language are public data, available to all PL/Perl functions within a
-    session. Use with care, especially in situations that involve use of 
-    multiple roles or <literal>SECURITY DEFINER</> functions.
+   For security reasons, PL/Perl executes functions called by any one SQL role
+   in a separate Perl interpreter for that role.  This prevents accidental or
+   malicious interference by one user with the behavior of another user's
+   PL/Perl functions.  Each such interpreter has its own value of the
+   <varname>%_SHARED</varname> variable and other global state.  Thus, two
+   PL/Perl functions will share the same value of <varname>%_SHARED</varname>
+   if and only if they are executed by the same SQL role.  In an application
+   wherein a single session executes code under multiple SQL roles (via
+   <literal>SECURITY DEFINER</> functions, use of <command>SET ROLE</>, etc)
+   you may need to take explicit steps to ensure that PL/Perl functions can
+   share data via <varname>%_SHARED</varname>.  To do that, make sure that
+   functions that should communicate are owned by the same user, and mark
+   them <literal>SECURITY DEFINER</>.  You must of course take care that
+   such functions can't be used to do anything unintended.
   </para>
  </sect1>
 
@@ -908,22 +918,31 @@ $$ LANGUAGE plperl;
   </para>
 
   <note>
-    <para>
-      For security reasons, to stop a leak of privileged operations from
-      <application>PL/PerlU</> to <application>PL/Perl</>, these two languages
-      have to run in separate instances of the Perl interpreter. If your
-      Perl installation has been appropriately compiled, this is not a problem.
-      However, not all installations are compiled with the requisite flags.
-      If <productname>PostgreSQL</> detects that this is the case then it will
-      not start a second interpreter, but instead create an error. In
-      consequence, in such an installation, you cannot use both
-      <application>PL/PerlU</> and <application>PL/Perl</> in the same backend
-      process. The remedy for this is to obtain a Perl installation configured
-      with the appropriate flags, namely either <literal>usemultiplicity</>
-      or <literal>useithreads</>. <literal>usemultiplicity</> is preferred
-      unless you actually need to use threads. For more details, see the
-      <citerefentry><refentrytitle>perlembed</></citerefentry> man page.
-    </para>
+   <para>
+    While <application>PL/Perl</> functions run in a separate Perl
+    interpreter for each SQL role, all <application>PL/PerlU</> functions
+    executed in a given session run in a single Perl interpreter (which is
+    not any of the ones used for <application>PL/Perl</> functions).
+    This allows <application>PL/PerlU</> functions to share data freely,
+    but no communication can occur between <application>PL/Perl</> and
+    <application>PL/PerlU</> functions.
+   </para>
+  </note>
+
+  <note>
+   <para>
+    Perl cannot support multiple interpreters within one process unless
+    it was built with the appropriate flags, namely either
+    <literal>usemultiplicity</> or <literal>useithreads</>.
+    (<literal>usemultiplicity</> is preferred unless you actually need
+    to use threads.  For more details, see the
+    <citerefentry><refentrytitle>perlembed</></citerefentry> man page.)
+    If <application>PL/Perl</> is used with a copy of Perl that was not built
+    this way, then it is only possible to have one Perl interpreter per
+    session, and so any one session can only execute either
+    <application>PL/PerlU</> functions, or <application>PL/Perl</> functions
+    that are all called by the same SQL role.
+   </para>
   </note>
 
  </sect1>
@@ -1137,12 +1156,13 @@ CREATE TRIGGER test_valid_id_trig
       </indexterm>
       <listitem>
        <para>
-       Specifies Perl code to be executed when a Perl interpreter is first initialized
-       and before it is specialized for use by <literal>plperl</> or <literal>plperlu</>.
-       The SPI functions are not available when this code is executed.
-       If the code fails with an error it will abort the initialization of the interpreter
-       and propagate out to the calling query, causing the current transaction
-       or subtransaction to be aborted.
+        Specifies Perl code to be executed when a Perl interpreter is first
+        initialized, before it is specialized for use by <literal>plperl</> or
+        <literal>plperlu</>.
+        The SPI functions are not available when this code is executed.
+        If the code fails with an error it will abort the initialization of
+        the interpreter and propagate out to the calling query, causing the
+        current transaction or subtransaction to be aborted.
        </para>
        <para>
        The Perl code is limited to a single string. Longer code can be placed
@@ -1162,9 +1182,21 @@ DO 'elog(WARNING, join ", ", sort keys %INC)' language plperl;
 </programlisting>
        </para>
        <para>
-       Initialization will happen in the postmaster if the plperl library is included
-       in <literal>shared_preload_libraries</> (see <xref linkend="guc-shared-preload-libraries">),
-       in which case extra consideration should be given to the risk of destabilizing the postmaster.
+        Initialization will happen in the postmaster if the plperl library is
+        included in <xref linkend="guc-shared-preload-libraries">, in which
+        case extra consideration should be given to the risk of destabilizing
+        the postmaster.  The principal reason for making use of this feature
+        is that Perl modules loaded by <literal>plperl.on_init</> need be
+        loaded only at postmaster start, and will be instantly available
+        without loading overhead in individual database sessions.  However,
+        keep in mind that the overhead is avoided only for the first Perl
+        interpreter used by a database session &mdash; either PL/PerlU, or
+        PL/Perl for the first SQL role that calls a PL/Perl function.  Any
+        additional Perl interpreters created in a database session will have
+        to execute <literal>plperl.on_init</> afresh.  Also, on Windows there
+        will be no savings whatsoever from preloading, since the Perl
+        interpreter created in the postmaster process does not propagate to
+        child processes.
        </para>
        <para>
        This parameter can only be set in the postgresql.conf file or on the server command line.
@@ -1183,41 +1215,30 @@ DO 'elog(WARNING, join ", ", sort keys %INC)' language plperl;
       </indexterm>
       <listitem>
        <para>
-       These parameters specify Perl code to be executed when the
-       <literal>plperl</>, or <literal>plperlu</> language is first used in a
-       session.  Changes to these parameters after the corresponding language
-       has been used will have no effect.
-       The SPI functions are not available when this code is executed.
-       Only superusers can change these settings.
-       The Perl code in <literal>plperl.on_plperl_init</> can only perform trusted operations.
-       </para>
-       <para>
-       The effect of setting these parameters is very similar to executing a
-       <literal>DO</> command with the Perl code before any other use of the
-       language.  The parameters are useful when you want to execute the Perl
-       code automatically on every connection, or when a connection is not
-       interactive. The parameters can be used by non-superusers by having a
-       superuser execute an <literal>ALTER USER ... SET ...</> command.
-       For example:
-<programlisting>
-ALTER USER joe SET plperl.on_plperl_init = '$_SHARED{debug} = 1';
-</programlisting>
+        These parameters specify Perl code to be executed when a Perl
+        interpreter is specialized for <literal>plperl</> or
+        <literal>plperlu</> respectively.  This will happen when a PL/Perl or
+        PL/PerlU function is first executed in a database session, or when
+        an additional interpreter has to be created because the other language
+        is called or a PL/Perl function is called by a new SQL role.  This
+        follows any initialization done by <literal>plperl.on_init</>.
+        The SPI functions are not available when this code is executed.
+        The Perl code in <literal>plperl.on_plperl_init</> is executed after
+        <quote>locking down</> the interpreter, and thus it can only perform
+        trusted operations.
        </para>
        <para>
-       If the code fails with an error it will abort the initialization and
-       propagate out to the calling query, causing the current transaction or
-       subtransaction to be aborted. Any changes within Perl won't be undone.
-       If the language is used again the initialization will be repeated.
+        If the code fails with an error it will abort the initialization and
+        propagate out to the calling query, causing the current transaction or
+        subtransaction to be aborted.  Any actions already done within Perl
+        won't be undone; however, that interpreter won't be used again.
+        If the language is used again the initialization will be attempted
+        again within a fresh Perl interpreter.
        </para>
        <para>
-       The difference between these two settings and the 
-       <literal>plperl.on_init</> setting is that these can be used for
-       settings specific to the trusted or untrusted language variant, such
-       as setting values in the <varname>%_SHARED</> variable. By contrast,
-       <literal>plperl.on_init</> is more useful for doing things like
-       setting the library search path for <productname>Perl</> or 
-       loading Perl modules that don't interact directly with 
-       <productname>PostgreSQL</>.
+        Only superusers can change these settings.  Although these settings
+        can be changed within a session, such changes will not affect Perl
+        interpreters that have already been used to execute functions.
        </para>
       </listitem>
      </varlistentry>
@@ -1229,8 +1250,9 @@ ALTER USER joe SET plperl.on_plperl_init = '$_SHARED{debug} = 1';
       </indexterm>
       <listitem>
        <para>
-       When set true subsequent compilations of PL/Perl functions have the <literal>strict</> pragma enabled.
-       This parameter does not affect functions already compiled in the current session.
+        When set true subsequent compilations of PL/Perl functions will have
+        the <literal>strict</> pragma enabled.  This parameter does not affect
+        functions already compiled in the current session.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml
index eb29a8fd036ef45861d7e82cf97dff18ceec3164..326c757e432b20a3168ca5f4278787113cc8bb2b 100644
--- a/doc/src/sgml/pltcl.sgml
+++ b/doc/src/sgml/pltcl.sgml
@@ -214,14 +214,36 @@ $$ LANGUAGE pltcl;
      Sometimes it
      is useful to have some global data that is held between two
      calls to a function or is shared between different functions.
-     This is easily done since
-     all PL/Tcl functions executed in one session share the same
-     safe Tcl interpreter.  So, any global Tcl variable is accessible to
-     all PL/Tcl function calls and will persist for the duration of the
-     SQL session.  (Note that <application>PL/TclU</> functions likewise share
-     global data, but they are in a different Tcl interpreter and cannot
-     communicate with PL/Tcl functions.)
+     This is easily done in PL/Tcl, but there are some restrictions that
+     must be understood.
     </para>
+
+    <para>
+     For security reasons, PL/Tcl executes functions called by any one SQL
+     role in a separate Tcl interpreter for that role.  This prevents
+     accidental or malicious interference by one user with the behavior of
+     another user's PL/Tcl functions.  Each such interpreter will have its own
+     values for any <quote>global</> Tcl variables.  Thus, two PL/Tcl
+     functions will share the same global variables if and only if they are
+     executed by the same SQL role.  In an application wherein a single
+     session executes code under multiple SQL roles (via <literal>SECURITY
+     DEFINER</> functions, use of <command>SET ROLE</>, etc) you may need to
+     take explicit steps to ensure that PL/Tcl functions can share data.  To
+     do that, make sure that functions that should communicate are owned by
+     the same user, and mark them <literal>SECURITY DEFINER</>.  You must of
+     course take care that such functions can't be used to do anything
+     unintended.
+    </para>
+
+    <para>
+     All PL/TclU functions used in a session execute in the same Tcl
+     interpreter, which of course is distinct from the interpreter(s)
+     used for PL/Tcl functions.  So global data is automatically shared
+     between PL/TclU functions.  This is not considered a security risk
+     because all PL/TclU functions execute at the same trust level,
+     namely that of a database superuser.
+    </para>
+
     <para>
      To help protect PL/Tcl functions from unintentionally interfering
      with each other, a global
@@ -231,7 +253,9 @@ $$ LANGUAGE pltcl;
      <literal>GD</> be used
      for persistent private data of a function.  Use regular Tcl global
      variables only for values that you specifically intend to be shared among
-     multiple functions.
+     multiple functions.  (Note that the <literal>GD</> arrays are only
+     global within a particular interpreter, so they do not bypass the
+     security restrictions mentioned above.)
     </para>
 
     <para>
@@ -691,8 +715,8 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
         exists, the module <literal>unknown</> is fetched from the table
         and loaded into the Tcl interpreter immediately before the first
         execution of a PL/Tcl function in a database session.  (This
-        happens separately for PL/Tcl and PL/TclU, if both are used,
-        because separate interpreters are used for the two languages.)
+        happens separately for each Tcl interpreter, if more than one is
+        used in a session; see <xref linkend="pltcl-global">.)
        </para>
        <para>
         While the <literal>unknown</> module could actually contain any
diff --git a/doc/src/sgml/release-7.4.sgml b/doc/src/sgml/release-7.4.sgml
index 2c52be70064caa941cedd574a9b45c33d85f28a9..226275bf3201265c8452a2d8bf7f2258a0e13ace 100644
--- a/doc/src/sgml/release-7.4.sgml
+++ b/doc/src/sgml/release-7.4.sgml
@@ -37,6 +37,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-8.0.sgml b/doc/src/sgml/release-8.0.sgml
index ae2b3c04cf7989450a5ec0ed28f7c987da33eb37..f35cb61f419b81251dd7b63f789ec64ae25de00e 100644
--- a/doc/src/sgml/release-8.0.sgml
+++ b/doc/src/sgml/release-8.0.sgml
@@ -37,6 +37,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-8.1.sgml b/doc/src/sgml/release-8.1.sgml
index 37e3751c0e1d8610d0e3869cdb5b2cb041ccd8c7..34b3022d05d5d6efd7ab9e29cae3af034b7ac8a5 100644
--- a/doc/src/sgml/release-8.1.sgml
+++ b/doc/src/sgml/release-8.1.sgml
@@ -37,6 +37,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-8.2.sgml b/doc/src/sgml/release-8.2.sgml
index f4b0056f6f8a135dde7713b450eecac5ecfedef9..89431c31f4f798dc8a7da01a906aa372f6d7830d 100644
--- a/doc/src/sgml/release-8.2.sgml
+++ b/doc/src/sgml/release-8.2.sgml
@@ -31,6 +31,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-8.3.sgml b/doc/src/sgml/release-8.3.sgml
index eac868f3f15a6d4064a02c6313dcab3ffc792149..0f4d44f9c5a17c50fd7f49a57760018ca692d0d5 100644
--- a/doc/src/sgml/release-8.3.sgml
+++ b/doc/src/sgml/release-8.3.sgml
@@ -31,6 +31,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-8.4.sgml b/doc/src/sgml/release-8.4.sgml
index 9ff4610ccfaec4fa590cea48ce16ea5836b620c4..f426023896eb06139e42ea8aebc07d7b28b7f24d 100644
--- a/doc/src/sgml/release-8.4.sgml
+++ b/doc/src/sgml/release-8.4.sgml
@@ -31,6 +31,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Prevent possible crashes in <function>pg_get_expr()</> by disallowing
diff --git a/doc/src/sgml/release-9.0.sgml b/doc/src/sgml/release-9.0.sgml
index 4d2fef797e82666914c198702bad1ab9e5f29836..67bfa558d7fb874825388021dc02b8684ec7c81f 100644
--- a/doc/src/sgml/release-9.0.sgml
+++ b/doc/src/sgml/release-9.0.sgml
@@ -29,6 +29,43 @@
 
    <itemizedlist>
 
+    <listitem>
+     <para>
+      Use a separate interpreter for each calling SQL userid in PL/Perl and
+      PL/Tcl (Tom Lane)
+     </para>
+
+     <para>
+      This change prevents security problems that can be caused by subverting
+      Perl or Tcl code that will be executed later in the same session under
+      another SQL user identity (for example, within a <literal>SECURITY
+      DEFINER</> function).  Most scripting languages offer numerous ways that
+      that might be done, such as redefining standard functions or operators
+      called by the target function.  Without this change, any SQL user with
+      Perl or Tcl language usage rights can do essentially anything with the
+      SQL privileges of the target function's owner.
+     </para>
+
+     <para>
+      The cost of this change is that intentional communication among Perl
+      and Tcl functions becomes more difficult.  To provide an escape hatch,
+      PL/PerlU and PL/TclU functions continue to use only one interpreter
+      per session.  This is not considered a security issue since all such
+      functions execute at the trust level of a database superuser already.
+     </para>
+
+     <para>
+      It is likely that third-party procedural languages that claim to offer
+      trusted execution have similar security issues.  We advise contacting
+      the authors of any PL you are depending on for security-critical
+      purposes.
+     </para>
+
+     <para>
+      Our thanks to Tim Bunce for pointing out this issue (CVE-2010-3433).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       Improve <function>pg_get_expr()</> security fix so that the function
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index cfad4878aa3393bb918802e28308ea64cd12e314..b4ced1ce8d45b93f5641bf4360c4053492527631 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -49,8 +49,45 @@
 /* defines PLPERL_SET_OPMASK */
 #include "plperl_opmask.h"
 
+EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
+EXTERN_C void boot_PostgreSQL__InServer__Util(pTHX_ CV *cv);
+EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
+
 PG_MODULE_MAGIC;
 
+
+/**********************************************************************
+ * Information associated with a Perl interpreter.  We have one interpreter
+ * that is used for all plperlu (untrusted) functions.  For plperl (trusted)
+ * functions, there is a separate interpreter for each effective SQL userid.
+ * (This is needed to ensure that an unprivileged user can't inject Perl code
+ * that'll be executed with the privileges of some other SQL user.)
+ *
+ * The plperl_interp_desc structs are kept in a Postgres hash table indexed
+ * by userid OID, with OID 0 used for the single untrusted interpreter.
+ *
+ * We start out by creating a "held" interpreter, which we initialize
+ * only as far as we can do without deciding if it will be trusted or
+ * untrusted.  Later, when we first need to run a plperl or plperlu
+ * function, we complete the initialization appropriately and move the
+ * PerlInterpreter pointer into the plperl_interp_hash hashtable.  If after
+ * that we need more interpreters, we create them as needed if we can, or
+ * fail if the Perl build doesn't support multiple interpreters.
+ *
+ * The reason for all the dancing about with a held interpreter is to make
+ * it possible for people to preload a lot of Perl code at postmaster startup
+ * (using plperl.on_init) and then use that code in backends.  Of course this
+ * will only work for the first interpreter created in any backend, but it's
+ * still useful with that restriction.
+ **********************************************************************/
+typedef struct plperl_interp_desc
+{
+	Oid			user_id;				/* Hash key (must be first!) */
+	PerlInterpreter *interp;			/* The interpreter */
+	HTAB	   *query_hash;				/* plperl_query_entry structs */
+} plperl_interp_desc;
+
+
 /**********************************************************************
  * The information we cache about loaded procedures
  **********************************************************************/
@@ -59,6 +96,7 @@ typedef struct plperl_proc_desc
 	char	   *proname;		/* user name of procedure */
 	TransactionId fn_xmin;
 	ItemPointerData fn_tid;
+	plperl_interp_desc *interp;	/* interpreter it's created in */
 	bool		fn_readonly;
 	bool		lanpltrusted;
 	bool		fn_retistuple;	/* true, if function returns tuple */
@@ -73,14 +111,35 @@ typedef struct plperl_proc_desc
 	SV		   *reference;
 } plperl_proc_desc;
 
-/* hash table entry for proc desc  */
+/**********************************************************************
+ * For speedy lookup, we maintain a hash table mapping from
+ * function OID + trigger flag + user OID to plperl_proc_desc pointers.
+ * The reason the plperl_proc_desc struct isn't directly part of the hash
+ * entry is to simplify recovery from errors during compile_plperl_function.
+ *
+ * Note: if the same function is called by multiple userIDs within a session,
+ * there will be a separate plperl_proc_desc entry for each userID in the case
+ * of plperl functions, but only one entry for plperlu functions, because we
+ * set user_id = 0 for that case.  If the user redeclares the same function
+ * from plperl to plperlu or vice versa, there might be multiple
+ * plperl_proc_ptr entries in the hashtable, but only one is valid.
+ **********************************************************************/
+typedef struct plperl_proc_key
+{
+	Oid			proc_id;				/* Function OID */
+	/*
+	 * is_trigger is really a bool, but declare as Oid to ensure this struct
+	 * contains no padding
+	 */
+	Oid			is_trigger;				/* is it a trigger function? */
+	Oid			user_id;				/* User calling the function, or 0 */
+} plperl_proc_key;
 
-typedef struct plperl_proc_entry
+typedef struct plperl_proc_ptr
 {
-	char		proc_name[NAMEDATALEN]; /* internal name, eg
-										 * __PLPerl_proc_39987 */
-	plperl_proc_desc *proc_data;
-} plperl_proc_entry;
+	plperl_proc_key proc_key;			/* Hash key (must be first!) */
+	plperl_proc_desc *proc_ptr;
+} plperl_proc_ptr;
 
 /*
  * The information we cache for the duration of a single call to a
@@ -101,7 +160,7 @@ typedef struct plperl_call_data
  **********************************************************************/
 typedef struct plperl_query_desc
 {
-	char		qname[20];
+	char		qname[24];
 	void	   *plan;
 	int			nargs;
 	Oid		   *argtypes;
@@ -121,33 +180,21 @@ typedef struct plperl_query_entry
  * Global data
  **********************************************************************/
 
-typedef enum
-{
-	INTERP_NONE,
-	INTERP_HELD,
-	INTERP_TRUSTED,
-	INTERP_UNTRUSTED,
-	INTERP_BOTH
-} InterpState;
-
-static InterpState interp_state = INTERP_NONE;
-
-static PerlInterpreter *plperl_trusted_interp = NULL;
-static PerlInterpreter *plperl_untrusted_interp = NULL;
-static PerlInterpreter *plperl_held_interp = NULL;
-static OP  *(*pp_require_orig) (pTHX) = NULL;
-static OP  *pp_require_safe(pTHX);
-static bool trusted_context;
+static HTAB *plperl_interp_hash = NULL;
 static HTAB *plperl_proc_hash = NULL;
-static HTAB *plperl_query_hash = NULL;
+static plperl_interp_desc *plperl_active_interp = NULL;
+/* If we have an unassigned "held" interpreter, it's stored here */
+static PerlInterpreter *plperl_held_interp = NULL;
 
+/* GUC variables */
 static bool plperl_use_strict = false;
 static char *plperl_on_init = NULL;
 static char *plperl_on_plperl_init = NULL;
 static char *plperl_on_plperlu_init = NULL;
+
 static bool plperl_ending = false;
+static OP  *(*pp_require_orig) (pTHX) = NULL;
 static char plperl_opmask[MAXO];
-static void set_interp_require(void);
 
 /* this is saved and restored by plperl_call_handler */
 static plperl_call_data *current_call_data = NULL;
@@ -163,6 +210,7 @@ void		_PG_init(void);
 static PerlInterpreter *plperl_init_interp(void);
 static void plperl_destroy_interp(PerlInterpreter **);
 static void plperl_fini(int code, Datum arg);
+static void set_interp_require(bool trusted);
 
 static Datum plperl_func_handler(PG_FUNCTION_ARGS);
 static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
@@ -184,7 +232,7 @@ static void plperl_exec_callback(void *arg);
 static void plperl_inline_callback(void *arg);
 static char *strip_trailing_ws(const char *msg);
 static OP  *pp_require_safe(pTHX);
-static int	restore_context(bool);
+static void activate_interpreter(plperl_interp_desc *interp_desc);
 
 #ifdef WIN32
 static char *setlocale_perl(int category, char *locale);
@@ -251,8 +299,14 @@ _PG_init(void)
 	if (inited)
 		return;
 
+	/*
+	 * Support localized messages.
+	 */
 	pg_bindtextdomain(TEXTDOMAIN);
 
+	/*
+	 * Initialize plperl's GUCs.
+	 */
 	DefineCustomBoolVariable("plperl.use_strict",
 							 gettext_noop("If true, trusted and untrusted Perl code will be compiled in strict mode."),
 							 NULL,
@@ -261,6 +315,12 @@ _PG_init(void)
 							 PGC_USERSET, 0,
 							 NULL, NULL);
 
+	/*
+	 * plperl.on_init is marked PGC_SIGHUP to support the idea that it might
+	 * be executed in the postmaster (if plperl is loaded into the postmaster
+	 * via shared_preload_libraries).  This isn't really right either way,
+	 * though.
+	 */
 	DefineCustomStringVariable("plperl.on_init",
 							   gettext_noop("Perl initialization code to execute when a Perl interpreter is initialized."),
 							   NULL,
@@ -270,13 +330,18 @@ _PG_init(void)
 							   NULL, NULL);
 
 	/*
-	 * plperl.on_plperl_init is currently PGC_SUSET to avoid issues whereby a
-	 * user who doesn't have USAGE privileges on the plperl language could
-	 * possibly use SET plperl.on_plperl_init='...' to influence the behaviour
-	 * of any existing plperl function that they can EXECUTE (which may be
-	 * security definer). Set
+	 * plperl.on_plperl_init is marked PGC_SUSET to avoid issues whereby a
+	 * user who might not even have USAGE privilege on the plperl language
+	 * could nonetheless use SET plperl.on_plperl_init='...' to influence the
+	 * behaviour of any existing plperl function that they can execute (which
+	 * might be SECURITY DEFINER, leading to a privilege escalation).  See
 	 * http://archives.postgresql.org/pgsql-hackers/2010-02/msg00281.php and
 	 * the overall thread.
+	 *
+	 * Note that because plperl.use_strict is USERSET, a nefarious user could
+	 * set it to be applied against other people's functions.  This is judged
+	 * OK since the worst result would be an error.  Your code oughta pass
+	 * use_strict anyway ;-)
 	 */
 	DefineCustomStringVariable("plperl.on_plperl_init",
 							   gettext_noop("Perl initialization code to execute once when plperl is first used."),
@@ -296,35 +361,45 @@ _PG_init(void)
 
 	EmitWarningsOnPlaceholders("plperl");
 
-	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
-
-	hash_ctl.keysize = NAMEDATALEN;
-	hash_ctl.entrysize = sizeof(plperl_proc_entry);
-
-	plperl_proc_hash = hash_create("PLPerl Procedures",
+	/*
+	 * Create hash tables.
+	 */
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(Oid);
+	hash_ctl.entrysize = sizeof(plperl_interp_desc);
+	hash_ctl.hash = oid_hash;
+	plperl_interp_hash = hash_create("PL/Perl interpreters",
+									 8,
+									 &hash_ctl,
+									 HASH_ELEM | HASH_FUNCTION);
+
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(plperl_proc_key);
+	hash_ctl.entrysize = sizeof(plperl_proc_ptr);
+	hash_ctl.hash = tag_hash;
+	plperl_proc_hash = hash_create("PL/Perl procedures",
 								   32,
 								   &hash_ctl,
-								   HASH_ELEM);
-
-	hash_ctl.entrysize = sizeof(plperl_query_entry);
-	plperl_query_hash = hash_create("PLPerl Queries",
-									32,
-									&hash_ctl,
-									HASH_ELEM);
+								   HASH_ELEM | HASH_FUNCTION);
 
+	/*
+	 * Save the default opmask.
+	 */
 	PLPERL_SET_OPMASK(plperl_opmask);
 
+	/*
+	 * Create the first Perl interpreter, but only partially initialize it.
+	 */
 	plperl_held_interp = plperl_init_interp();
-	interp_state = INTERP_HELD;
 
 	inited = true;
 }
 
 
 static void
-set_interp_require(void)
+set_interp_require(bool trusted)
 {
-	if (trusted_context)
+	if (trusted)
 	{
 		PL_ppaddr[OP_REQUIRE] = pp_require_safe;
 		PL_ppaddr[OP_DOFILE] = pp_require_safe;
@@ -343,6 +418,9 @@ set_interp_require(void)
 static void
 plperl_fini(int code, Datum arg)
 {
+	HASH_SEQ_STATUS hash_seq;
+	plperl_interp_desc *interp_desc;
+
 	elog(DEBUG3, "plperl_fini");
 
 	/*
@@ -360,91 +438,129 @@ plperl_fini(int code, Datum arg)
 		return;
 	}
 
-	plperl_destroy_interp(&plperl_trusted_interp);
-	plperl_destroy_interp(&plperl_untrusted_interp);
+	/* Zap the "held" interpreter, if we still have it */
 	plperl_destroy_interp(&plperl_held_interp);
 
+	/* Zap any fully-initialized interpreters */
+	hash_seq_init(&hash_seq, plperl_interp_hash);
+	while ((interp_desc = hash_seq_search(&hash_seq)) != NULL)
+	{
+		if (interp_desc->interp)
+		{
+			activate_interpreter(interp_desc);
+			plperl_destroy_interp(&interp_desc->interp);
+		}
+	}
+
 	elog(DEBUG3, "plperl_fini: done");
 }
 
 
-/********************************************************************
- *
- * We start out by creating a "held" interpreter that we can use in
- * trusted or untrusted mode (but not both) as the need arises. Later, we
- * assign that interpreter if it is available to either the trusted or
- * untrusted interpreter. If it has already been assigned, and we need to
- * create the other interpreter, we do that if we can, or error out.
+/*
+ * Select and activate an appropriate Perl interpreter.
  */
-
-
 static void
 select_perl_context(bool trusted)
 {
-	EXTERN_C void boot_PostgreSQL__InServer__SPI(pTHX_ CV *cv);
+	Oid			user_id;
+	plperl_interp_desc *interp_desc;
+	bool		found;
+	PerlInterpreter *interp = NULL;
+
+	/* Find or create the interpreter hashtable entry for this userid */
+	if (trusted)
+		user_id = GetUserId();
+	else
+		user_id = InvalidOid;
+
+	interp_desc = hash_search(plperl_interp_hash, &user_id,
+							  HASH_ENTER,
+							  &found);
+	if (!found)
+	{
+		/* Initialize newly-created hashtable entry */
+		interp_desc->interp = NULL;
+		interp_desc->query_hash = NULL;
+	}
+
+	/* Make sure we have a query_hash for this interpreter */
+	if (interp_desc->query_hash == NULL)
+	{
+		HASHCTL		hash_ctl;
+
+		memset(&hash_ctl, 0, sizeof(hash_ctl));
+		hash_ctl.keysize = NAMEDATALEN;
+		hash_ctl.entrysize = sizeof(plperl_query_entry);
+		interp_desc->query_hash = hash_create("PL/Perl queries",
+											  32,
+											  &hash_ctl,
+											  HASH_ELEM);
+	}
 
 	/*
-	 * handle simple cases
+	 * Quick exit if already have an interpreter
 	 */
-	if (restore_context(trusted))
+	if (interp_desc->interp)
+	{
+		activate_interpreter(interp_desc);
 		return;
+	}
 
 	/*
 	 * adopt held interp if free, else create new one if possible
 	 */
-	if (interp_state == INTERP_HELD)
+	if (plperl_held_interp != NULL)
 	{
 		/* first actual use of a perl interpreter */
+		interp = plperl_held_interp;
+
+		/*
+		 * Reset the plperl_held_interp pointer first; if we fail during init
+		 * we don't want to try again with the partially-initialized interp.
+		 */
+		plperl_held_interp = NULL;
 
 		if (trusted)
-		{
 			plperl_trusted_init();
-			plperl_trusted_interp = plperl_held_interp;
-			interp_state = INTERP_TRUSTED;
-		}
 		else
-		{
 			plperl_untrusted_init();
-			plperl_untrusted_interp = plperl_held_interp;
-			interp_state = INTERP_UNTRUSTED;
-		}
 
 		/* successfully initialized, so arrange for cleanup */
 		on_proc_exit(plperl_fini, 0);
-
 	}
 	else
 	{
 #ifdef MULTIPLICITY
-		PerlInterpreter *plperl = plperl_init_interp();
+		/*
+		 * plperl_init_interp will change Perl's idea of the active
+		 * interpreter.  Reset plperl_active_interp temporarily, so that if we
+		 * hit an error partway through here, we'll make sure to switch back
+		 * to a non-broken interpreter before running any other Perl
+		 * functions.
+		 */
+		plperl_active_interp = NULL;
+
+		/* Now build the new interpreter */
+		interp = plperl_init_interp();
 
 		if (trusted)
-		{
 			plperl_trusted_init();
-			plperl_trusted_interp = plperl;
-		}
 		else
-		{
 			plperl_untrusted_init();
-			plperl_untrusted_interp = plperl;
-		}
-		interp_state = INTERP_BOTH;
 #else
 		elog(ERROR,
-			 "cannot allocate second Perl interpreter on this platform");
+			 "cannot allocate multiple Perl interpreters on this platform");
 #endif
 	}
-	plperl_held_interp = NULL;
-	trusted_context = trusted;
-	set_interp_require();
+
+	set_interp_require(trusted);
 
 	/*
 	 * Since the timing of first use of PL/Perl can't be predicted, any
 	 * database interaction during initialization is problematic. Including,
 	 * but not limited to, security definer issues. So we only enable access
 	 * to the database AFTER on_*_init code has run. See
-	 * http://archives.postgresql.org/message-id/20100127143318.GE713@timac.loc
-	 * al
+	 * http://archives.postgresql.org/pgsql-hackers/2010-01/msg02669.php
 	 */
 	newXS("PostgreSQL::InServer::SPI::bootstrap",
 		  boot_PostgreSQL__InServer__SPI, __FILE__);
@@ -454,35 +570,41 @@ select_perl_context(bool trusted)
 		ereport(ERROR,
 				(errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
 		errcontext("while executing PostgreSQL::InServer::SPI::bootstrap")));
+
+	/* Fully initialized, so mark the hashtable entry valid */
+	interp_desc->interp = interp;
+
+	/* And mark this as the active interpreter */
+	plperl_active_interp = interp_desc;
 }
 
 /*
- * Restore previous interpreter selection, if two are active
+ * Make the specified interpreter the active one
+ *
+ * A call with NULL does nothing.  This is so that "restoring" to a previously
+ * null state of plperl_active_interp doesn't result in useless thrashing.
  */
-static int
-restore_context(bool trusted)
+static void
+activate_interpreter(plperl_interp_desc *interp_desc)
 {
-	if (interp_state == INTERP_BOTH ||
-		(trusted && interp_state == INTERP_TRUSTED) ||
-		(!trusted && interp_state == INTERP_UNTRUSTED))
+	if (interp_desc && plperl_active_interp != interp_desc)
 	{
-		if (trusted_context != trusted)
-		{
-			if (trusted)
-				PERL_SET_CONTEXT(plperl_trusted_interp);
-			else
-				PERL_SET_CONTEXT(plperl_untrusted_interp);
-
-			trusted_context = trusted;
-			set_interp_require();
-		}
-		return 1;				/* context restored */
+		Assert(interp_desc->interp);
+		PERL_SET_CONTEXT(interp_desc->interp);
+		/* trusted iff user_id isn't InvalidOid */
+		set_interp_require(OidIsValid(interp_desc->user_id));
+		plperl_active_interp = interp_desc;
 	}
-
-	return 0;					/* unable - appropriate interpreter not
-								 * available */
 }
 
+/*
+ * Create a new Perl interpreter.
+ *
+ * We initialize the interpreter as far as we can without knowing whether
+ * it will become a trusted or untrusted interpreter; in particular, the
+ * plperl.on_init code will get executed.  Later, either plperl_trusted_init
+ * or plperl_untrusted_init must be called to complete the initialization.
+ */
 static PerlInterpreter *
 plperl_init_interp(void)
 {
@@ -538,17 +660,17 @@ plperl_init_interp(void)
 	STMT_START { \
 		if (saved != NULL) { setlocale_perl(name, saved); pfree(saved); } \
 	} STMT_END
-#endif
+#endif /* WIN32 */
 
-	if (plperl_on_init)
+	if (plperl_on_init && *plperl_on_init)
 	{
 		embedding[nargs++] = "-e";
 		embedding[nargs++] = plperl_on_init;
 	}
 
-	/****
+	/*
 	 * The perl API docs state that PERL_SYS_INIT3 should be called before
-	 * allocating interprters. Unfortunately, on some platforms this fails
+	 * allocating interpreters. Unfortunately, on some platforms this fails
 	 * in the Perl_do_taint() routine, which is called when the platform is
 	 * using the system's malloc() instead of perl's own. Other platforms,
 	 * notably Windows, fail if PERL_SYS_INIT3 is not called. So we call it
@@ -655,6 +777,11 @@ pp_require_safe(pTHX)
 }
 
 
+/*
+ * Destroy one Perl interpreter ... actually we just run END blocks.
+ *
+ * Caller must have ensured this interpreter is the active one.
+ */
 static void
 plperl_destroy_interp(PerlInterpreter **interp)
 {
@@ -671,8 +798,6 @@ plperl_destroy_interp(PerlInterpreter **interp)
 		 * be used to perform manual cleanup.
 		 */
 
-		PERL_SET_CONTEXT(*interp);
-
 		/* Run END blocks - based on perl's perl_destruct() */
 		if (PL_exit_flags & PERL_EXIT_DESTRUCT_END)
 		{
@@ -692,7 +817,9 @@ plperl_destroy_interp(PerlInterpreter **interp)
 	}
 }
 
-
+/*
+ * Initialize the current Perl interpreter as a trusted interp
+ */
 static void
 plperl_trusted_init(void)
 {
@@ -770,9 +897,15 @@ plperl_trusted_init(void)
 }
 
 
+/*
+ * Initialize the current Perl interpreter as an untrusted interp
+ */
 static void
 plperl_untrusted_init(void)
 {
+	/*
+	 * Nothing to do except execute plperl.on_plperlu_init
+	 */
 	if (plperl_on_plperlu_init && *plperl_on_plperlu_init)
 	{
 		eval_pv(plperl_on_plperlu_init, FALSE);
@@ -1077,7 +1210,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
 {
 	Datum		retval;
 	plperl_call_data *save_call_data = current_call_data;
-	bool		oldcontext = trusted_context;
+	plperl_interp_desc *oldinterp = plperl_active_interp;
 
 	PG_TRY();
 	{
@@ -1089,13 +1222,13 @@ plperl_call_handler(PG_FUNCTION_ARGS)
 	PG_CATCH();
 	{
 		current_call_data = save_call_data;
-		restore_context(oldcontext);
+		activate_interpreter(oldinterp);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
 	current_call_data = save_call_data;
-	restore_context(oldcontext);
+	activate_interpreter(oldinterp);
 	return retval;
 }
 
@@ -1112,7 +1245,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 	FmgrInfo	flinfo;
 	plperl_proc_desc desc;
 	plperl_call_data *save_call_data = current_call_data;
-	bool		oldcontext = trusted_context;
+	plperl_interp_desc *oldinterp = plperl_active_interp;
 	ErrorContextCallback pl_error_context;
 
 	/* Set up a callback for error reporting */
@@ -1175,7 +1308,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		if (desc.reference)
 			SvREFCNT_dec(desc.reference);
 		current_call_data = save_call_data;
-		restore_context(oldcontext);
+		activate_interpreter(oldinterp);
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
@@ -1184,7 +1317,7 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
 		SvREFCNT_dec(desc.reference);
 
 	current_call_data = save_call_data;
-	restore_context(oldcontext);
+	activate_interpreter(oldinterp);
 
 	error_context_stack = pl_error_context.previous;
 
@@ -1336,8 +1469,6 @@ static void
 plperl_init_shared_libs(pTHX)
 {
 	char	   *file = __FILE__;
-	EXTERN_C void boot_DynaLoader(pTHX_ CV *cv);
-	EXTERN_C void boot_PostgreSQL__InServer__Util(pTHX_ CV *cv);
 
 	newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
 	newXS("PostgreSQL::InServer::Util::bootstrap",
@@ -1535,7 +1666,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
 							"cannot accept a set")));
 	}
 
-	select_perl_context(prodesc->lanpltrusted);
+	activate_interpreter(prodesc->interp);
 
 	perlret = plperl_call_perl_func(prodesc, fcinfo);
 
@@ -1682,7 +1813,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
 	pl_error_context.arg = prodesc->proname;
 	error_context_stack = &pl_error_context;
 
-	select_perl_context(prodesc->lanpltrusted);
+	activate_interpreter(prodesc->interp);
 
 	svTD = plperl_trigger_build_args(fcinfo);
 	perlret = plperl_call_perl_trigger_func(prodesc, fcinfo, svTD);
@@ -1762,17 +1893,54 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
 }
 
 
+static bool
+validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
+{
+	if (proc_ptr && proc_ptr->proc_ptr)
+	{
+		plperl_proc_desc *prodesc = proc_ptr->proc_ptr;
+		bool		uptodate;
+
+		/************************************************************
+		 * If it's present, must check whether it's still up to date.
+		 * This is needed because CREATE OR REPLACE FUNCTION can modify the
+		 * function's pg_proc entry without changing its OID.
+		 ************************************************************/
+		uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+					ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
+
+		if (uptodate)
+			return true;
+
+		/* Otherwise, unlink the obsoleted entry from the hashtable ... */
+		proc_ptr->proc_ptr = NULL;
+		/* ... and throw it away */
+		if (prodesc->reference)
+		{
+			plperl_interp_desc *oldinterp = plperl_active_interp;
+
+			activate_interpreter(prodesc->interp);
+			SvREFCNT_dec(prodesc->reference);
+			activate_interpreter(oldinterp);
+		}
+		free(prodesc->proname);
+		free(prodesc);
+	}
+
+	return false;
+}
+
+
 static plperl_proc_desc *
 compile_plperl_function(Oid fn_oid, bool is_trigger)
 {
 	HeapTuple	procTup;
 	Form_pg_proc procStruct;
-	char		internal_proname[NAMEDATALEN];
+	plperl_proc_key proc_key;
+	plperl_proc_ptr *proc_ptr;
 	plperl_proc_desc *prodesc = NULL;
 	int			i;
-	plperl_proc_entry *hash_entry;
-	bool		found;
-	bool		oldcontext = trusted_context;
+	plperl_interp_desc *oldinterp = plperl_active_interp;
 	ErrorContextCallback plperl_error_context;
 
 	/* We'll need the pg_proc tuple in any case... */
@@ -1787,48 +1955,24 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 	plperl_error_context.arg = NameStr(procStruct->proname);
 	error_context_stack = &plperl_error_context;
 
-	/************************************************************
-	 * Build our internal proc name from the function's Oid
-	 ************************************************************/
-	if (!is_trigger)
-		sprintf(internal_proname, "__PLPerl_proc_%u", fn_oid);
-	else
-		sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid);
+	/* Try to find function in plperl_proc_hash */
+	proc_key.proc_id = fn_oid;
+	proc_key.is_trigger = is_trigger;
+	proc_key.user_id = GetUserId();
 
-	/************************************************************
-	 * Lookup the internal proc name in the hashtable
-	 ************************************************************/
-	hash_entry = hash_search(plperl_proc_hash, internal_proname,
-							 HASH_FIND, NULL);
+	proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+						   HASH_FIND, NULL);
 
-	if (hash_entry)
+	if (validate_plperl_function(proc_ptr, procTup))
+		prodesc = proc_ptr->proc_ptr;
+	else
 	{
-		bool		uptodate;
-
-		prodesc = hash_entry->proc_data;
-
-		/************************************************************
-		 * If it's present, must check whether it's still up to date.
-		 * This is needed because CREATE OR REPLACE FUNCTION can modify the
-		 * function's pg_proc entry without changing its OID.
-		 ************************************************************/
-		uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
-					ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
-
-		if (!uptodate)
-		{
-			hash_search(plperl_proc_hash, internal_proname,
-						HASH_REMOVE, NULL);
-			if (prodesc->reference)
-			{
-				select_perl_context(prodesc->lanpltrusted);
-				SvREFCNT_dec(prodesc->reference);
-				restore_context(oldcontext);
-			}
-			free(prodesc->proname);
-			free(prodesc);
-			prodesc = NULL;
-		}
+		/* If not found or obsolete, maybe it's plperlu */
+		proc_key.user_id = InvalidOid;
+		proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+							   HASH_FIND, NULL);
+		if (validate_plperl_function(proc_ptr, procTup))
+			prodesc = proc_ptr->proc_ptr;
 	}
 
 	/************************************************************
@@ -1859,6 +2003,10 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 					 errmsg("out of memory")));
 		MemSet(prodesc, 0, sizeof(plperl_proc_desc));
 		prodesc->proname = strdup(NameStr(procStruct->proname));
+		if (prodesc->proname == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory")));
 		prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
 		prodesc->fn_tid = procTup->t_self;
 
@@ -1996,27 +2144,33 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 		proc_source = TextDatumGetCString(prosrcdatum);
 
 		/************************************************************
-		 * Create the procedure in the interpreter
+		 * Create the procedure in the appropriate interpreter
 		 ************************************************************/
 
 		select_perl_context(prodesc->lanpltrusted);
 
+		prodesc->interp = plperl_active_interp;
+
 		plperl_create_sub(prodesc, proc_source, fn_oid);
 
-		restore_context(oldcontext);
+		activate_interpreter(oldinterp);
 
 		pfree(proc_source);
 		if (!prodesc->reference)	/* can this happen? */
 		{
 			free(prodesc->proname);
 			free(prodesc);
-			elog(ERROR, "could not create internal procedure \"%s\"",
-				 internal_proname);
+			elog(ERROR, "could not create PL/Perl internal procedure");
 		}
 
-		hash_entry = hash_search(plperl_proc_hash, internal_proname,
-								 HASH_ENTER, &found);
-		hash_entry->proc_data = prodesc;
+		/************************************************************
+		 * OK, link the procedure into the correct hashtable entry
+		 ************************************************************/
+		proc_key.user_id = prodesc->lanpltrusted ? GetUserId() : InvalidOid;
+
+		proc_ptr = hash_search(plperl_proc_hash, &proc_key,
+							   HASH_ENTER, NULL);
+		proc_ptr->proc_ptr = prodesc;
 	}
 
 	/* restore previous error callback */
@@ -2636,7 +2790,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
 	 * the key to the caller.
 	 ************************************************************/
 
-	hash_entry = hash_search(plperl_query_hash, qdesc->qname,
+	hash_entry = hash_search(plperl_active_interp->query_hash, qdesc->qname,
 							 HASH_ENTER, &found);
 	hash_entry->query_data = qdesc;
 
@@ -2675,7 +2829,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
 		 * Fetch the saved plan descriptor, see if it's o.k.
 		 ************************************************************/
 
-		hash_entry = hash_search(plperl_query_hash, query,
+		hash_entry = hash_search(plperl_active_interp->query_hash, query,
 								 HASH_FIND, NULL);
 		if (hash_entry == NULL)
 			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2683,7 +2837,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
 		qdesc = hash_entry->query_data;
 
 		if (qdesc == NULL)
-			elog(ERROR, "spi_exec_prepared: panic - plperl_query_hash value vanished");
+			elog(ERROR, "spi_exec_prepared: panic - plperl query_hash value vanished");
 
 		if (qdesc->nargs != argc)
 			elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
@@ -2818,7 +2972,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
 		/************************************************************
 		 * Fetch the saved plan descriptor, see if it's o.k.
 		 ************************************************************/
-		hash_entry = hash_search(plperl_query_hash, query,
+		hash_entry = hash_search(plperl_active_interp->query_hash, query,
 								 HASH_FIND, NULL);
 		if (hash_entry == NULL)
 			elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2826,7 +2980,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
 		qdesc = hash_entry->query_data;
 
 		if (qdesc == NULL)
-			elog(ERROR, "spi_query_prepared: panic - plperl_query_hash value vanished");
+			elog(ERROR, "spi_query_prepared: panic - plperl query_hash value vanished");
 
 		if (qdesc->nargs != argc)
 			elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
@@ -2934,7 +3088,7 @@ plperl_spi_freeplan(char *query)
 
 	check_spi_usage_allowed();
 
-	hash_entry = hash_search(plperl_query_hash, query,
+	hash_entry = hash_search(plperl_active_interp->query_hash, query,
 							 HASH_FIND, NULL);
 	if (hash_entry == NULL)
 		elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
@@ -2942,13 +3096,13 @@ plperl_spi_freeplan(char *query)
 	qdesc = hash_entry->query_data;
 
 	if (qdesc == NULL)
-		elog(ERROR, "spi_exec_freeplan: panic - plperl_query_hash value vanished");
+		elog(ERROR, "spi_exec_freeplan: panic - plperl query_hash value vanished");
 
 	/*
 	 * free all memory before SPI_freeplan, so if it dies, nothing will be
 	 * left over
 	 */
-	hash_search(plperl_query_hash, query,
+	hash_search(plperl_active_interp->query_hash, query,
 				HASH_REMOVE, NULL);
 
 	plan = qdesc->plan;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 8c94c826c994e024a275bb8fc6ab9d193b6a8c27..1c45751d8b398cdcf3383fdca459ac568995cf41 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -19,7 +19,6 @@
 #endif
 
 #include "access/xact.h"
-#include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
@@ -83,6 +82,25 @@ utf_e2u(unsigned char *src)
 
 PG_MODULE_MAGIC;
 
+
+/**********************************************************************
+ * Information associated with a Tcl interpreter.  We have one interpreter
+ * that is used for all pltclu (untrusted) functions.  For pltcl (trusted)
+ * functions, there is a separate interpreter for each effective SQL userid.
+ * (This is needed to ensure that an unprivileged user can't inject Tcl code
+ * that'll be executed with the privileges of some other SQL user.)
+ *
+ * The pltcl_interp_desc structs are kept in a Postgres hash table indexed
+ * by userid OID, with OID 0 used for the single untrusted interpreter.
+ **********************************************************************/
+typedef struct pltcl_interp_desc
+{
+	Oid			user_id;				/* Hash key (must be first!) */
+	Tcl_Interp *interp;					/* The interpreter */
+	Tcl_HashTable query_hash;			/* pltcl_query_desc structs */
+} pltcl_interp_desc;
+
+
 /**********************************************************************
  * The information we cache about loaded procedures
  **********************************************************************/
@@ -94,6 +112,7 @@ typedef struct pltcl_proc_desc
 	ItemPointerData fn_tid;
 	bool		fn_readonly;
 	bool		lanpltrusted;
+	pltcl_interp_desc *interp_desc;
 	FmgrInfo	result_in_func;
 	Oid			result_typioparam;
 	int			nargs;
@@ -116,20 +135,40 @@ typedef struct pltcl_query_desc
 } pltcl_query_desc;
 
 
+/**********************************************************************
+ * For speedy lookup, we maintain a hash table mapping from
+ * function OID + trigger OID + user OID to pltcl_proc_desc pointers.
+ * The reason the pltcl_proc_desc struct isn't directly part of the hash
+ * entry is to simplify recovery from errors during compile_pltcl_function.
+ *
+ * Note: if the same function is called by multiple userIDs within a session,
+ * there will be a separate pltcl_proc_desc entry for each userID in the case
+ * of pltcl functions, but only one entry for pltclu functions, because we
+ * set user_id = 0 for that case.
+ **********************************************************************/
+typedef struct pltcl_proc_key
+{
+	Oid			proc_id;				/* Function OID */
+	Oid			trig_id;				/* Trigger OID, or 0 if not trigger */
+	Oid			user_id;				/* User calling the function, or 0 */
+} pltcl_proc_key;
+
+typedef struct pltcl_proc_ptr
+{
+	pltcl_proc_key proc_key;			/* Hash key (must be first!) */
+	pltcl_proc_desc *proc_ptr;
+} pltcl_proc_ptr;
+
+
 /**********************************************************************
  * Global data
  **********************************************************************/
 static bool pltcl_pm_init_done = false;
-static bool pltcl_be_norm_init_done = false;
-static bool pltcl_be_safe_init_done = false;
 static Tcl_Interp *pltcl_hold_interp = NULL;
-static Tcl_Interp *pltcl_norm_interp = NULL;
-static Tcl_Interp *pltcl_safe_interp = NULL;
-static Tcl_HashTable *pltcl_proc_hash = NULL;
-static Tcl_HashTable *pltcl_norm_query_hash = NULL;
-static Tcl_HashTable *pltcl_safe_query_hash = NULL;
+static HTAB *pltcl_interp_htab = NULL;
+static HTAB *pltcl_proc_htab = NULL;
 
-/* these are saved and restored by pltcl_call_handler */
+/* these are saved and restored by pltcl_handler */
 static FunctionCallInfo pltcl_current_fcinfo = NULL;
 static pltcl_proc_desc *pltcl_current_prodesc = NULL;
 
@@ -140,17 +179,20 @@ Datum		pltcl_call_handler(PG_FUNCTION_ARGS);
 Datum		pltclu_call_handler(PG_FUNCTION_ARGS);
 void		_PG_init(void);
 
-static void pltcl_init_interp(Tcl_Interp *interp);
-static Tcl_Interp *pltcl_fetch_interp(bool pltrusted);
+static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
+static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
 static void pltcl_init_load_unknown(Tcl_Interp *interp);
 
-static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
+static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
+
+static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
 
-static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
+static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
 
 static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
 
-static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid);
+static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
+											   bool pltrusted);
 
 static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
 		   int argc, CONST84 char *argv[]);
@@ -264,10 +306,15 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
  * _PG_init()			- library load-time initialization
  *
  * DO NOT make this static nor change its name!
+ *
+ * The work done here must be safe to do in the postmaster process,
+ * in case the pltcl library is preloaded in the postmaster.
  */
 void
 _PG_init(void)
 {
+	HASHCTL		hash_ctl;
+
 	/* Be sure we do initialization only once (should be redundant now) */
 	if (pltcl_pm_init_done)
 		return;
@@ -304,47 +351,62 @@ _PG_init(void)
 	 * stdout and stderr on DeleteInterp
 	 ************************************************************/
 	if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
-		elog(ERROR, "could not create \"hold\" interpreter");
+		elog(ERROR, "could not create master Tcl interpreter");
 	if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
-		elog(ERROR, "could not initialize \"hold\" interpreter");
+		elog(ERROR, "could not initialize master Tcl interpreter");
 
 	/************************************************************
-	 * Create the two slave interpreters.  Note: Tcl automatically does
-	 * Tcl_Init on the normal slave, and it's not wanted for the safe slave.
+	 * Create the hash table for working interpreters
 	 ************************************************************/
-	if ((pltcl_norm_interp =
-		 Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
-		elog(ERROR, "could not create \"normal\" interpreter");
-	pltcl_init_interp(pltcl_norm_interp);
-
-	if ((pltcl_safe_interp =
-		 Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
-		elog(ERROR, "could not create \"safe\" interpreter");
-	pltcl_init_interp(pltcl_safe_interp);
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(Oid);
+	hash_ctl.entrysize = sizeof(pltcl_interp_desc);
+	hash_ctl.hash = oid_hash;
+	pltcl_interp_htab = hash_create("PL/Tcl interpreters",
+									8,
+									&hash_ctl,
+									HASH_ELEM | HASH_FUNCTION);
 
 	/************************************************************
-	 * Initialize the proc and query hash tables
+	 * Create the hash table for function lookup
 	 ************************************************************/
-	pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
-	pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
-	pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
-	Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
-	Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS);
-	Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS);
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(pltcl_proc_key);
+	hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
+	hash_ctl.hash = tag_hash;
+	pltcl_proc_htab = hash_create("PL/Tcl functions",
+								  100,
+								  &hash_ctl,
+								  HASH_ELEM | HASH_FUNCTION);
 
 	pltcl_pm_init_done = true;
 }
 
 /**********************************************************************
- * pltcl_init_interp() - initialize a Tcl interpreter
- *
- * The work done here must be safe to do in the postmaster process,
- * in case the pltcl library is preloaded in the postmaster.  Note
- * that this is applied separately to the "normal" and "safe" interpreters.
+ * pltcl_init_interp() - initialize a new Tcl interpreter
  **********************************************************************/
 static void
-pltcl_init_interp(Tcl_Interp *interp)
+pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
 {
+	Tcl_Interp *interp;
+	char		interpname[32];
+
+	/************************************************************
+	 * Create the Tcl interpreter as a slave of pltcl_hold_interp.
+	 * Note: Tcl automatically does Tcl_Init in the untrusted case,
+	 * and it's not wanted in the trusted case.
+	 ************************************************************/
+	snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
+	if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
+								  pltrusted ? 1 : 0)) == NULL)
+		elog(ERROR, "could not create slave Tcl interpreter");
+	interp_desc->interp = interp;
+
+	/************************************************************
+	 * Initialize the query hash table associated with interpreter
+	 ************************************************************/
+	Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
+
 	/************************************************************
 	 * Install the commands for SPI support in the interpreter
 	 ************************************************************/
@@ -365,43 +427,39 @@ pltcl_init_interp(Tcl_Interp *interp)
 					  pltcl_SPI_execute_plan, NULL, NULL);
 	Tcl_CreateCommand(interp, "spi_lastoid",
 					  pltcl_SPI_lastoid, NULL, NULL);
+
+	/************************************************************
+	 * Try to load the unknown procedure from pltcl_modules
+	 ************************************************************/
+	pltcl_init_load_unknown(interp);
 }
 
 /**********************************************************************
  * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
  *
  * This also takes care of any on-first-use initialization required.
- * The initialization work done here can't be done in the postmaster, and
- * hence is not safe to do at library load time, because it may invoke
- * arbitrary user-defined code.
  * Note: we assume caller has already connected to SPI.
  **********************************************************************/
-static Tcl_Interp *
+static pltcl_interp_desc *
 pltcl_fetch_interp(bool pltrusted)
 {
-	Tcl_Interp *interp;
+	Oid			user_id;
+	pltcl_interp_desc *interp_desc;
+	bool		found;
 
-	/* On first use, we try to load the unknown procedure from pltcl_modules */
+	/* Find or create the interpreter hashtable entry for this userid */
 	if (pltrusted)
-	{
-		interp = pltcl_safe_interp;
-		if (!pltcl_be_safe_init_done)
-		{
-			pltcl_init_load_unknown(interp);
-			pltcl_be_safe_init_done = true;
-		}
-	}
+		user_id = GetUserId();
 	else
-	{
-		interp = pltcl_norm_interp;
-		if (!pltcl_be_norm_init_done)
-		{
-			pltcl_init_load_unknown(interp);
-			pltcl_be_norm_init_done = true;
-		}
-	}
+		user_id = InvalidOid;
+
+	interp_desc = hash_search(pltcl_interp_htab, &user_id,
+							  HASH_ENTER,
+							  &found);
+	if (!found)
+		pltcl_init_interp(interp_desc, pltrusted);
 
-	return interp;
+	return interp_desc;
 }
 
 /**********************************************************************
@@ -532,6 +590,25 @@ PG_FUNCTION_INFO_V1(pltcl_call_handler);
 /* keep non-static */
 Datum
 pltcl_call_handler(PG_FUNCTION_ARGS)
+{
+	return pltcl_handler(fcinfo, true);
+}
+
+/*
+ * Alternative handler for unsafe functions
+ */
+PG_FUNCTION_INFO_V1(pltclu_call_handler);
+
+/* keep non-static */
+Datum
+pltclu_call_handler(PG_FUNCTION_ARGS)
+{
+	return pltcl_handler(fcinfo, false);
+}
+
+
+static Datum
+pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
 {
 	Datum		retval;
 	FunctionCallInfo save_fcinfo;
@@ -552,12 +629,12 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
 		if (CALLED_AS_TRIGGER(fcinfo))
 		{
 			pltcl_current_fcinfo = NULL;
-			retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
+			retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
 		}
 		else
 		{
 			pltcl_current_fcinfo = fcinfo;
-			retval = pltcl_func_handler(fcinfo);
+			retval = pltcl_func_handler(fcinfo, pltrusted);
 		}
 	}
 	PG_CATCH();
@@ -575,23 +652,11 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
 }
 
 
-/*
- * Alternative handler for unsafe functions
- */
-PG_FUNCTION_INFO_V1(pltclu_call_handler);
-
-/* keep non-static */
-Datum
-pltclu_call_handler(PG_FUNCTION_ARGS)
-{
-	return pltcl_call_handler(fcinfo);
-}
-
 /**********************************************************************
  * pltcl_func_handler()		- Handler for regular function calls
  **********************************************************************/
 static Datum
-pltcl_func_handler(PG_FUNCTION_ARGS)
+pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
 {
 	pltcl_proc_desc *prodesc;
 	Tcl_Interp *volatile interp;
@@ -606,11 +671,12 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 		elog(ERROR, "could not connect to SPI manager");
 
 	/* Find or compile the function */
-	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid);
+	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
+									 pltrusted);
 
 	pltcl_current_prodesc = prodesc;
 
-	interp = pltcl_fetch_interp(prodesc->lanpltrusted);
+	interp = prodesc->interp_desc->interp;
 
 	/************************************************************
 	 * Create the tcl command to call the internal
@@ -738,7 +804,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
  * pltcl_trigger_handler()	- Handler for trigger calls
  **********************************************************************/
 static HeapTuple
-pltcl_trigger_handler(PG_FUNCTION_ARGS)
+pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
 {
 	pltcl_proc_desc *prodesc;
 	Tcl_Interp *volatile interp;
@@ -764,11 +830,12 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 
 	/* Find or compile the function */
 	prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
-									 RelationGetRelid(trigdata->tg_relation));
+									 RelationGetRelid(trigdata->tg_relation),
+									 pltrusted);
 
 	pltcl_current_prodesc = prodesc;
 
-	interp = pltcl_fetch_interp(prodesc->lanpltrusted);
+	interp = prodesc->interp_desc->interp;
 
 	tupdesc = trigdata->tg_relation->rd_att;
 
@@ -1086,18 +1153,14 @@ throw_tcl_error(Tcl_Interp *interp, const char *proname)
  * (InvalidOid) when compiling a plain function.
  **********************************************************************/
 static pltcl_proc_desc *
-compile_pltcl_function(Oid fn_oid, Oid tgreloid)
+compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
 {
-	bool		is_trigger = OidIsValid(tgreloid);
 	HeapTuple	procTup;
 	Form_pg_proc procStruct;
-	char		internal_proname[128];
-	Tcl_HashEntry *hashent;
-	pltcl_proc_desc *prodesc = NULL;
-	Tcl_Interp *interp;
-	int			i;
-	int			hashnew;
-	int			tcl_rc;
+	pltcl_proc_key proc_key;
+	pltcl_proc_ptr *proc_ptr;
+	bool		found;
+	pltcl_proc_desc *prodesc;
 
 	/* We'll need the pg_proc tuple in any case... */
 	procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
@@ -1105,39 +1168,35 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		elog(ERROR, "cache lookup failed for function %u", fn_oid);
 	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
-	/************************************************************
-	 * Build our internal proc name from the functions Oid
-	 ************************************************************/
-	if (!is_trigger)
-		snprintf(internal_proname, sizeof(internal_proname),
-				 "__PLTcl_proc_%u", fn_oid);
-	else
-		snprintf(internal_proname, sizeof(internal_proname),
-				 "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid);
+	/* Try to find function in pltcl_proc_htab */
+	proc_key.proc_id = fn_oid;
+	proc_key.trig_id = tgreloid;
+	proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
 
-	/************************************************************
-	 * Lookup the internal proc name in the hashtable
-	 ************************************************************/
-	hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
+	proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
+						   HASH_ENTER,
+						   &found);
+	if (!found)
+		proc_ptr->proc_ptr = NULL;
+
+	prodesc = proc_ptr->proc_ptr;
 
 	/************************************************************
 	 * If it's present, must check whether it's still up to date.
 	 * This is needed because CREATE OR REPLACE FUNCTION can modify the
 	 * function's pg_proc entry without changing its OID.
 	 ************************************************************/
-	if (hashent != NULL)
+	if (prodesc != NULL)
 	{
 		bool		uptodate;
 
-		prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
-
 		uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
 					ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
 
 		if (!uptodate)
 		{
-			Tcl_DeleteHashEntry(hashent);
-			hashent = NULL;
+			proc_ptr->proc_ptr = NULL;
+			prodesc = NULL;
 		}
 	}
 
@@ -1149,11 +1208,11 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 	 *
 	 * Then we load the procedure into the Tcl interpreter.
 	 ************************************************************/
-	if (hashent == NULL)
+	if (prodesc == NULL)
 	{
-		HeapTuple	langTup;
+		bool		is_trigger = OidIsValid(tgreloid);
+		char		internal_proname[128];
 		HeapTuple	typeTup;
-		Form_pg_language langStruct;
 		Form_pg_type typeStruct;
 		Tcl_DString proc_internal_def;
 		Tcl_DString proc_internal_body;
@@ -1162,6 +1221,19 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		bool		isnull;
 		char	   *proc_source;
 		char		buf[32];
+		Tcl_Interp *interp;
+		int			i;
+		int			tcl_rc;
+
+		/************************************************************
+		 * Build our internal proc name from the functions Oid + trigger Oid
+		 ************************************************************/
+		if (!is_trigger)
+			snprintf(internal_proname, sizeof(internal_proname),
+					 "__PLTcl_proc_%u", fn_oid);
+		else
+			snprintf(internal_proname, sizeof(internal_proname),
+					 "__PLTcl_proc_%u_trigger_%u", fn_oid, tgreloid);
 
 		/************************************************************
 		 * Allocate a new procedure description block
@@ -1174,31 +1246,24 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
 		prodesc->user_proname = strdup(NameStr(procStruct->proname));
 		prodesc->internal_proname = strdup(internal_proname);
+		if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory")));
 		prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
 		prodesc->fn_tid = procTup->t_self;
 
 		/* Remember if function is STABLE/IMMUTABLE */
 		prodesc->fn_readonly =
 			(procStruct->provolatile != PROVOLATILE_VOLATILE);
+		/* And whether it is trusted */
+		prodesc->lanpltrusted = pltrusted;
 
 		/************************************************************
-		 * Lookup the pg_language tuple by Oid
+		 * Identify the interpreter to use for the function
 		 ************************************************************/
-		langTup = SearchSysCache1(LANGOID,
-								  ObjectIdGetDatum(procStruct->prolang));
-		if (!HeapTupleIsValid(langTup))
-		{
-			free(prodesc->user_proname);
-			free(prodesc->internal_proname);
-			free(prodesc);
-			elog(ERROR, "cache lookup failed for language %u",
-				 procStruct->prolang);
-		}
-		langStruct = (Form_pg_language) GETSTRUCT(langTup);
-		prodesc->lanpltrusted = langStruct->lanpltrusted;
-		ReleaseSysCache(langTup);
-
-		interp = pltcl_fetch_interp(prodesc->lanpltrusted);
+		prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
+		interp = prodesc->interp_desc->interp;
 
 		/************************************************************
 		 * Get the required information for input conversion of the
@@ -1404,11 +1469,12 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
 		}
 
 		/************************************************************
-		 * Add the proc description block to the hashtable
+		 * Add the proc description block to the hashtable.  Note we do not
+		 * attempt to free any previously existing prodesc block.  This is
+		 * annoying, but necessary since there could be active calls using
+		 * the old prodesc.
 		 ************************************************************/
-		hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
-									  prodesc->internal_proname, &hashnew);
-		Tcl_SetHashValue(hashent, (ClientData) prodesc);
+		proc_ptr->proc_ptr = prodesc;
 	}
 
 	ReleaseSysCache(procTup);
@@ -2064,10 +2130,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
 	 * Insert a hashtable entry for the plan and return
 	 * the key to the caller
 	 ************************************************************/
-	if (interp == pltcl_norm_interp)
-		query_hash = pltcl_norm_query_hash;
-	else
-		query_hash = pltcl_safe_query_hash;
+	query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
 
 	hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
 	Tcl_SetHashValue(hashent, (ClientData) qdesc);
@@ -2158,10 +2221,7 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
 		return TCL_ERROR;
 	}
 
-	if (interp == pltcl_norm_interp)
-		query_hash = pltcl_norm_query_hash;
-	else
-		query_hash = pltcl_safe_query_hash;
+	query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
 
 	hashent = Tcl_FindHashEntry(query_hash, argv[i]);
 	if (hashent == NULL)