Fix pltcl to update cached function def after
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 19 Oct 2001 02:43:46 +0000 (02:43 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 19 Oct 2001 02:43:46 +0000 (02:43 +0000)
CREATE OR REPLACE FUNCTION.

src/pl/tcl/pltcl.c

index af3af23cd0e3e1ac2578983da3c2ee0d36e6b28c..b929fea623d3a0cd101aee4cfc066f0ef04fe02e 100644 (file)
@@ -31,7 +31,7 @@
  *       ENHANCEMENTS, OR MODIFICATIONS.
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.44 2001/10/13 04:23:50 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.45 2001/10/19 02:43:46 tgl Exp $
  *
  **********************************************************************/
 
@@ -99,6 +99,8 @@ utf_e2u(unsigned char *src) {
 typedef struct pltcl_proc_desc
 {
        char       *proname;
+       TransactionId fn_xmin;
+       CommandId       fn_cmin;
        bool            lanpltrusted;
        FmgrInfo        result_in_func;
        Oid                     result_in_elem;
@@ -155,6 +157,8 @@ static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
 
 static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
 
+static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, bool is_trigger);
+
 static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
                   int argc, char *argv[]);
 static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
@@ -201,11 +205,6 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
 static void
 pltcl_init_all(void)
 {
-       Tcl_HashEntry *hashent;
-       Tcl_HashSearch hashsearch;
-       pltcl_proc_desc *prodesc;
-       pltcl_query_desc *querydesc;
-
        /************************************************************
         * Do initialization only once
         ************************************************************/
@@ -450,243 +449,22 @@ pltclu_call_handler(PG_FUNCTION_ARGS)
 static Datum
 pltcl_func_handler(PG_FUNCTION_ARGS)
 {
-       Tcl_Interp *interp;
-       int                     i;
-       char            internal_proname[512];
-       Tcl_HashEntry *hashent;
-       int                     hashnew;
-       pltcl_proc_desc *volatile prodesc;
+       pltcl_proc_desc *prodesc;
+       Tcl_Interp *volatile interp;
        Tcl_DString tcl_cmd;
        Tcl_DString list_tmp;
+       int                     i;
        int                     tcl_rc;
        Datum           retval;
        sigjmp_buf      save_restart;
 
-       /************************************************************
-        * Build our internal proc name from the functions Oid
-        ************************************************************/
-       sprintf(internal_proname, "__PLTcl_proc_%u", fcinfo->flinfo->fn_oid);
-
-       /************************************************************
-        * Lookup the internal proc name in the hashtable
-        ************************************************************/
-       hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
-       if (hashent == NULL)
-       {
-               /************************************************************
-                * If we haven't found it in the hashtable, we analyze
-                * the functions arguments and returntype and store
-                * the in-/out-functions in the prodesc block and create
-                * a new hashtable entry for it.
-                *
-                * Then we load the procedure into the safe interpreter.
-                ************************************************************/
-               HeapTuple       procTup;
-               HeapTuple       langTup;
-               HeapTuple       typeTup;
-               Form_pg_proc procStruct;
-               Form_pg_language langStruct;
-               Form_pg_type typeStruct;
-               Tcl_DString proc_internal_def;
-               Tcl_DString proc_internal_body;
-               char            proc_internal_args[4096];
-               char       *proc_source;
-               char            buf[512];
-
-               /************************************************************
-                * Allocate a new procedure description block
-                ************************************************************/
-               prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
-               prodesc->proname = malloc(strlen(internal_proname) + 1);
-               strcpy(prodesc->proname, internal_proname);
-
-               /************************************************************
-                * Lookup the pg_proc tuple by Oid
-                ************************************************************/
-               procTup = SearchSysCache(PROCOID,
-                                                                ObjectIdGetDatum(fcinfo->flinfo->fn_oid),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(procTup))
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cache lookup for proc %u failed",
-                                fcinfo->flinfo->fn_oid);
-               }
-               procStruct = (Form_pg_proc) GETSTRUCT(procTup);
-
-               /************************************************************
-                * Lookup the pg_language tuple by Oid
-                ************************************************************/
-               langTup = SearchSysCache(LANGOID,
-                                                                ObjectIdGetDatum(procStruct->prolang),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(langTup))
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cache lookup for language %u failed",
-                                procStruct->prolang);
-               }
-               langStruct = (Form_pg_language) GETSTRUCT(langTup);
-
-               prodesc->lanpltrusted = langStruct->lanpltrusted;
-               if (prodesc->lanpltrusted)
-                       interp = pltcl_safe_interp;
-               else
-                       interp = pltcl_norm_interp;
-               ReleaseSysCache(langTup);
-
-               /************************************************************
-                * Get the required information for input conversion of the
-                * return value.
-                ************************************************************/
-               typeTup = SearchSysCache(TYPEOID,
-                                                                ObjectIdGetDatum(procStruct->prorettype),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(typeTup))
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       if (!OidIsValid(procStruct->prorettype))
-                               elog(ERROR, "pltcl functions cannot return type \"opaque\""
-                                        "\n\texcept when used as triggers");
-                       else
-                               elog(ERROR, "pltcl: cache lookup for return type %u failed",
-                                        procStruct->prorettype);
-               }
-               typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-
-               if (typeStruct->typrelid != InvalidOid)
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: return types of tuples not supported yet");
-               }
-
-               perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
-               prodesc->result_in_elem = typeStruct->typelem;
-
-               ReleaseSysCache(typeTup);
-
-               /************************************************************
-                * Get the required information for output conversion
-                * of all procedure arguments
-                ************************************************************/
-               prodesc->nargs = procStruct->pronargs;
-               proc_internal_args[0] = '\0';
-               for (i = 0; i < prodesc->nargs; i++)
-               {
-                       typeTup = SearchSysCache(TYPEOID,
-                                                       ObjectIdGetDatum(procStruct->proargtypes[i]),
-                                                                        0, 0, 0);
-                       if (!HeapTupleIsValid(typeTup))
-                       {
-                               free(prodesc->proname);
-                               free(prodesc);
-                               if (!OidIsValid(procStruct->proargtypes[i]))
-                                       elog(ERROR, "pltcl functions cannot take type \"opaque\"");
-                               else
-                                       elog(ERROR, "pltcl: cache lookup for argument type %u failed",
-                                                procStruct->proargtypes[i]);
-                       }
-                       typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-
-                       if (typeStruct->typrelid != InvalidOid)
-                       {
-                               prodesc->arg_is_rel[i] = 1;
-                               if (i > 0)
-                                       strcat(proc_internal_args, " ");
-                               sprintf(buf, "__PLTcl_Tup_%d", i + 1);
-                               strcat(proc_internal_args, buf);
-                               ReleaseSysCache(typeTup);
-                               continue;
-                       }
-                       else
-                               prodesc->arg_is_rel[i] = 0;
-
-                       perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
-                       prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem);
-                       prodesc->arg_out_len[i] = typeStruct->typlen;
-
-                       if (i > 0)
-                               strcat(proc_internal_args, " ");
-                       sprintf(buf, "%d", i + 1);
-                       strcat(proc_internal_args, buf);
-
-                       ReleaseSysCache(typeTup);
-               }
-
-               /************************************************************
-                * Create the tcl command to define the internal
-                * procedure
-                ************************************************************/
-               Tcl_DStringInit(&proc_internal_def);
-               Tcl_DStringInit(&proc_internal_body);
-               Tcl_DStringAppendElement(&proc_internal_def, "proc");
-               Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
-               Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
-
-               /************************************************************
-                * prefix procedure body with
-                * upvar #0 <internal_procname> GD
-                * and with appropriate upvars for tuple arguments
-                ************************************************************/
-               Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
-               Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
-               Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
-               for (i = 0; i < fcinfo->nargs; i++)
-               {
-                       if (!prodesc->arg_is_rel[i])
-                               continue;
-                       sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1);
-                       Tcl_DStringAppend(&proc_internal_body, buf, -1);
-               }
-               proc_source = DatumGetCString(DirectFunctionCall1(textout,
-                                                                 PointerGetDatum(&procStruct->prosrc)));
-               UTF_BEGIN;
-               Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
-               UTF_END;
-               pfree(proc_source);
-               Tcl_DStringAppendElement(&proc_internal_def,
-                                                                Tcl_DStringValue(&proc_internal_body));
-               Tcl_DStringFree(&proc_internal_body);
+       /* Find or compile the function */
+       prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, false);
 
-               /************************************************************
-                * Create the procedure in the interpreter
-                ************************************************************/
-               tcl_rc = Tcl_GlobalEval(interp,
-                                                               Tcl_DStringValue(&proc_internal_def));
-               Tcl_DStringFree(&proc_internal_def);
-               if (tcl_rc != TCL_OK)
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
-                                internal_proname, interp->result);
-               }
-
-               /************************************************************
-                * Add the proc description block to the hashtable
-                ************************************************************/
-               hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
-                                                                         prodesc->proname, &hashnew);
-               Tcl_SetHashValue(hashent, (ClientData) prodesc);
-
-               ReleaseSysCache(procTup);
-       }
+       if (prodesc->lanpltrusted)
+               interp = pltcl_safe_interp;
        else
-       {
-               /************************************************************
-                * Found the proc description block in the hashtable
-                ************************************************************/
-               prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
-
-               if (prodesc->lanpltrusted)
-                       interp = pltcl_safe_interp;
-               else
-                       interp = pltcl_norm_interp;
-       }
+               interp = pltcl_norm_interp;
 
        /************************************************************
         * Create the tcl command to call the internal
@@ -694,7 +472,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
         ************************************************************/
        Tcl_DStringInit(&tcl_cmd);
        Tcl_DStringInit(&list_tmp);
-       Tcl_DStringAppendElement(&tcl_cmd, internal_proname);
+       Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
        /************************************************************
         * Catch elog(ERROR) during build of the Tcl command
@@ -841,13 +619,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
 static HeapTuple
 pltcl_trigger_handler(PG_FUNCTION_ARGS)
 {
-       Tcl_Interp *interp;
+       pltcl_proc_desc *prodesc;
+       Tcl_Interp *volatile interp;
        TriggerData *trigdata = (TriggerData *) fcinfo->context;
-       char            internal_proname[512];
        char       *stroid;
-       Tcl_HashEntry *hashent;
-       int                     hashnew;
-       pltcl_proc_desc *prodesc;
        TupleDesc       tupdesc;
        volatile HeapTuple rettup;
        Tcl_DString tcl_cmd;
@@ -865,154 +640,13 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 
        sigjmp_buf      save_restart;
 
-       /************************************************************
-        * Build our internal proc name from the functions Oid
-        ************************************************************/
-       sprintf(internal_proname, "__PLTcl_proc_%u", fcinfo->flinfo->fn_oid);
-
-       /************************************************************
-        * Lookup the internal proc name in the hashtable
-        ************************************************************/
-       hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
-       if (hashent == NULL)
-       {
-               /************************************************************
-                * If we haven't found it in the hashtable,
-                * we load the procedure into the safe interpreter.
-                ************************************************************/
-               Tcl_DString proc_internal_def;
-               Tcl_DString proc_internal_body;
-               HeapTuple       procTup;
-               HeapTuple       langTup;
-               Form_pg_proc procStruct;
-               Form_pg_language langStruct;
-               char       *proc_source;
-
-               /************************************************************
-                * Allocate a new procedure description block
-                ************************************************************/
-               prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
-               memset(prodesc, 0, sizeof(pltcl_proc_desc));
-               prodesc->proname = malloc(strlen(internal_proname) + 1);
-               strcpy(prodesc->proname, internal_proname);
-
-               /************************************************************
-                * Lookup the pg_proc tuple by Oid
-                ************************************************************/
-               procTup = SearchSysCache(PROCOID,
-                                                                ObjectIdGetDatum(fcinfo->flinfo->fn_oid),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(procTup))
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cache lookup for proc %u failed",
-                                fcinfo->flinfo->fn_oid);
-               }
-               procStruct = (Form_pg_proc) GETSTRUCT(procTup);
-
-               /************************************************************
-                * Lookup the pg_language tuple by Oid
-                ************************************************************/
-               langTup = SearchSysCache(LANGOID,
-                                                                ObjectIdGetDatum(procStruct->prolang),
-                                                                0, 0, 0);
-               if (!HeapTupleIsValid(langTup))
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cache lookup for language %u failed",
-                                procStruct->prolang);
-               }
-               langStruct = (Form_pg_language) GETSTRUCT(langTup);
-
-               prodesc->lanpltrusted = langStruct->lanpltrusted;
-               if (prodesc->lanpltrusted)
-                       interp = pltcl_safe_interp;
-               else
-                       interp = pltcl_norm_interp;
-               ReleaseSysCache(langTup);
+       /* Find or compile the function */
+       prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, true);
 
-               /************************************************************
-                * Create the tcl command to define the internal
-                * procedure
-                ************************************************************/
-               Tcl_DStringInit(&proc_internal_def);
-               Tcl_DStringInit(&proc_internal_body);
-               Tcl_DStringAppendElement(&proc_internal_def, "proc");
-               Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
-               Tcl_DStringAppendElement(&proc_internal_def,
-                                                                "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
-
-               /************************************************************
-                * prefix procedure body with
-                * upvar #0 <internal_procname> GD
-                * and with appropriate setting of NEW, OLD,
-                * and the arguments as numerical variables.
-                ************************************************************/
-               Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
-               Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
-               Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
-
-               Tcl_DStringAppend(&proc_internal_body,
-                                                 "array set NEW $__PLTcl_Tup_NEW\n", -1);
-               Tcl_DStringAppend(&proc_internal_body,
-                                                 "array set OLD $__PLTcl_Tup_OLD\n", -1);
-
-               Tcl_DStringAppend(&proc_internal_body,
-                                                 "set i 0\n"
-                                                 "set v 0\n"
-                                                 "foreach v $args {\n"
-                                                 "  incr i\n"
-                                                 "  set $i $v\n"
-                                                 "}\n"
-                                                 "unset i v\n\n", -1);
-
-               proc_source = DatumGetCString(DirectFunctionCall1(textout,
-                                                                 PointerGetDatum(&procStruct->prosrc)));
-               UTF_BEGIN;
-               Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
-               UTF_END;
-               pfree(proc_source);
-               Tcl_DStringAppendElement(&proc_internal_def,
-                                                                Tcl_DStringValue(&proc_internal_body));
-               Tcl_DStringFree(&proc_internal_body);
-
-               /************************************************************
-                * Create the procedure in the interpreter
-                ************************************************************/
-               tcl_rc = Tcl_GlobalEval(interp,
-                                                               Tcl_DStringValue(&proc_internal_def));
-               Tcl_DStringFree(&proc_internal_def);
-               if (tcl_rc != TCL_OK)
-               {
-                       free(prodesc->proname);
-                       free(prodesc);
-                       elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
-                                internal_proname, interp->result);
-               }
-
-               /************************************************************
-                * Add the proc description block to the hashtable
-                ************************************************************/
-               hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
-                                                                         prodesc->proname, &hashnew);
-               Tcl_SetHashValue(hashent, (ClientData) prodesc);
-
-               ReleaseSysCache(procTup);
-       }
+       if (prodesc->lanpltrusted)
+               interp = pltcl_safe_interp;
        else
-       {
-               /************************************************************
-                * Found the proc description block in the hashtable
-                ************************************************************/
-               prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
-
-               if (prodesc->lanpltrusted)
-                       interp = pltcl_safe_interp;
-               else
-                       interp = pltcl_norm_interp;
-       }
+               interp = pltcl_norm_interp;
 
        tupdesc = trigdata->tg_relation->rd_att;
 
@@ -1041,7 +675,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
        }
 
        /* The procedure name */
-       Tcl_DStringAppendElement(&tcl_cmd, internal_proname);
+       Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
 
        /* The trigger name for argument TG_name */
        Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
@@ -1303,6 +937,295 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
 }
 
 
+/**********************************************************************
+ * compile_pltcl_function      - compile (or hopefully just look up) function
+ **********************************************************************/
+static pltcl_proc_desc *
+compile_pltcl_function(Oid fn_oid, bool is_trigger)
+{
+       HeapTuple       procTup;
+       Form_pg_proc procStruct;
+       char            internal_proname[64];
+       Tcl_HashEntry *hashent;
+       pltcl_proc_desc *prodesc = NULL;
+       Tcl_Interp *interp;
+       int                     i;
+       int                     hashnew;
+       int                     tcl_rc;
+
+       /* We'll need the pg_proc tuple in any case... */
+       procTup = SearchSysCache(PROCOID,
+                                                        ObjectIdGetDatum(fn_oid),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(procTup))
+               elog(ERROR, "pltcl: cache lookup for proc %u failed", fn_oid);
+       procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+
+       /************************************************************
+        * Build our internal proc name from the functions Oid
+        ************************************************************/
+       if (!is_trigger)
+               sprintf(internal_proname, "__PLTcl_proc_%u", fn_oid);
+       else
+               sprintf(internal_proname, "__PLTcl_proc_%u_trigger", fn_oid);
+
+       /************************************************************
+        * Lookup the internal proc name in the hashtable
+        ************************************************************/
+       hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
+
+       /************************************************************
+        * 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)
+       {
+               bool            uptodate;
+
+               prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
+
+               uptodate = (prodesc->fn_xmin == procTup->t_data->t_xmin &&
+                                       prodesc->fn_cmin == procTup->t_data->t_cmin);
+
+               if (!uptodate)
+               {
+                       Tcl_DeleteHashEntry(hashent);
+                       hashent = NULL;
+               }
+       }
+
+       /************************************************************
+        * If we haven't found it in the hashtable, we analyze
+        * the functions arguments and returntype and store
+        * the in-/out-functions in the prodesc block and create
+        * a new hashtable entry for it.
+        *
+        * Then we load the procedure into the safe interpreter.
+        ************************************************************/
+       if (hashent == NULL)
+       {
+               HeapTuple       langTup;
+               HeapTuple       typeTup;
+               Form_pg_language langStruct;
+               Form_pg_type typeStruct;
+               Tcl_DString proc_internal_def;
+               Tcl_DString proc_internal_body;
+               char            proc_internal_args[4096];
+               char       *proc_source;
+               char            buf[512];
+
+               /************************************************************
+                * Allocate a new procedure description block
+                ************************************************************/
+               prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
+               if (prodesc == NULL)
+                       elog(ERROR, "pltcl: out of memory");
+               MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
+               prodesc->proname = strdup(internal_proname);
+               prodesc->fn_xmin = procTup->t_data->t_xmin;
+               prodesc->fn_cmin = procTup->t_data->t_cmin;
+
+               /************************************************************
+                * Lookup the pg_language tuple by Oid
+                ************************************************************/
+               langTup = SearchSysCache(LANGOID,
+                                                                ObjectIdGetDatum(procStruct->prolang),
+                                                                0, 0, 0);
+               if (!HeapTupleIsValid(langTup))
+               {
+                       free(prodesc->proname);
+                       free(prodesc);
+                       elog(ERROR, "pltcl: cache lookup for language %u failed",
+                                procStruct->prolang);
+               }
+               langStruct = (Form_pg_language) GETSTRUCT(langTup);
+               prodesc->lanpltrusted = langStruct->lanpltrusted;
+               ReleaseSysCache(langTup);
+
+               if (prodesc->lanpltrusted)
+                       interp = pltcl_safe_interp;
+               else
+                       interp = pltcl_norm_interp;
+
+               /************************************************************
+                * Get the required information for input conversion of the
+                * return value.
+                ************************************************************/
+               if (!is_trigger)
+               {
+                       typeTup = SearchSysCache(TYPEOID,
+                                                                        ObjectIdGetDatum(procStruct->prorettype),
+                                                                        0, 0, 0);
+                       if (!HeapTupleIsValid(typeTup))
+                       {
+                               free(prodesc->proname);
+                               free(prodesc);
+                               if (!OidIsValid(procStruct->prorettype))
+                                       elog(ERROR, "pltcl functions cannot return type \"opaque\""
+                                                "\n\texcept when used as triggers");
+                               else
+                                       elog(ERROR, "pltcl: cache lookup for return type %u failed",
+                                                procStruct->prorettype);
+                       }
+                       typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+                       if (typeStruct->typrelid != InvalidOid)
+                       {
+                               free(prodesc->proname);
+                               free(prodesc);
+                               elog(ERROR, "pltcl: return types of tuples not supported yet");
+                       }
+
+                       perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
+                       prodesc->result_in_elem = typeStruct->typelem;
+
+                       ReleaseSysCache(typeTup);
+               }
+
+               /************************************************************
+                * Get the required information for output conversion
+                * of all procedure arguments
+                ************************************************************/
+               if (!is_trigger)
+               {
+                       prodesc->nargs = procStruct->pronargs;
+                       proc_internal_args[0] = '\0';
+                       for (i = 0; i < prodesc->nargs; i++)
+                       {
+                               typeTup = SearchSysCache(TYPEOID,
+                                                                                ObjectIdGetDatum(procStruct->proargtypes[i]),
+                                                                                0, 0, 0);
+                               if (!HeapTupleIsValid(typeTup))
+                               {
+                                       free(prodesc->proname);
+                                       free(prodesc);
+                                       if (!OidIsValid(procStruct->proargtypes[i]))
+                                               elog(ERROR, "pltcl functions cannot take type \"opaque\"");
+                                       else
+                                               elog(ERROR, "pltcl: cache lookup for argument type %u failed",
+                                                        procStruct->proargtypes[i]);
+                               }
+                               typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+                               if (typeStruct->typrelid != InvalidOid)
+                               {
+                                       prodesc->arg_is_rel[i] = 1;
+                                       if (i > 0)
+                                               strcat(proc_internal_args, " ");
+                                       sprintf(buf, "__PLTcl_Tup_%d", i + 1);
+                                       strcat(proc_internal_args, buf);
+                                       ReleaseSysCache(typeTup);
+                                       continue;
+                               }
+                               else
+                                       prodesc->arg_is_rel[i] = 0;
+
+                               perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
+                               prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem);
+                               prodesc->arg_out_len[i] = typeStruct->typlen;
+
+                               if (i > 0)
+                                       strcat(proc_internal_args, " ");
+                               sprintf(buf, "%d", i + 1);
+                               strcat(proc_internal_args, buf);
+
+                               ReleaseSysCache(typeTup);
+                       }
+               }
+               else
+               {
+                       /* trigger procedure has fixed args */
+                       strcpy(proc_internal_args,
+                                  "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
+               }
+
+               /************************************************************
+                * Create the tcl command to define the internal
+                * procedure
+                ************************************************************/
+               Tcl_DStringInit(&proc_internal_def);
+               Tcl_DStringInit(&proc_internal_body);
+               Tcl_DStringAppendElement(&proc_internal_def, "proc");
+               Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
+               Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
+
+               /************************************************************
+                * prefix procedure body with
+                * upvar #0 <internal_procname> GD
+                * and with appropriate setting of arguments
+                ************************************************************/
+               Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
+               Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
+               Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
+               if (!is_trigger)
+               {
+                       for (i = 0; i < prodesc->nargs; i++)
+                       {
+                               if (!prodesc->arg_is_rel[i])
+                                       continue;
+                               sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1);
+                               Tcl_DStringAppend(&proc_internal_body, buf, -1);
+                       }
+               }
+               else
+               {
+                       Tcl_DStringAppend(&proc_internal_body,
+                                                         "array set NEW $__PLTcl_Tup_NEW\n", -1);
+                       Tcl_DStringAppend(&proc_internal_body,
+                                                         "array set OLD $__PLTcl_Tup_OLD\n", -1);
+
+                       Tcl_DStringAppend(&proc_internal_body,
+                                                         "set i 0\n"
+                                                         "set v 0\n"
+                                                         "foreach v $args {\n"
+                                                         "  incr i\n"
+                                                         "  set $i $v\n"
+                                                         "}\n"
+                                                         "unset i v\n\n", -1);
+               }
+
+               /************************************************************
+                * Add user's function definition to proc body
+                ************************************************************/
+               proc_source = DatumGetCString(DirectFunctionCall1(textout,
+                                                                 PointerGetDatum(&procStruct->prosrc)));
+               UTF_BEGIN;
+               Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
+               UTF_END;
+               pfree(proc_source);
+               Tcl_DStringAppendElement(&proc_internal_def,
+                                                                Tcl_DStringValue(&proc_internal_body));
+               Tcl_DStringFree(&proc_internal_body);
+
+               /************************************************************
+                * Create the procedure in the interpreter
+                ************************************************************/
+               tcl_rc = Tcl_GlobalEval(interp,
+                                                               Tcl_DStringValue(&proc_internal_def));
+               Tcl_DStringFree(&proc_internal_def);
+               if (tcl_rc != TCL_OK)
+               {
+                       free(prodesc->proname);
+                       free(prodesc);
+                       elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
+                                internal_proname, interp->result);
+               }
+
+               /************************************************************
+                * Add the proc description block to the hashtable
+                ************************************************************/
+               hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
+                                                                         prodesc->proname, &hashnew);
+               Tcl_SetHashValue(hashent, (ClientData) prodesc);
+       }
+
+       ReleaseSysCache(procTup);
+
+       return prodesc;
+}
+
+
 /**********************************************************************
  * pltcl_elog()                - elog() support for PLTcl
  **********************************************************************/
@@ -1486,7 +1409,6 @@ static int
 pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
                                 int argc, char *argv[])
 {
-       int                     argno;
        FunctionCallInfo fcinfo = pltcl_current_fcinfo;
 
        /************************************************************