Header And Logo

PostgreSQL
| The world's most advanced open source database.

pltcl.c

Go to the documentation of this file.
00001 /**********************************************************************
00002  * pltcl.c      - PostgreSQL support for Tcl as
00003  *                procedural language (PL)
00004  *
00005  *    src/pl/tcl/pltcl.c
00006  *
00007  **********************************************************************/
00008 
00009 #include "postgres.h"
00010 
00011 #include <tcl.h>
00012 
00013 #include <unistd.h>
00014 #include <fcntl.h>
00015 
00016 /* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
00017 #ifndef CONST84
00018 #define CONST84
00019 #endif
00020 
00021 /* ... and for Tcl 8.6. */
00022 #ifndef CONST86
00023 #define CONST86
00024 #endif
00025 
00026 #include "access/htup_details.h"
00027 #include "access/xact.h"
00028 #include "catalog/pg_proc.h"
00029 #include "catalog/pg_type.h"
00030 #include "commands/trigger.h"
00031 #include "executor/spi.h"
00032 #include "fmgr.h"
00033 #include "miscadmin.h"
00034 #include "nodes/makefuncs.h"
00035 #include "parser/parse_type.h"
00036 #include "tcop/tcopprot.h"
00037 #include "utils/builtins.h"
00038 #include "utils/lsyscache.h"
00039 #include "utils/memutils.h"
00040 #include "utils/rel.h"
00041 #include "utils/syscache.h"
00042 #include "utils/typcache.h"
00043 
00044 
00045 #define HAVE_TCL_VERSION(maj,min) \
00046     ((TCL_MAJOR_VERSION > maj) || \
00047      (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
00048 
00049 /* In Tcl >= 8.0, really not supposed to touch interp->result directly */
00050 #if !HAVE_TCL_VERSION(8,0)
00051 #define Tcl_GetStringResult(interp)  ((interp)->result)
00052 #endif
00053 
00054 /* define our text domain for translations */
00055 #undef TEXTDOMAIN
00056 #define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
00057 
00058 #if defined(UNICODE_CONVERSION) && HAVE_TCL_VERSION(8,1)
00059 
00060 #include "mb/pg_wchar.h"
00061 
00062 static unsigned char *
00063 utf_u2e(unsigned char *src)
00064 {
00065     return pg_do_encoding_conversion(src, strlen(src), PG_UTF8, GetDatabaseEncoding());
00066 }
00067 
00068 static unsigned char *
00069 utf_e2u(unsigned char *src)
00070 {
00071     return pg_do_encoding_conversion(src, strlen(src), GetDatabaseEncoding(), PG_UTF8);
00072 }
00073 
00074 #define PLTCL_UTF
00075 #define UTF_BEGIN    do { \
00076                     unsigned char *_pltcl_utf_src; \
00077                     unsigned char *_pltcl_utf_dst
00078 #define UTF_END      if (_pltcl_utf_src!=_pltcl_utf_dst) \
00079                     pfree(_pltcl_utf_dst); } while (0)
00080 #define UTF_U2E(x)   (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
00081 #define UTF_E2U(x)   (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
00082 #else                           /* !PLTCL_UTF */
00083 
00084 #define  UTF_BEGIN
00085 #define  UTF_END
00086 #define  UTF_U2E(x)  (x)
00087 #define  UTF_E2U(x)  (x)
00088 #endif   /* PLTCL_UTF */
00089 
00090 PG_MODULE_MAGIC;
00091 
00092 
00093 /**********************************************************************
00094  * Information associated with a Tcl interpreter.  We have one interpreter
00095  * that is used for all pltclu (untrusted) functions.  For pltcl (trusted)
00096  * functions, there is a separate interpreter for each effective SQL userid.
00097  * (This is needed to ensure that an unprivileged user can't inject Tcl code
00098  * that'll be executed with the privileges of some other SQL user.)
00099  *
00100  * The pltcl_interp_desc structs are kept in a Postgres hash table indexed
00101  * by userid OID, with OID 0 used for the single untrusted interpreter.
00102  **********************************************************************/
00103 typedef struct pltcl_interp_desc
00104 {
00105     Oid         user_id;        /* Hash key (must be first!) */
00106     Tcl_Interp *interp;         /* The interpreter */
00107     Tcl_HashTable query_hash;   /* pltcl_query_desc structs */
00108 } pltcl_interp_desc;
00109 
00110 
00111 /**********************************************************************
00112  * The information we cache about loaded procedures
00113  **********************************************************************/
00114 typedef struct pltcl_proc_desc
00115 {
00116     char       *user_proname;
00117     char       *internal_proname;
00118     TransactionId fn_xmin;
00119     ItemPointerData fn_tid;
00120     bool        fn_readonly;
00121     bool        lanpltrusted;
00122     pltcl_interp_desc *interp_desc;
00123     FmgrInfo    result_in_func;
00124     Oid         result_typioparam;
00125     int         nargs;
00126     FmgrInfo    arg_out_func[FUNC_MAX_ARGS];
00127     bool        arg_is_rowtype[FUNC_MAX_ARGS];
00128 } pltcl_proc_desc;
00129 
00130 
00131 /**********************************************************************
00132  * The information we cache about prepared and saved plans
00133  **********************************************************************/
00134 typedef struct pltcl_query_desc
00135 {
00136     char        qname[20];
00137     SPIPlanPtr  plan;
00138     int         nargs;
00139     Oid        *argtypes;
00140     FmgrInfo   *arginfuncs;
00141     Oid        *argtypioparams;
00142 } pltcl_query_desc;
00143 
00144 
00145 /**********************************************************************
00146  * For speedy lookup, we maintain a hash table mapping from
00147  * function OID + trigger flag + user OID to pltcl_proc_desc pointers.
00148  * The reason the pltcl_proc_desc struct isn't directly part of the hash
00149  * entry is to simplify recovery from errors during compile_pltcl_function.
00150  *
00151  * Note: if the same function is called by multiple userIDs within a session,
00152  * there will be a separate pltcl_proc_desc entry for each userID in the case
00153  * of pltcl functions, but only one entry for pltclu functions, because we
00154  * set user_id = 0 for that case.
00155  **********************************************************************/
00156 typedef struct pltcl_proc_key
00157 {
00158     Oid         proc_id;        /* Function OID */
00159 
00160     /*
00161      * is_trigger is really a bool, but declare as Oid to ensure this struct
00162      * contains no padding
00163      */
00164     Oid         is_trigger;     /* is it a trigger function? */
00165     Oid         user_id;        /* User calling the function, or 0 */
00166 } pltcl_proc_key;
00167 
00168 typedef struct pltcl_proc_ptr
00169 {
00170     pltcl_proc_key proc_key;    /* Hash key (must be first!) */
00171     pltcl_proc_desc *proc_ptr;
00172 } pltcl_proc_ptr;
00173 
00174 
00175 /**********************************************************************
00176  * Global data
00177  **********************************************************************/
00178 static bool pltcl_pm_init_done = false;
00179 static Tcl_Interp *pltcl_hold_interp = NULL;
00180 static HTAB *pltcl_interp_htab = NULL;
00181 static HTAB *pltcl_proc_htab = NULL;
00182 
00183 /* these are saved and restored by pltcl_handler */
00184 static FunctionCallInfo pltcl_current_fcinfo = NULL;
00185 static pltcl_proc_desc *pltcl_current_prodesc = NULL;
00186 
00187 /**********************************************************************
00188  * Forward declarations
00189  **********************************************************************/
00190 Datum       pltcl_call_handler(PG_FUNCTION_ARGS);
00191 Datum       pltclu_call_handler(PG_FUNCTION_ARGS);
00192 void        _PG_init(void);
00193 
00194 static void pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted);
00195 static pltcl_interp_desc *pltcl_fetch_interp(bool pltrusted);
00196 static void pltcl_init_load_unknown(Tcl_Interp *interp);
00197 
00198 static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
00199 
00200 static Datum pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted);
00201 
00202 static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted);
00203 
00204 static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
00205 
00206 static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
00207                        bool pltrusted);
00208 
00209 static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
00210            int argc, CONST84 char *argv[]);
00211 static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
00212             int argc, CONST84 char *argv[]);
00213 static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
00214                 int argc, CONST84 char *argv[]);
00215 static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
00216                  int argc, CONST84 char *argv[]);
00217 
00218 static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
00219                   int argc, CONST84 char *argv[]);
00220 static int pltcl_process_SPI_result(Tcl_Interp *interp,
00221                          CONST84 char *arrayname,
00222                          CONST84 char *loop_body,
00223                          int spi_rc,
00224                          SPITupleTable *tuptable,
00225                          int ntuples);
00226 static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
00227                   int argc, CONST84 char *argv[]);
00228 static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
00229                        int argc, CONST84 char *argv[]);
00230 static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
00231                   int argc, CONST84 char *argv[]);
00232 
00233 static void pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
00234                        int tupno, HeapTuple tuple, TupleDesc tupdesc);
00235 static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
00236                            Tcl_DString *retval);
00237 
00238 
00239 /*
00240  * Hack to override Tcl's builtin Notifier subsystem.  This prevents the
00241  * backend from becoming multithreaded, which breaks all sorts of things.
00242  * That happens in the default version of Tcl_InitNotifier if the TCL library
00243  * has been compiled with multithreading support (i.e. when TCL_THREADS is
00244  * defined under Unix, and in all cases under Windows).
00245  * It's okay to disable the notifier because we never enter the Tcl event loop
00246  * from Postgres, so the notifier capabilities are initialized, but never
00247  * used.  Only InitNotifier and DeleteFileHandler ever seem to get called
00248  * within Postgres, but we implement all the functions for completeness.
00249  * We can only fix this with Tcl >= 8.4, when Tcl_SetNotifier() appeared.
00250  */
00251 #if HAVE_TCL_VERSION(8,4)
00252 
00253 static ClientData
00254 pltcl_InitNotifier(void)
00255 {
00256     static int  fakeThreadKey;  /* To give valid address for ClientData */
00257 
00258     return (ClientData) &(fakeThreadKey);
00259 }
00260 
00261 static void
00262 pltcl_FinalizeNotifier(ClientData clientData)
00263 {
00264 }
00265 
00266 static void
00267 pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
00268 {
00269 }
00270 
00271 static void
00272 pltcl_AlertNotifier(ClientData clientData)
00273 {
00274 }
00275 
00276 static void
00277 pltcl_CreateFileHandler(int fd, int mask,
00278                         Tcl_FileProc *proc, ClientData clientData)
00279 {
00280 }
00281 
00282 static void
00283 pltcl_DeleteFileHandler(int fd)
00284 {
00285 }
00286 
00287 static void
00288 pltcl_ServiceModeHook(int mode)
00289 {
00290 }
00291 
00292 static int
00293 pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
00294 {
00295     return 0;
00296 }
00297 #endif   /* HAVE_TCL_VERSION(8,4) */
00298 
00299 
00300 /*
00301  * This routine is a crock, and so is everyplace that calls it.  The problem
00302  * is that the cached form of pltcl functions/queries is allocated permanently
00303  * (mostly via malloc()) and never released until backend exit.  Subsidiary
00304  * data structures such as fmgr info records therefore must live forever
00305  * as well.  A better implementation would store all this stuff in a per-
00306  * function memory context that could be reclaimed at need.  In the meantime,
00307  * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
00308  * it might allocate, and whatever the eventual function might allocate using
00309  * fn_mcxt, will live forever too.
00310  */
00311 static void
00312 perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
00313 {
00314     fmgr_info_cxt(functionId, finfo, TopMemoryContext);
00315 }
00316 
00317 /*
00318  * _PG_init()           - library load-time initialization
00319  *
00320  * DO NOT make this static nor change its name!
00321  *
00322  * The work done here must be safe to do in the postmaster process,
00323  * in case the pltcl library is preloaded in the postmaster.
00324  */
00325 void
00326 _PG_init(void)
00327 {
00328     HASHCTL     hash_ctl;
00329 
00330     /* Be sure we do initialization only once (should be redundant now) */
00331     if (pltcl_pm_init_done)
00332         return;
00333 
00334     pg_bindtextdomain(TEXTDOMAIN);
00335 
00336 #ifdef WIN32
00337     /* Required on win32 to prevent error loading init.tcl */
00338     Tcl_FindExecutable("");
00339 #endif
00340 
00341 #if HAVE_TCL_VERSION(8,4)
00342 
00343     /*
00344      * Override the functions in the Notifier subsystem.  See comments above.
00345      */
00346     {
00347         Tcl_NotifierProcs notifier;
00348 
00349         notifier.setTimerProc = pltcl_SetTimer;
00350         notifier.waitForEventProc = pltcl_WaitForEvent;
00351         notifier.createFileHandlerProc = pltcl_CreateFileHandler;
00352         notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
00353         notifier.initNotifierProc = pltcl_InitNotifier;
00354         notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
00355         notifier.alertNotifierProc = pltcl_AlertNotifier;
00356         notifier.serviceModeHookProc = pltcl_ServiceModeHook;
00357         Tcl_SetNotifier(&notifier);
00358     }
00359 #endif
00360 
00361     /************************************************************
00362      * Create the dummy hold interpreter to prevent close of
00363      * stdout and stderr on DeleteInterp
00364      ************************************************************/
00365     if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
00366         elog(ERROR, "could not create master Tcl interpreter");
00367     if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
00368         elog(ERROR, "could not initialize master Tcl interpreter");
00369 
00370     /************************************************************
00371      * Create the hash table for working interpreters
00372      ************************************************************/
00373     memset(&hash_ctl, 0, sizeof(hash_ctl));
00374     hash_ctl.keysize = sizeof(Oid);
00375     hash_ctl.entrysize = sizeof(pltcl_interp_desc);
00376     hash_ctl.hash = oid_hash;
00377     pltcl_interp_htab = hash_create("PL/Tcl interpreters",
00378                                     8,
00379                                     &hash_ctl,
00380                                     HASH_ELEM | HASH_FUNCTION);
00381 
00382     /************************************************************
00383      * Create the hash table for function lookup
00384      ************************************************************/
00385     memset(&hash_ctl, 0, sizeof(hash_ctl));
00386     hash_ctl.keysize = sizeof(pltcl_proc_key);
00387     hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
00388     hash_ctl.hash = tag_hash;
00389     pltcl_proc_htab = hash_create("PL/Tcl functions",
00390                                   100,
00391                                   &hash_ctl,
00392                                   HASH_ELEM | HASH_FUNCTION);
00393 
00394     pltcl_pm_init_done = true;
00395 }
00396 
00397 /**********************************************************************
00398  * pltcl_init_interp() - initialize a new Tcl interpreter
00399  **********************************************************************/
00400 static void
00401 pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
00402 {
00403     Tcl_Interp *interp;
00404     char        interpname[32];
00405 
00406     /************************************************************
00407      * Create the Tcl interpreter as a slave of pltcl_hold_interp.
00408      * Note: Tcl automatically does Tcl_Init in the untrusted case,
00409      * and it's not wanted in the trusted case.
00410      ************************************************************/
00411     snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
00412     if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
00413                                   pltrusted ? 1 : 0)) == NULL)
00414         elog(ERROR, "could not create slave Tcl interpreter");
00415     interp_desc->interp = interp;
00416 
00417     /************************************************************
00418      * Initialize the query hash table associated with interpreter
00419      ************************************************************/
00420     Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
00421 
00422     /************************************************************
00423      * Install the commands for SPI support in the interpreter
00424      ************************************************************/
00425     Tcl_CreateCommand(interp, "elog",
00426                       pltcl_elog, NULL, NULL);
00427     Tcl_CreateCommand(interp, "quote",
00428                       pltcl_quote, NULL, NULL);
00429     Tcl_CreateCommand(interp, "argisnull",
00430                       pltcl_argisnull, NULL, NULL);
00431     Tcl_CreateCommand(interp, "return_null",
00432                       pltcl_returnnull, NULL, NULL);
00433 
00434     Tcl_CreateCommand(interp, "spi_exec",
00435                       pltcl_SPI_execute, NULL, NULL);
00436     Tcl_CreateCommand(interp, "spi_prepare",
00437                       pltcl_SPI_prepare, NULL, NULL);
00438     Tcl_CreateCommand(interp, "spi_execp",
00439                       pltcl_SPI_execute_plan, NULL, NULL);
00440     Tcl_CreateCommand(interp, "spi_lastoid",
00441                       pltcl_SPI_lastoid, NULL, NULL);
00442 
00443     /************************************************************
00444      * Try to load the unknown procedure from pltcl_modules
00445      ************************************************************/
00446     pltcl_init_load_unknown(interp);
00447 }
00448 
00449 /**********************************************************************
00450  * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
00451  *
00452  * This also takes care of any on-first-use initialization required.
00453  * Note: we assume caller has already connected to SPI.
00454  **********************************************************************/
00455 static pltcl_interp_desc *
00456 pltcl_fetch_interp(bool pltrusted)
00457 {
00458     Oid         user_id;
00459     pltcl_interp_desc *interp_desc;
00460     bool        found;
00461 
00462     /* Find or create the interpreter hashtable entry for this userid */
00463     if (pltrusted)
00464         user_id = GetUserId();
00465     else
00466         user_id = InvalidOid;
00467 
00468     interp_desc = hash_search(pltcl_interp_htab, &user_id,
00469                               HASH_ENTER,
00470                               &found);
00471     if (!found)
00472         pltcl_init_interp(interp_desc, pltrusted);
00473 
00474     return interp_desc;
00475 }
00476 
00477 /**********************************************************************
00478  * pltcl_init_load_unknown()    - Load the unknown procedure from
00479  *                table pltcl_modules (if it exists)
00480  **********************************************************************/
00481 static void
00482 pltcl_init_load_unknown(Tcl_Interp *interp)
00483 {
00484     Relation    pmrel;
00485     char       *pmrelname,
00486                *nspname;
00487     char       *buf;
00488     int         buflen;
00489     int         spi_rc;
00490     int         tcl_rc;
00491     Tcl_DString unknown_src;
00492     char       *part;
00493     int         i;
00494     int         fno;
00495 
00496     /************************************************************
00497      * Check if table pltcl_modules exists
00498      *
00499      * We allow the table to be found anywhere in the search_path.
00500      * This is for backwards compatibility.  To ensure that the table
00501      * is trustworthy, we require it to be owned by a superuser.
00502      ************************************************************/
00503     pmrel = relation_openrv_extended(makeRangeVar(NULL, "pltcl_modules", -1),
00504                                      AccessShareLock, true);
00505     if (pmrel == NULL)
00506         return;
00507     /* must be table or view, else ignore */
00508     if (!(pmrel->rd_rel->relkind == RELKIND_RELATION ||
00509           pmrel->rd_rel->relkind == RELKIND_MATVIEW ||
00510           pmrel->rd_rel->relkind == RELKIND_VIEW))
00511     {
00512         relation_close(pmrel, AccessShareLock);
00513         return;
00514     }
00515     /* must be owned by superuser, else ignore */
00516     if (!superuser_arg(pmrel->rd_rel->relowner))
00517     {
00518         relation_close(pmrel, AccessShareLock);
00519         return;
00520     }
00521     /* get fully qualified table name for use in select command */
00522     nspname = get_namespace_name(RelationGetNamespace(pmrel));
00523     if (!nspname)
00524         elog(ERROR, "cache lookup failed for namespace %u",
00525              RelationGetNamespace(pmrel));
00526     pmrelname = quote_qualified_identifier(nspname,
00527                                            RelationGetRelationName(pmrel));
00528 
00529     /************************************************************
00530      * Read all the rows from it where modname = 'unknown',
00531      * in the order of modseq
00532      ************************************************************/
00533     buflen = strlen(pmrelname) + 100;
00534     buf = (char *) palloc(buflen);
00535     snprintf(buf, buflen,
00536            "select modsrc from %s where modname = 'unknown' order by modseq",
00537              pmrelname);
00538 
00539     spi_rc = SPI_execute(buf, false, 0);
00540     if (spi_rc != SPI_OK_SELECT)
00541         elog(ERROR, "select from pltcl_modules failed");
00542 
00543     pfree(buf);
00544 
00545     /************************************************************
00546      * If there's nothing, module unknown doesn't exist
00547      ************************************************************/
00548     if (SPI_processed == 0)
00549     {
00550         SPI_freetuptable(SPI_tuptable);
00551         elog(WARNING, "module \"unknown\" not found in pltcl_modules");
00552         relation_close(pmrel, AccessShareLock);
00553         return;
00554     }
00555 
00556     /************************************************************
00557      * There is a module named unknown. Reassemble the
00558      * source from the modsrc attributes and evaluate
00559      * it in the Tcl interpreter
00560      ************************************************************/
00561     fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
00562 
00563     Tcl_DStringInit(&unknown_src);
00564 
00565     for (i = 0; i < SPI_processed; i++)
00566     {
00567         part = SPI_getvalue(SPI_tuptable->vals[i],
00568                             SPI_tuptable->tupdesc, fno);
00569         if (part != NULL)
00570         {
00571             UTF_BEGIN;
00572             Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
00573             UTF_END;
00574             pfree(part);
00575         }
00576     }
00577     tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
00578 
00579     Tcl_DStringFree(&unknown_src);
00580     SPI_freetuptable(SPI_tuptable);
00581 
00582     if (tcl_rc != TCL_OK)
00583     {
00584         UTF_BEGIN;
00585         elog(ERROR, "could not load module \"unknown\": %s",
00586              UTF_U2E(Tcl_GetStringResult(interp)));
00587         UTF_END;
00588     }
00589 
00590     relation_close(pmrel, AccessShareLock);
00591 }
00592 
00593 
00594 /**********************************************************************
00595  * pltcl_call_handler       - This is the only visible function
00596  *                of the PL interpreter. The PostgreSQL
00597  *                function manager and trigger manager
00598  *                call this function for execution of
00599  *                PL/Tcl procedures.
00600  **********************************************************************/
00601 PG_FUNCTION_INFO_V1(pltcl_call_handler);
00602 
00603 /* keep non-static */
00604 Datum
00605 pltcl_call_handler(PG_FUNCTION_ARGS)
00606 {
00607     return pltcl_handler(fcinfo, true);
00608 }
00609 
00610 /*
00611  * Alternative handler for unsafe functions
00612  */
00613 PG_FUNCTION_INFO_V1(pltclu_call_handler);
00614 
00615 /* keep non-static */
00616 Datum
00617 pltclu_call_handler(PG_FUNCTION_ARGS)
00618 {
00619     return pltcl_handler(fcinfo, false);
00620 }
00621 
00622 
00623 static Datum
00624 pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
00625 {
00626     Datum       retval;
00627     FunctionCallInfo save_fcinfo;
00628     pltcl_proc_desc *save_prodesc;
00629 
00630     /*
00631      * Ensure that static pointers are saved/restored properly
00632      */
00633     save_fcinfo = pltcl_current_fcinfo;
00634     save_prodesc = pltcl_current_prodesc;
00635 
00636     PG_TRY();
00637     {
00638         /*
00639          * Determine if called as function or trigger and call appropriate
00640          * subhandler
00641          */
00642         if (CALLED_AS_TRIGGER(fcinfo))
00643         {
00644             pltcl_current_fcinfo = NULL;
00645             retval = PointerGetDatum(pltcl_trigger_handler(fcinfo, pltrusted));
00646         }
00647         else
00648         {
00649             pltcl_current_fcinfo = fcinfo;
00650             retval = pltcl_func_handler(fcinfo, pltrusted);
00651         }
00652     }
00653     PG_CATCH();
00654     {
00655         pltcl_current_fcinfo = save_fcinfo;
00656         pltcl_current_prodesc = save_prodesc;
00657         PG_RE_THROW();
00658     }
00659     PG_END_TRY();
00660 
00661     pltcl_current_fcinfo = save_fcinfo;
00662     pltcl_current_prodesc = save_prodesc;
00663 
00664     return retval;
00665 }
00666 
00667 
00668 /**********************************************************************
00669  * pltcl_func_handler()     - Handler for regular function calls
00670  **********************************************************************/
00671 static Datum
00672 pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
00673 {
00674     pltcl_proc_desc *prodesc;
00675     Tcl_Interp *volatile interp;
00676     Tcl_DString tcl_cmd;
00677     Tcl_DString list_tmp;
00678     int         i;
00679     int         tcl_rc;
00680     Datum       retval;
00681 
00682     /* Connect to SPI manager */
00683     if (SPI_connect() != SPI_OK_CONNECT)
00684         elog(ERROR, "could not connect to SPI manager");
00685 
00686     /* Find or compile the function */
00687     prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
00688                                      pltrusted);
00689 
00690     pltcl_current_prodesc = prodesc;
00691 
00692     interp = prodesc->interp_desc->interp;
00693 
00694     /************************************************************
00695      * Create the tcl command to call the internal
00696      * proc in the Tcl interpreter
00697      ************************************************************/
00698     Tcl_DStringInit(&tcl_cmd);
00699     Tcl_DStringInit(&list_tmp);
00700     Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
00701 
00702     /************************************************************
00703      * Add all call arguments to the command
00704      ************************************************************/
00705     PG_TRY();
00706     {
00707         for (i = 0; i < prodesc->nargs; i++)
00708         {
00709             if (prodesc->arg_is_rowtype[i])
00710             {
00711                 /**************************************************
00712                  * For tuple values, add a list for 'array set ...'
00713                  **************************************************/
00714                 if (fcinfo->argnull[i])
00715                     Tcl_DStringAppendElement(&tcl_cmd, "");
00716                 else
00717                 {
00718                     HeapTupleHeader td;
00719                     Oid         tupType;
00720                     int32       tupTypmod;
00721                     TupleDesc   tupdesc;
00722                     HeapTupleData tmptup;
00723 
00724                     td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
00725                     /* Extract rowtype info and find a tupdesc */
00726                     tupType = HeapTupleHeaderGetTypeId(td);
00727                     tupTypmod = HeapTupleHeaderGetTypMod(td);
00728                     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00729                     /* Build a temporary HeapTuple control structure */
00730                     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
00731                     tmptup.t_data = td;
00732 
00733                     Tcl_DStringSetLength(&list_tmp, 0);
00734                     pltcl_build_tuple_argument(&tmptup, tupdesc, &list_tmp);
00735                     Tcl_DStringAppendElement(&tcl_cmd,
00736                                              Tcl_DStringValue(&list_tmp));
00737                     ReleaseTupleDesc(tupdesc);
00738                 }
00739             }
00740             else
00741             {
00742                 /**************************************************
00743                  * Single values are added as string element
00744                  * of their external representation
00745                  **************************************************/
00746                 if (fcinfo->argnull[i])
00747                     Tcl_DStringAppendElement(&tcl_cmd, "");
00748                 else
00749                 {
00750                     char       *tmp;
00751 
00752                     tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
00753                                              fcinfo->arg[i]);
00754                     UTF_BEGIN;
00755                     Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
00756                     UTF_END;
00757                     pfree(tmp);
00758                 }
00759             }
00760         }
00761     }
00762     PG_CATCH();
00763     {
00764         Tcl_DStringFree(&tcl_cmd);
00765         Tcl_DStringFree(&list_tmp);
00766         PG_RE_THROW();
00767     }
00768     PG_END_TRY();
00769     Tcl_DStringFree(&list_tmp);
00770 
00771     /************************************************************
00772      * Call the Tcl function
00773      *
00774      * We assume no PG error can be thrown directly from this call.
00775      ************************************************************/
00776     tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
00777     Tcl_DStringFree(&tcl_cmd);
00778 
00779     /************************************************************
00780      * Check for errors reported by Tcl.
00781      ************************************************************/
00782     if (tcl_rc != TCL_OK)
00783         throw_tcl_error(interp, prodesc->user_proname);
00784 
00785     /************************************************************
00786      * Disconnect from SPI manager and then create the return
00787      * value datum (if the input function does a palloc for it
00788      * this must not be allocated in the SPI memory context
00789      * because SPI_finish would free it).  But don't try to call
00790      * the result_in_func if we've been told to return a NULL;
00791      * the Tcl result may not be a valid value of the result type
00792      * in that case.
00793      ************************************************************/
00794     if (SPI_finish() != SPI_OK_FINISH)
00795         elog(ERROR, "SPI_finish() failed");
00796 
00797     if (fcinfo->isnull)
00798         retval = InputFunctionCall(&prodesc->result_in_func,
00799                                    NULL,
00800                                    prodesc->result_typioparam,
00801                                    -1);
00802     else
00803     {
00804         UTF_BEGIN;
00805         retval = InputFunctionCall(&prodesc->result_in_func,
00806                                UTF_U2E((char *) Tcl_GetStringResult(interp)),
00807                                    prodesc->result_typioparam,
00808                                    -1);
00809         UTF_END;
00810     }
00811 
00812     return retval;
00813 }
00814 
00815 
00816 /**********************************************************************
00817  * pltcl_trigger_handler()  - Handler for trigger calls
00818  **********************************************************************/
00819 static HeapTuple
00820 pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
00821 {
00822     pltcl_proc_desc *prodesc;
00823     Tcl_Interp *volatile interp;
00824     TriggerData *trigdata = (TriggerData *) fcinfo->context;
00825     char       *stroid;
00826     TupleDesc   tupdesc;
00827     volatile HeapTuple rettup;
00828     Tcl_DString tcl_cmd;
00829     Tcl_DString tcl_trigtup;
00830     Tcl_DString tcl_newtup;
00831     int         tcl_rc;
00832     int         i;
00833     int        *modattrs;
00834     Datum      *modvalues;
00835     char       *modnulls;
00836     int         ret_numvals;
00837     CONST84 char *result;
00838     CONST84 char **ret_values;
00839 
00840     /* Connect to SPI manager */
00841     if (SPI_connect() != SPI_OK_CONNECT)
00842         elog(ERROR, "could not connect to SPI manager");
00843 
00844     /* Find or compile the function */
00845     prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
00846                                      RelationGetRelid(trigdata->tg_relation),
00847                                      pltrusted);
00848 
00849     pltcl_current_prodesc = prodesc;
00850 
00851     interp = prodesc->interp_desc->interp;
00852 
00853     tupdesc = trigdata->tg_relation->rd_att;
00854 
00855     /************************************************************
00856      * Create the tcl command to call the internal
00857      * proc in the interpreter
00858      ************************************************************/
00859     Tcl_DStringInit(&tcl_cmd);
00860     Tcl_DStringInit(&tcl_trigtup);
00861     Tcl_DStringInit(&tcl_newtup);
00862     PG_TRY();
00863     {
00864         /* The procedure name */
00865         Tcl_DStringAppendElement(&tcl_cmd, prodesc->internal_proname);
00866 
00867         /* The trigger name for argument TG_name */
00868         Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
00869 
00870         /* The oid of the trigger relation for argument TG_relid */
00871         stroid = DatumGetCString(DirectFunctionCall1(oidout,
00872                             ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
00873         Tcl_DStringAppendElement(&tcl_cmd, stroid);
00874         pfree(stroid);
00875 
00876         /* The name of the table the trigger is acting on: TG_table_name */
00877         stroid = SPI_getrelname(trigdata->tg_relation);
00878         Tcl_DStringAppendElement(&tcl_cmd, stroid);
00879         pfree(stroid);
00880 
00881         /* The schema of the table the trigger is acting on: TG_table_schema */
00882         stroid = SPI_getnspname(trigdata->tg_relation);
00883         Tcl_DStringAppendElement(&tcl_cmd, stroid);
00884         pfree(stroid);
00885 
00886         /* A list of attribute names for argument TG_relatts */
00887         Tcl_DStringAppendElement(&tcl_trigtup, "");
00888         for (i = 0; i < tupdesc->natts; i++)
00889         {
00890             if (tupdesc->attrs[i]->attisdropped)
00891                 Tcl_DStringAppendElement(&tcl_trigtup, "");
00892             else
00893                 Tcl_DStringAppendElement(&tcl_trigtup,
00894                                          NameStr(tupdesc->attrs[i]->attname));
00895         }
00896         Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00897         Tcl_DStringFree(&tcl_trigtup);
00898         Tcl_DStringInit(&tcl_trigtup);
00899 
00900         /* The when part of the event for TG_when */
00901         if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00902             Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
00903         else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
00904             Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
00905         else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
00906             Tcl_DStringAppendElement(&tcl_cmd, "INSTEAD OF");
00907         else
00908             elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
00909 
00910         /* The level part of the event for TG_level */
00911         if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00912         {
00913             Tcl_DStringAppendElement(&tcl_cmd, "ROW");
00914 
00915             /* Build the data list for the trigtuple */
00916             pltcl_build_tuple_argument(trigdata->tg_trigtuple,
00917                                        tupdesc, &tcl_trigtup);
00918 
00919             /*
00920              * Now the command part of the event for TG_op and data for NEW
00921              * and OLD
00922              */
00923             if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00924             {
00925                 Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
00926 
00927                 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00928                 Tcl_DStringAppendElement(&tcl_cmd, "");
00929 
00930                 rettup = trigdata->tg_trigtuple;
00931             }
00932             else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
00933             {
00934                 Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
00935 
00936                 Tcl_DStringAppendElement(&tcl_cmd, "");
00937                 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00938 
00939                 rettup = trigdata->tg_trigtuple;
00940             }
00941             else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00942             {
00943                 Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
00944 
00945                 pltcl_build_tuple_argument(trigdata->tg_newtuple,
00946                                            tupdesc, &tcl_newtup);
00947 
00948                 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
00949                 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
00950 
00951                 rettup = trigdata->tg_newtuple;
00952             }
00953             else
00954                 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
00955         }
00956         else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
00957         {
00958             Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");
00959 
00960             if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00961                 Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
00962             else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
00963                 Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
00964             else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00965                 Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
00966             else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
00967                 Tcl_DStringAppendElement(&tcl_cmd, "TRUNCATE");
00968             else
00969                 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
00970 
00971             Tcl_DStringAppendElement(&tcl_cmd, "");
00972             Tcl_DStringAppendElement(&tcl_cmd, "");
00973 
00974             rettup = (HeapTuple) NULL;
00975         }
00976         else
00977             elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
00978 
00979         /* Finally append the arguments from CREATE TRIGGER */
00980         for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
00981             Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
00982 
00983     }
00984     PG_CATCH();
00985     {
00986         Tcl_DStringFree(&tcl_cmd);
00987         Tcl_DStringFree(&tcl_trigtup);
00988         Tcl_DStringFree(&tcl_newtup);
00989         PG_RE_THROW();
00990     }
00991     PG_END_TRY();
00992     Tcl_DStringFree(&tcl_trigtup);
00993     Tcl_DStringFree(&tcl_newtup);
00994 
00995     /************************************************************
00996      * Call the Tcl function
00997      *
00998      * We assume no PG error can be thrown directly from this call.
00999      ************************************************************/
01000     tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
01001     Tcl_DStringFree(&tcl_cmd);
01002 
01003     /************************************************************
01004      * Check for errors reported by Tcl.
01005      ************************************************************/
01006     if (tcl_rc != TCL_OK)
01007         throw_tcl_error(interp, prodesc->user_proname);
01008 
01009     /************************************************************
01010      * The return value from the procedure might be one of
01011      * the magic strings OK or SKIP or a list from array get.
01012      * We can check for OK or SKIP without worrying about encoding.
01013      ************************************************************/
01014     if (SPI_finish() != SPI_OK_FINISH)
01015         elog(ERROR, "SPI_finish() failed");
01016 
01017     result = Tcl_GetStringResult(interp);
01018 
01019     if (strcmp(result, "OK") == 0)
01020         return rettup;
01021     if (strcmp(result, "SKIP") == 0)
01022         return (HeapTuple) NULL;
01023 
01024     /************************************************************
01025      * Convert the result value from the Tcl interpreter
01026      * and setup structures for SPI_modifytuple();
01027      ************************************************************/
01028     if (Tcl_SplitList(interp, result,
01029                       &ret_numvals, &ret_values) != TCL_OK)
01030     {
01031         UTF_BEGIN;
01032         elog(ERROR, "could not split return value from trigger: %s",
01033              UTF_U2E(Tcl_GetStringResult(interp)));
01034         UTF_END;
01035     }
01036 
01037     /* Use a TRY to ensure ret_values will get freed */
01038     PG_TRY();
01039     {
01040         if (ret_numvals % 2 != 0)
01041             elog(ERROR, "invalid return list from trigger - must have even # of elements");
01042 
01043         modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
01044         modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
01045         for (i = 0; i < tupdesc->natts; i++)
01046         {
01047             modattrs[i] = i + 1;
01048             modvalues[i] = (Datum) NULL;
01049         }
01050 
01051         modnulls = palloc(tupdesc->natts);
01052         memset(modnulls, 'n', tupdesc->natts);
01053 
01054         for (i = 0; i < ret_numvals; i += 2)
01055         {
01056             CONST84 char *ret_name = ret_values[i];
01057             CONST84 char *ret_value = ret_values[i + 1];
01058             int         attnum;
01059             HeapTuple   typeTup;
01060             Oid         typinput;
01061             Oid         typioparam;
01062             FmgrInfo    finfo;
01063 
01064             /************************************************************
01065              * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
01066              ************************************************************/
01067             if (strcmp(ret_name, ".tupno") == 0)
01068                 continue;
01069 
01070             /************************************************************
01071              * Get the attribute number
01072              ************************************************************/
01073             attnum = SPI_fnumber(tupdesc, ret_name);
01074             if (attnum == SPI_ERROR_NOATTRIBUTE)
01075                 elog(ERROR, "invalid attribute \"%s\"", ret_name);
01076             if (attnum <= 0)
01077                 elog(ERROR, "cannot set system attribute \"%s\"", ret_name);
01078 
01079             /************************************************************
01080              * Ignore dropped columns
01081              ************************************************************/
01082             if (tupdesc->attrs[attnum - 1]->attisdropped)
01083                 continue;
01084 
01085             /************************************************************
01086              * Lookup the attribute type in the syscache
01087              * for the input function
01088              ************************************************************/
01089             typeTup = SearchSysCache1(TYPEOID,
01090                      ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid));
01091             if (!HeapTupleIsValid(typeTup))
01092                 elog(ERROR, "cache lookup failed for type %u",
01093                      tupdesc->attrs[attnum - 1]->atttypid);
01094             typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
01095             typioparam = getTypeIOParam(typeTup);
01096             ReleaseSysCache(typeTup);
01097 
01098             /************************************************************
01099              * Set the attribute to NOT NULL and convert the contents
01100              ************************************************************/
01101             modnulls[attnum - 1] = ' ';
01102             fmgr_info(typinput, &finfo);
01103             UTF_BEGIN;
01104             modvalues[attnum - 1] = InputFunctionCall(&finfo,
01105                                                  (char *) UTF_U2E(ret_value),
01106                                                       typioparam,
01107                                       tupdesc->attrs[attnum - 1]->atttypmod);
01108             UTF_END;
01109         }
01110 
01111         rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
01112                                  modattrs, modvalues, modnulls);
01113 
01114         pfree(modattrs);
01115         pfree(modvalues);
01116         pfree(modnulls);
01117 
01118         if (rettup == NULL)
01119             elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
01120 
01121     }
01122     PG_CATCH();
01123     {
01124         ckfree((char *) ret_values);
01125         PG_RE_THROW();
01126     }
01127     PG_END_TRY();
01128     ckfree((char *) ret_values);
01129 
01130     return rettup;
01131 }
01132 
01133 
01134 /**********************************************************************
01135  * throw_tcl_error  - ereport an error returned from the Tcl interpreter
01136  **********************************************************************/
01137 static void
01138 throw_tcl_error(Tcl_Interp *interp, const char *proname)
01139 {
01140     /*
01141      * Caution is needed here because Tcl_GetVar could overwrite the
01142      * interpreter result (even though it's not really supposed to), and we
01143      * can't control the order of evaluation of ereport arguments. Hence, make
01144      * real sure we have our own copy of the result string before invoking
01145      * Tcl_GetVar.
01146      */
01147     char       *emsg;
01148     char       *econtext;
01149 
01150     UTF_BEGIN;
01151     emsg = pstrdup(UTF_U2E(Tcl_GetStringResult(interp)));
01152     UTF_END;
01153     UTF_BEGIN;
01154     econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
01155                                            TCL_GLOBAL_ONLY));
01156     ereport(ERROR,
01157             (errmsg("%s", emsg),
01158              errcontext("%s\nin PL/Tcl function \"%s\"",
01159                         econtext, proname)));
01160     UTF_END;
01161 }
01162 
01163 
01164 /**********************************************************************
01165  * compile_pltcl_function   - compile (or hopefully just look up) function
01166  *
01167  * tgreloid is the OID of the relation when compiling a trigger, or zero
01168  * (InvalidOid) when compiling a plain function.
01169  **********************************************************************/
01170 static pltcl_proc_desc *
01171 compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
01172 {
01173     HeapTuple   procTup;
01174     Form_pg_proc procStruct;
01175     pltcl_proc_key proc_key;
01176     pltcl_proc_ptr *proc_ptr;
01177     bool        found;
01178     pltcl_proc_desc *prodesc;
01179 
01180     /* We'll need the pg_proc tuple in any case... */
01181     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
01182     if (!HeapTupleIsValid(procTup))
01183         elog(ERROR, "cache lookup failed for function %u", fn_oid);
01184     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
01185 
01186     /* Try to find function in pltcl_proc_htab */
01187     proc_key.proc_id = fn_oid;
01188     proc_key.is_trigger = OidIsValid(tgreloid);
01189     proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
01190 
01191     proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
01192                            HASH_ENTER,
01193                            &found);
01194     if (!found)
01195         proc_ptr->proc_ptr = NULL;
01196 
01197     prodesc = proc_ptr->proc_ptr;
01198 
01199     /************************************************************
01200      * If it's present, must check whether it's still up to date.
01201      * This is needed because CREATE OR REPLACE FUNCTION can modify the
01202      * function's pg_proc entry without changing its OID.
01203      ************************************************************/
01204     if (prodesc != NULL)
01205     {
01206         bool        uptodate;
01207 
01208         uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
01209                     ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
01210 
01211         if (!uptodate)
01212         {
01213             proc_ptr->proc_ptr = NULL;
01214             prodesc = NULL;
01215         }
01216     }
01217 
01218     /************************************************************
01219      * If we haven't found it in the hashtable, we analyze
01220      * the functions arguments and returntype and store
01221      * the in-/out-functions in the prodesc block and create
01222      * a new hashtable entry for it.
01223      *
01224      * Then we load the procedure into the Tcl interpreter.
01225      ************************************************************/
01226     if (prodesc == NULL)
01227     {
01228         bool        is_trigger = OidIsValid(tgreloid);
01229         char        internal_proname[128];
01230         HeapTuple   typeTup;
01231         Form_pg_type typeStruct;
01232         Tcl_DString proc_internal_def;
01233         Tcl_DString proc_internal_body;
01234         char        proc_internal_args[33 * FUNC_MAX_ARGS];
01235         Datum       prosrcdatum;
01236         bool        isnull;
01237         char       *proc_source;
01238         char        buf[32];
01239         Tcl_Interp *interp;
01240         int         i;
01241         int         tcl_rc;
01242 
01243         /************************************************************
01244          * Build our internal proc name from the function's Oid.  Append
01245          * "_trigger" when appropriate to ensure the normal and trigger
01246          * cases are kept separate.
01247          ************************************************************/
01248         if (!is_trigger)
01249             snprintf(internal_proname, sizeof(internal_proname),
01250                      "__PLTcl_proc_%u", fn_oid);
01251         else
01252             snprintf(internal_proname, sizeof(internal_proname),
01253                      "__PLTcl_proc_%u_trigger", fn_oid);
01254 
01255         /************************************************************
01256          * Allocate a new procedure description block
01257          ************************************************************/
01258         prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
01259         if (prodesc == NULL)
01260             ereport(ERROR,
01261                     (errcode(ERRCODE_OUT_OF_MEMORY),
01262                      errmsg("out of memory")));
01263         MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
01264         prodesc->user_proname = strdup(NameStr(procStruct->proname));
01265         prodesc->internal_proname = strdup(internal_proname);
01266         if (prodesc->user_proname == NULL || prodesc->internal_proname == NULL)
01267             ereport(ERROR,
01268                     (errcode(ERRCODE_OUT_OF_MEMORY),
01269                      errmsg("out of memory")));
01270         prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
01271         prodesc->fn_tid = procTup->t_self;
01272 
01273         /* Remember if function is STABLE/IMMUTABLE */
01274         prodesc->fn_readonly =
01275             (procStruct->provolatile != PROVOLATILE_VOLATILE);
01276         /* And whether it is trusted */
01277         prodesc->lanpltrusted = pltrusted;
01278 
01279         /************************************************************
01280          * Identify the interpreter to use for the function
01281          ************************************************************/
01282         prodesc->interp_desc = pltcl_fetch_interp(prodesc->lanpltrusted);
01283         interp = prodesc->interp_desc->interp;
01284 
01285         /************************************************************
01286          * Get the required information for input conversion of the
01287          * return value.
01288          ************************************************************/
01289         if (!is_trigger)
01290         {
01291             typeTup =
01292                 SearchSysCache1(TYPEOID,
01293                                 ObjectIdGetDatum(procStruct->prorettype));
01294             if (!HeapTupleIsValid(typeTup))
01295             {
01296                 free(prodesc->user_proname);
01297                 free(prodesc->internal_proname);
01298                 free(prodesc);
01299                 elog(ERROR, "cache lookup failed for type %u",
01300                      procStruct->prorettype);
01301             }
01302             typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
01303 
01304             /* Disallow pseudotype result, except VOID */
01305             if (typeStruct->typtype == TYPTYPE_PSEUDO)
01306             {
01307                 if (procStruct->prorettype == VOIDOID)
01308                      /* okay */ ;
01309                 else if (procStruct->prorettype == TRIGGEROID)
01310                 {
01311                     free(prodesc->user_proname);
01312                     free(prodesc->internal_proname);
01313                     free(prodesc);
01314                     ereport(ERROR,
01315                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01316                              errmsg("trigger functions can only be called as triggers")));
01317                 }
01318                 else
01319                 {
01320                     free(prodesc->user_proname);
01321                     free(prodesc->internal_proname);
01322                     free(prodesc);
01323                     ereport(ERROR,
01324                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01325                              errmsg("PL/Tcl functions cannot return type %s",
01326                                     format_type_be(procStruct->prorettype))));
01327                 }
01328             }
01329 
01330             if (typeStruct->typtype == TYPTYPE_COMPOSITE)
01331             {
01332                 free(prodesc->user_proname);
01333                 free(prodesc->internal_proname);
01334                 free(prodesc);
01335                 ereport(ERROR,
01336                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01337                   errmsg("PL/Tcl functions cannot return composite types")));
01338             }
01339 
01340             perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
01341             prodesc->result_typioparam = getTypeIOParam(typeTup);
01342 
01343             ReleaseSysCache(typeTup);
01344         }
01345 
01346         /************************************************************
01347          * Get the required information for output conversion
01348          * of all procedure arguments
01349          ************************************************************/
01350         if (!is_trigger)
01351         {
01352             prodesc->nargs = procStruct->pronargs;
01353             proc_internal_args[0] = '\0';
01354             for (i = 0; i < prodesc->nargs; i++)
01355             {
01356                 typeTup = SearchSysCache1(TYPEOID,
01357                         ObjectIdGetDatum(procStruct->proargtypes.values[i]));
01358                 if (!HeapTupleIsValid(typeTup))
01359                 {
01360                     free(prodesc->user_proname);
01361                     free(prodesc->internal_proname);
01362                     free(prodesc);
01363                     elog(ERROR, "cache lookup failed for type %u",
01364                          procStruct->proargtypes.values[i]);
01365                 }
01366                 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
01367 
01368                 /* Disallow pseudotype argument */
01369                 if (typeStruct->typtype == TYPTYPE_PSEUDO)
01370                 {
01371                     free(prodesc->user_proname);
01372                     free(prodesc->internal_proname);
01373                     free(prodesc);
01374                     ereport(ERROR,
01375                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01376                              errmsg("PL/Tcl functions cannot accept type %s",
01377                         format_type_be(procStruct->proargtypes.values[i]))));
01378                 }
01379 
01380                 if (typeStruct->typtype == TYPTYPE_COMPOSITE)
01381                 {
01382                     prodesc->arg_is_rowtype[i] = true;
01383                     snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
01384                 }
01385                 else
01386                 {
01387                     prodesc->arg_is_rowtype[i] = false;
01388                     perm_fmgr_info(typeStruct->typoutput,
01389                                    &(prodesc->arg_out_func[i]));
01390                     snprintf(buf, sizeof(buf), "%d", i + 1);
01391                 }
01392 
01393                 if (i > 0)
01394                     strcat(proc_internal_args, " ");
01395                 strcat(proc_internal_args, buf);
01396 
01397                 ReleaseSysCache(typeTup);
01398             }
01399         }
01400         else
01401         {
01402             /* trigger procedure has fixed args */
01403             strcpy(proc_internal_args,
01404                    "TG_name TG_relid TG_table_name TG_table_schema TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
01405         }
01406 
01407         /************************************************************
01408          * Create the tcl command to define the internal
01409          * procedure
01410          ************************************************************/
01411         Tcl_DStringInit(&proc_internal_def);
01412         Tcl_DStringInit(&proc_internal_body);
01413         Tcl_DStringAppendElement(&proc_internal_def, "proc");
01414         Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
01415         Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
01416 
01417         /************************************************************
01418          * prefix procedure body with
01419          * upvar #0 <internal_procname> GD
01420          * and with appropriate setting of arguments
01421          ************************************************************/
01422         Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
01423         Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
01424         Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
01425         if (!is_trigger)
01426         {
01427             for (i = 0; i < prodesc->nargs; i++)
01428             {
01429                 if (prodesc->arg_is_rowtype[i])
01430                 {
01431                     snprintf(buf, sizeof(buf),
01432                              "array set %d $__PLTcl_Tup_%d\n",
01433                              i + 1, i + 1);
01434                     Tcl_DStringAppend(&proc_internal_body, buf, -1);
01435                 }
01436             }
01437         }
01438         else
01439         {
01440             Tcl_DStringAppend(&proc_internal_body,
01441                               "array set NEW $__PLTcl_Tup_NEW\n", -1);
01442             Tcl_DStringAppend(&proc_internal_body,
01443                               "array set OLD $__PLTcl_Tup_OLD\n", -1);
01444 
01445             Tcl_DStringAppend(&proc_internal_body,
01446                               "set i 0\n"
01447                               "set v 0\n"
01448                               "foreach v $args {\n"
01449                               "  incr i\n"
01450                               "  set $i $v\n"
01451                               "}\n"
01452                               "unset i v\n\n", -1);
01453         }
01454 
01455         /************************************************************
01456          * Add user's function definition to proc body
01457          ************************************************************/
01458         prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
01459                                       Anum_pg_proc_prosrc, &isnull);
01460         if (isnull)
01461             elog(ERROR, "null prosrc");
01462         proc_source = TextDatumGetCString(prosrcdatum);
01463         UTF_BEGIN;
01464         Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
01465         UTF_END;
01466         pfree(proc_source);
01467         Tcl_DStringAppendElement(&proc_internal_def,
01468                                  Tcl_DStringValue(&proc_internal_body));
01469         Tcl_DStringFree(&proc_internal_body);
01470 
01471         /************************************************************
01472          * Create the procedure in the interpreter
01473          ************************************************************/
01474         tcl_rc = Tcl_GlobalEval(interp,
01475                                 Tcl_DStringValue(&proc_internal_def));
01476         Tcl_DStringFree(&proc_internal_def);
01477         if (tcl_rc != TCL_OK)
01478         {
01479             free(prodesc->user_proname);
01480             free(prodesc->internal_proname);
01481             free(prodesc);
01482             UTF_BEGIN;
01483             elog(ERROR, "could not create internal procedure \"%s\": %s",
01484                  internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
01485             UTF_END;
01486         }
01487 
01488         /************************************************************
01489          * Add the proc description block to the hashtable.  Note we do not
01490          * attempt to free any previously existing prodesc block.  This is
01491          * annoying, but necessary since there could be active calls using
01492          * the old prodesc.
01493          ************************************************************/
01494         proc_ptr->proc_ptr = prodesc;
01495     }
01496 
01497     ReleaseSysCache(procTup);
01498 
01499     return prodesc;
01500 }
01501 
01502 
01503 /**********************************************************************
01504  * pltcl_elog()     - elog() support for PLTcl
01505  **********************************************************************/
01506 static int
01507 pltcl_elog(ClientData cdata, Tcl_Interp *interp,
01508            int argc, CONST84 char *argv[])
01509 {
01510     volatile int level;
01511     MemoryContext oldcontext;
01512 
01513     if (argc != 3)
01514     {
01515         Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
01516         return TCL_ERROR;
01517     }
01518 
01519     if (strcmp(argv[1], "DEBUG") == 0)
01520         level = DEBUG2;
01521     else if (strcmp(argv[1], "LOG") == 0)
01522         level = LOG;
01523     else if (strcmp(argv[1], "INFO") == 0)
01524         level = INFO;
01525     else if (strcmp(argv[1], "NOTICE") == 0)
01526         level = NOTICE;
01527     else if (strcmp(argv[1], "WARNING") == 0)
01528         level = WARNING;
01529     else if (strcmp(argv[1], "ERROR") == 0)
01530         level = ERROR;
01531     else if (strcmp(argv[1], "FATAL") == 0)
01532         level = FATAL;
01533     else
01534     {
01535         Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
01536                          "'", NULL);
01537         return TCL_ERROR;
01538     }
01539 
01540     if (level == ERROR)
01541     {
01542         /*
01543          * We just pass the error back to Tcl.  If it's not caught, it'll
01544          * eventually get converted to a PG error when we reach the call
01545          * handler.
01546          */
01547         Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
01548         return TCL_ERROR;
01549     }
01550 
01551     /*
01552      * For non-error messages, just pass 'em to elog().  We do not expect that
01553      * this will fail, but just on the off chance it does, report the error
01554      * back to Tcl.  Note we are assuming that elog() can't have any internal
01555      * failures that are so bad as to require a transaction abort.
01556      *
01557      * This path is also used for FATAL errors, which aren't going to come
01558      * back to us at all.
01559      */
01560     oldcontext = CurrentMemoryContext;
01561     PG_TRY();
01562     {
01563         UTF_BEGIN;
01564         elog(level, "%s", UTF_U2E(argv[2]));
01565         UTF_END;
01566     }
01567     PG_CATCH();
01568     {
01569         ErrorData  *edata;
01570 
01571         /* Must reset elog.c's state */
01572         MemoryContextSwitchTo(oldcontext);
01573         edata = CopyErrorData();
01574         FlushErrorState();
01575 
01576         /* Pass the error message to Tcl */
01577         UTF_BEGIN;
01578         Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
01579         UTF_END;
01580         FreeErrorData(edata);
01581 
01582         return TCL_ERROR;
01583     }
01584     PG_END_TRY();
01585 
01586     return TCL_OK;
01587 }
01588 
01589 
01590 /**********************************************************************
01591  * pltcl_quote()    - quote literal strings that are to
01592  *            be used in SPI_execute query strings
01593  **********************************************************************/
01594 static int
01595 pltcl_quote(ClientData cdata, Tcl_Interp *interp,
01596             int argc, CONST84 char *argv[])
01597 {
01598     char       *tmp;
01599     const char *cp1;
01600     char       *cp2;
01601 
01602     /************************************************************
01603      * Check call syntax
01604      ************************************************************/
01605     if (argc != 2)
01606     {
01607         Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
01608         return TCL_ERROR;
01609     }
01610 
01611     /************************************************************
01612      * Allocate space for the maximum the string can
01613      * grow to and initialize pointers
01614      ************************************************************/
01615     tmp = palloc(strlen(argv[1]) * 2 + 1);
01616     cp1 = argv[1];
01617     cp2 = tmp;
01618 
01619     /************************************************************
01620      * Walk through string and double every quote and backslash
01621      ************************************************************/
01622     while (*cp1)
01623     {
01624         if (*cp1 == '\'')
01625             *cp2++ = '\'';
01626         else
01627         {
01628             if (*cp1 == '\\')
01629                 *cp2++ = '\\';
01630         }
01631         *cp2++ = *cp1++;
01632     }
01633 
01634     /************************************************************
01635      * Terminate the string and set it as result
01636      ************************************************************/
01637     *cp2 = '\0';
01638     Tcl_SetResult(interp, tmp, TCL_VOLATILE);
01639     pfree(tmp);
01640     return TCL_OK;
01641 }
01642 
01643 
01644 /**********************************************************************
01645  * pltcl_argisnull()    - determine if a specific argument is NULL
01646  **********************************************************************/
01647 static int
01648 pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
01649                 int argc, CONST84 char *argv[])
01650 {
01651     int         argno;
01652     FunctionCallInfo fcinfo = pltcl_current_fcinfo;
01653 
01654     /************************************************************
01655      * Check call syntax
01656      ************************************************************/
01657     if (argc != 2)
01658     {
01659         Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
01660                       TCL_STATIC);
01661         return TCL_ERROR;
01662     }
01663 
01664     /************************************************************
01665      * Check that we're called as a normal function
01666      ************************************************************/
01667     if (fcinfo == NULL)
01668     {
01669         Tcl_SetResult(interp, "argisnull cannot be used in triggers",
01670                       TCL_STATIC);
01671         return TCL_ERROR;
01672     }
01673 
01674     /************************************************************
01675      * Get the argument number
01676      ************************************************************/
01677     if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
01678         return TCL_ERROR;
01679 
01680     /************************************************************
01681      * Check that the argno is valid
01682      ************************************************************/
01683     argno--;
01684     if (argno < 0 || argno >= fcinfo->nargs)
01685     {
01686         Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
01687         return TCL_ERROR;
01688     }
01689 
01690     /************************************************************
01691      * Get the requested NULL state
01692      ************************************************************/
01693     if (PG_ARGISNULL(argno))
01694         Tcl_SetResult(interp, "1", TCL_STATIC);
01695     else
01696         Tcl_SetResult(interp, "0", TCL_STATIC);
01697 
01698     return TCL_OK;
01699 }
01700 
01701 
01702 /**********************************************************************
01703  * pltcl_returnnull()   - Cause a NULL return from a function
01704  **********************************************************************/
01705 static int
01706 pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
01707                  int argc, CONST84 char *argv[])
01708 {
01709     FunctionCallInfo fcinfo = pltcl_current_fcinfo;
01710 
01711     /************************************************************
01712      * Check call syntax
01713      ************************************************************/
01714     if (argc != 1)
01715     {
01716         Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
01717         return TCL_ERROR;
01718     }
01719 
01720     /************************************************************
01721      * Check that we're called as a normal function
01722      ************************************************************/
01723     if (fcinfo == NULL)
01724     {
01725         Tcl_SetResult(interp, "return_null cannot be used in triggers",
01726                       TCL_STATIC);
01727         return TCL_ERROR;
01728     }
01729 
01730     /************************************************************
01731      * Set the NULL return flag and cause Tcl to return from the
01732      * procedure.
01733      ************************************************************/
01734     fcinfo->isnull = true;
01735 
01736     return TCL_RETURN;
01737 }
01738 
01739 
01740 /*----------
01741  * Support for running SPI operations inside subtransactions
01742  *
01743  * Intended usage pattern is:
01744  *
01745  *  MemoryContext oldcontext = CurrentMemoryContext;
01746  *  ResourceOwner oldowner = CurrentResourceOwner;
01747  *
01748  *  ...
01749  *  pltcl_subtrans_begin(oldcontext, oldowner);
01750  *  PG_TRY();
01751  *  {
01752  *      do something risky;
01753  *      pltcl_subtrans_commit(oldcontext, oldowner);
01754  *  }
01755  *  PG_CATCH();
01756  *  {
01757  *      pltcl_subtrans_abort(interp, oldcontext, oldowner);
01758  *      return TCL_ERROR;
01759  *  }
01760  *  PG_END_TRY();
01761  *  return TCL_OK;
01762  *----------
01763  */
01764 static void
01765 pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
01766 {
01767     BeginInternalSubTransaction(NULL);
01768 
01769     /* Want to run inside function's memory context */
01770     MemoryContextSwitchTo(oldcontext);
01771 }
01772 
01773 static void
01774 pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
01775 {
01776     /* Commit the inner transaction, return to outer xact context */
01777     ReleaseCurrentSubTransaction();
01778     MemoryContextSwitchTo(oldcontext);
01779     CurrentResourceOwner = oldowner;
01780 
01781     /*
01782      * AtEOSubXact_SPI() should not have popped any SPI context, but just in
01783      * case it did, make sure we remain connected.
01784      */
01785     SPI_restore_connection();
01786 }
01787 
01788 static void
01789 pltcl_subtrans_abort(Tcl_Interp *interp,
01790                      MemoryContext oldcontext, ResourceOwner oldowner)
01791 {
01792     ErrorData  *edata;
01793 
01794     /* Save error info */
01795     MemoryContextSwitchTo(oldcontext);
01796     edata = CopyErrorData();
01797     FlushErrorState();
01798 
01799     /* Abort the inner transaction */
01800     RollbackAndReleaseCurrentSubTransaction();
01801     MemoryContextSwitchTo(oldcontext);
01802     CurrentResourceOwner = oldowner;
01803 
01804     /*
01805      * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
01806      * have left us in a disconnected state.  We need this hack to return to
01807      * connected state.
01808      */
01809     SPI_restore_connection();
01810 
01811     /* Pass the error message to Tcl */
01812     UTF_BEGIN;
01813     Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
01814     UTF_END;
01815     FreeErrorData(edata);
01816 }
01817 
01818 
01819 /**********************************************************************
01820  * pltcl_SPI_execute()      - The builtin SPI_execute command
01821  *                for the Tcl interpreter
01822  **********************************************************************/
01823 static int
01824 pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
01825                   int argc, CONST84 char *argv[])
01826 {
01827     int         my_rc;
01828     int         spi_rc;
01829     int         query_idx;
01830     int         i;
01831     int         count = 0;
01832     CONST84 char *volatile arrayname = NULL;
01833     CONST84 char *volatile loop_body = NULL;
01834     MemoryContext oldcontext = CurrentMemoryContext;
01835     ResourceOwner oldowner = CurrentResourceOwner;
01836 
01837     char       *usage = "syntax error - 'SPI_exec "
01838     "?-count n? "
01839     "?-array name? query ?loop body?";
01840 
01841     /************************************************************
01842      * Check the call syntax and get the options
01843      ************************************************************/
01844     if (argc < 2)
01845     {
01846         Tcl_SetResult(interp, usage, TCL_STATIC);
01847         return TCL_ERROR;
01848     }
01849 
01850     i = 1;
01851     while (i < argc)
01852     {
01853         if (strcmp(argv[i], "-array") == 0)
01854         {
01855             if (++i >= argc)
01856             {
01857                 Tcl_SetResult(interp, usage, TCL_STATIC);
01858                 return TCL_ERROR;
01859             }
01860             arrayname = argv[i++];
01861             continue;
01862         }
01863 
01864         if (strcmp(argv[i], "-count") == 0)
01865         {
01866             if (++i >= argc)
01867             {
01868                 Tcl_SetResult(interp, usage, TCL_STATIC);
01869                 return TCL_ERROR;
01870             }
01871             if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
01872                 return TCL_ERROR;
01873             continue;
01874         }
01875 
01876         break;
01877     }
01878 
01879     query_idx = i;
01880     if (query_idx >= argc || query_idx + 2 < argc)
01881     {
01882         Tcl_SetResult(interp, usage, TCL_STATIC);
01883         return TCL_ERROR;
01884     }
01885     if (query_idx + 1 < argc)
01886         loop_body = argv[query_idx + 1];
01887 
01888     /************************************************************
01889      * Execute the query inside a sub-transaction, so we can cope with
01890      * errors sanely
01891      ************************************************************/
01892 
01893     pltcl_subtrans_begin(oldcontext, oldowner);
01894 
01895     PG_TRY();
01896     {
01897         UTF_BEGIN;
01898         spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
01899                              pltcl_current_prodesc->fn_readonly, count);
01900         UTF_END;
01901 
01902         my_rc = pltcl_process_SPI_result(interp,
01903                                          arrayname,
01904                                          loop_body,
01905                                          spi_rc,
01906                                          SPI_tuptable,
01907                                          SPI_processed);
01908 
01909         pltcl_subtrans_commit(oldcontext, oldowner);
01910     }
01911     PG_CATCH();
01912     {
01913         pltcl_subtrans_abort(interp, oldcontext, oldowner);
01914         return TCL_ERROR;
01915     }
01916     PG_END_TRY();
01917 
01918     return my_rc;
01919 }
01920 
01921 /*
01922  * Process the result from SPI_execute or SPI_execute_plan
01923  *
01924  * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
01925  */
01926 static int
01927 pltcl_process_SPI_result(Tcl_Interp *interp,
01928                          CONST84 char *arrayname,
01929                          CONST84 char *loop_body,
01930                          int spi_rc,
01931                          SPITupleTable *tuptable,
01932                          int ntuples)
01933 {
01934     int         my_rc = TCL_OK;
01935     char        buf[64];
01936     int         i;
01937     int         loop_rc;
01938     HeapTuple  *tuples;
01939     TupleDesc   tupdesc;
01940 
01941     switch (spi_rc)
01942     {
01943         case SPI_OK_SELINTO:
01944         case SPI_OK_INSERT:
01945         case SPI_OK_DELETE:
01946         case SPI_OK_UPDATE:
01947             snprintf(buf, sizeof(buf), "%d", ntuples);
01948             Tcl_SetResult(interp, buf, TCL_VOLATILE);
01949             break;
01950 
01951         case SPI_OK_UTILITY:
01952         case SPI_OK_REWRITTEN:
01953             if (tuptable == NULL)
01954             {
01955                 Tcl_SetResult(interp, "0", TCL_STATIC);
01956                 break;
01957             }
01958             /* FALL THRU for utility returning tuples */
01959 
01960         case SPI_OK_SELECT:
01961         case SPI_OK_INSERT_RETURNING:
01962         case SPI_OK_DELETE_RETURNING:
01963         case SPI_OK_UPDATE_RETURNING:
01964 
01965             /*
01966              * Process the tuples we got
01967              */
01968             tuples = tuptable->vals;
01969             tupdesc = tuptable->tupdesc;
01970 
01971             if (loop_body == NULL)
01972             {
01973                 /*
01974                  * If there is no loop body given, just set the variables from
01975                  * the first tuple (if any)
01976                  */
01977                 if (ntuples > 0)
01978                     pltcl_set_tuple_values(interp, arrayname, 0,
01979                                            tuples[0], tupdesc);
01980             }
01981             else
01982             {
01983                 /*
01984                  * There is a loop body - process all tuples and evaluate the
01985                  * body on each
01986                  */
01987                 for (i = 0; i < ntuples; i++)
01988                 {
01989                     pltcl_set_tuple_values(interp, arrayname, i,
01990                                            tuples[i], tupdesc);
01991 
01992                     loop_rc = Tcl_Eval(interp, loop_body);
01993 
01994                     if (loop_rc == TCL_OK)
01995                         continue;
01996                     if (loop_rc == TCL_CONTINUE)
01997                         continue;
01998                     if (loop_rc == TCL_RETURN)
01999                     {
02000                         my_rc = TCL_RETURN;
02001                         break;
02002                     }
02003                     if (loop_rc == TCL_BREAK)
02004                         break;
02005                     my_rc = TCL_ERROR;
02006                     break;
02007                 }
02008             }
02009 
02010             if (my_rc == TCL_OK)
02011             {
02012                 snprintf(buf, sizeof(buf), "%d", ntuples);
02013                 Tcl_SetResult(interp, buf, TCL_VOLATILE);
02014             }
02015             break;
02016 
02017         default:
02018             Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
02019                              SPI_result_code_string(spi_rc), NULL);
02020             my_rc = TCL_ERROR;
02021             break;
02022     }
02023 
02024     SPI_freetuptable(tuptable);
02025 
02026     return my_rc;
02027 }
02028 
02029 
02030 /**********************************************************************
02031  * pltcl_SPI_prepare()      - Builtin support for prepared plans
02032  *                The Tcl command SPI_prepare
02033  *                always saves the plan using
02034  *                SPI_keepplan and returns a key for
02035  *                access. There is no chance to prepare
02036  *                and not save the plan currently.
02037  **********************************************************************/
02038 static int
02039 pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
02040                   int argc, CONST84 char *argv[])
02041 {
02042     int         nargs;
02043     CONST84 char **args;
02044     pltcl_query_desc *qdesc;
02045     int         i;
02046     Tcl_HashEntry *hashent;
02047     int         hashnew;
02048     Tcl_HashTable *query_hash;
02049     MemoryContext oldcontext = CurrentMemoryContext;
02050     ResourceOwner oldowner = CurrentResourceOwner;
02051 
02052     /************************************************************
02053      * Check the call syntax
02054      ************************************************************/
02055     if (argc != 3)
02056     {
02057         Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
02058                       TCL_STATIC);
02059         return TCL_ERROR;
02060     }
02061 
02062     /************************************************************
02063      * Split the argument type list
02064      ************************************************************/
02065     if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
02066         return TCL_ERROR;
02067 
02068     /************************************************************
02069      * Allocate the new querydesc structure
02070      ************************************************************/
02071     qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
02072     snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
02073     qdesc->nargs = nargs;
02074     qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
02075     qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
02076     qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));
02077 
02078     /************************************************************
02079      * Execute the prepare inside a sub-transaction, so we can cope with
02080      * errors sanely
02081      ************************************************************/
02082 
02083     pltcl_subtrans_begin(oldcontext, oldowner);
02084 
02085     PG_TRY();
02086     {
02087         /************************************************************
02088          * Resolve argument type names and then look them up by oid
02089          * in the system cache, and remember the required information
02090          * for input conversion.
02091          ************************************************************/
02092         for (i = 0; i < nargs; i++)
02093         {
02094             Oid         typId,
02095                         typInput,
02096                         typIOParam;
02097             int32       typmod;
02098 
02099             parseTypeString(args[i], &typId, &typmod);
02100 
02101             getTypeInputInfo(typId, &typInput, &typIOParam);
02102 
02103             qdesc->argtypes[i] = typId;
02104             perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
02105             qdesc->argtypioparams[i] = typIOParam;
02106         }
02107 
02108         /************************************************************
02109          * Prepare the plan and check for errors
02110          ************************************************************/
02111         UTF_BEGIN;
02112         qdesc->plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
02113         UTF_END;
02114 
02115         if (qdesc->plan == NULL)
02116             elog(ERROR, "SPI_prepare() failed");
02117 
02118         /************************************************************
02119          * Save the plan into permanent memory (right now it's in the
02120          * SPI procCxt, which will go away at function end).
02121          ************************************************************/
02122         if (SPI_keepplan(qdesc->plan))
02123             elog(ERROR, "SPI_keepplan() failed");
02124 
02125         pltcl_subtrans_commit(oldcontext, oldowner);
02126     }
02127     PG_CATCH();
02128     {
02129         pltcl_subtrans_abort(interp, oldcontext, oldowner);
02130 
02131         free(qdesc->argtypes);
02132         free(qdesc->arginfuncs);
02133         free(qdesc->argtypioparams);
02134         free(qdesc);
02135         ckfree((char *) args);
02136 
02137         return TCL_ERROR;
02138     }
02139     PG_END_TRY();
02140 
02141     /************************************************************
02142      * Insert a hashtable entry for the plan and return
02143      * the key to the caller
02144      ************************************************************/
02145     query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
02146 
02147     hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
02148     Tcl_SetHashValue(hashent, (ClientData) qdesc);
02149 
02150     ckfree((char *) args);
02151 
02152     /* qname is ASCII, so no need for encoding conversion */
02153     Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
02154     return TCL_OK;
02155 }
02156 
02157 
02158 /**********************************************************************
02159  * pltcl_SPI_execute_plan()     - Execute a prepared plan
02160  **********************************************************************/
02161 static int
02162 pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
02163                        int argc, CONST84 char *argv[])
02164 {
02165     int         my_rc;
02166     int         spi_rc;
02167     int         i;
02168     int         j;
02169     Tcl_HashEntry *hashent;
02170     pltcl_query_desc *qdesc;
02171     const char *volatile nulls = NULL;
02172     CONST84 char *volatile arrayname = NULL;
02173     CONST84 char *volatile loop_body = NULL;
02174     int         count = 0;
02175     int         callnargs;
02176     CONST84 char **callargs = NULL;
02177     Datum      *argvalues;
02178     MemoryContext oldcontext = CurrentMemoryContext;
02179     ResourceOwner oldowner = CurrentResourceOwner;
02180     Tcl_HashTable *query_hash;
02181 
02182     char       *usage = "syntax error - 'SPI_execp "
02183     "?-nulls string? ?-count n? "
02184     "?-array name? query ?args? ?loop body?";
02185 
02186     /************************************************************
02187      * Get the options and check syntax
02188      ************************************************************/
02189     i = 1;
02190     while (i < argc)
02191     {
02192         if (strcmp(argv[i], "-array") == 0)
02193         {
02194             if (++i >= argc)
02195             {
02196                 Tcl_SetResult(interp, usage, TCL_STATIC);
02197                 return TCL_ERROR;
02198             }
02199             arrayname = argv[i++];
02200             continue;
02201         }
02202         if (strcmp(argv[i], "-nulls") == 0)
02203         {
02204             if (++i >= argc)
02205             {
02206                 Tcl_SetResult(interp, usage, TCL_STATIC);
02207                 return TCL_ERROR;
02208             }
02209             nulls = argv[i++];
02210             continue;
02211         }
02212         if (strcmp(argv[i], "-count") == 0)
02213         {
02214             if (++i >= argc)
02215             {
02216                 Tcl_SetResult(interp, usage, TCL_STATIC);
02217                 return TCL_ERROR;
02218             }
02219             if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
02220                 return TCL_ERROR;
02221             continue;
02222         }
02223 
02224         break;
02225     }
02226 
02227     /************************************************************
02228      * Get the prepared plan descriptor by its key
02229      ************************************************************/
02230     if (i >= argc)
02231     {
02232         Tcl_SetResult(interp, usage, TCL_STATIC);
02233         return TCL_ERROR;
02234     }
02235 
02236     query_hash = &pltcl_current_prodesc->interp_desc->query_hash;
02237 
02238     hashent = Tcl_FindHashEntry(query_hash, argv[i]);
02239     if (hashent == NULL)
02240     {
02241         Tcl_AppendResult(interp, "invalid queryid '", argv[i], "'", NULL);
02242         return TCL_ERROR;
02243     }
02244     qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
02245     i++;
02246 
02247     /************************************************************
02248      * If a nulls string is given, check for correct length
02249      ************************************************************/
02250     if (nulls != NULL)
02251     {
02252         if (strlen(nulls) != qdesc->nargs)
02253         {
02254             Tcl_SetResult(interp,
02255                        "length of nulls string doesn't match # of arguments",
02256                           TCL_STATIC);
02257             return TCL_ERROR;
02258         }
02259     }
02260 
02261     /************************************************************
02262      * If there was a argtype list on preparation, we need
02263      * an argument value list now
02264      ************************************************************/
02265     if (qdesc->nargs > 0)
02266     {
02267         if (i >= argc)
02268         {
02269             Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
02270             return TCL_ERROR;
02271         }
02272 
02273         /************************************************************
02274          * Split the argument values
02275          ************************************************************/
02276         if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
02277             return TCL_ERROR;
02278 
02279         /************************************************************
02280          * Check that the # of arguments matches
02281          ************************************************************/
02282         if (callnargs != qdesc->nargs)
02283         {
02284             Tcl_SetResult(interp,
02285                "argument list length doesn't match # of arguments for query",
02286                           TCL_STATIC);
02287             ckfree((char *) callargs);
02288             return TCL_ERROR;
02289         }
02290     }
02291     else
02292         callnargs = 0;
02293 
02294     /************************************************************
02295      * Get loop body if present
02296      ************************************************************/
02297     if (i < argc)
02298         loop_body = argv[i++];
02299 
02300     if (i != argc)
02301     {
02302         Tcl_SetResult(interp, usage, TCL_STATIC);
02303         return TCL_ERROR;
02304     }
02305 
02306     /************************************************************
02307      * Execute the plan inside a sub-transaction, so we can cope with
02308      * errors sanely
02309      ************************************************************/
02310 
02311     pltcl_subtrans_begin(oldcontext, oldowner);
02312 
02313     PG_TRY();
02314     {
02315         /************************************************************
02316          * Setup the value array for SPI_execute_plan() using
02317          * the type specific input functions
02318          ************************************************************/
02319         argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
02320 
02321         for (j = 0; j < callnargs; j++)
02322         {
02323             if (nulls && nulls[j] == 'n')
02324             {
02325                 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
02326                                                  NULL,
02327                                                  qdesc->argtypioparams[j],
02328                                                  -1);
02329             }
02330             else
02331             {
02332                 UTF_BEGIN;
02333                 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
02334                                                (char *) UTF_U2E(callargs[j]),
02335                                                  qdesc->argtypioparams[j],
02336                                                  -1);
02337                 UTF_END;
02338             }
02339         }
02340 
02341         if (callargs)
02342             ckfree((char *) callargs);
02343         callargs = NULL;
02344 
02345         /************************************************************
02346          * Execute the plan
02347          ************************************************************/
02348         spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
02349                                   pltcl_current_prodesc->fn_readonly, count);
02350 
02351         my_rc = pltcl_process_SPI_result(interp,
02352                                          arrayname,
02353                                          loop_body,
02354                                          spi_rc,
02355                                          SPI_tuptable,
02356                                          SPI_processed);
02357 
02358         pltcl_subtrans_commit(oldcontext, oldowner);
02359     }
02360     PG_CATCH();
02361     {
02362         pltcl_subtrans_abort(interp, oldcontext, oldowner);
02363 
02364         if (callargs)
02365             ckfree((char *) callargs);
02366 
02367         return TCL_ERROR;
02368     }
02369     PG_END_TRY();
02370 
02371     return my_rc;
02372 }
02373 
02374 
02375 /**********************************************************************
02376  * pltcl_SPI_lastoid()  - return the last oid. To
02377  *        be used after insert queries
02378  **********************************************************************/
02379 static int
02380 pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
02381                   int argc, CONST84 char *argv[])
02382 {
02383     char        buf[64];
02384 
02385     snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
02386     Tcl_SetResult(interp, buf, TCL_VOLATILE);
02387     return TCL_OK;
02388 }
02389 
02390 
02391 /**********************************************************************
02392  * pltcl_set_tuple_values() - Set variables for all attributes
02393  *                of a given tuple
02394  **********************************************************************/
02395 static void
02396 pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
02397                        int tupno, HeapTuple tuple, TupleDesc tupdesc)
02398 {
02399     int         i;
02400     char       *outputstr;
02401     char        buf[64];
02402     Datum       attr;
02403     bool        isnull;
02404 
02405     CONST84 char *attname;
02406     HeapTuple   typeTup;
02407     Oid         typoutput;
02408 
02409     CONST84 char **arrptr;
02410     CONST84 char **nameptr;
02411     CONST84 char *nullname = NULL;
02412 
02413     /************************************************************
02414      * Prepare pointers for Tcl_SetVar2() below and in array
02415      * mode set the .tupno element
02416      ************************************************************/
02417     if (arrayname == NULL)
02418     {
02419         arrptr = &attname;
02420         nameptr = &nullname;
02421     }
02422     else
02423     {
02424         arrptr = &arrayname;
02425         nameptr = &attname;
02426         snprintf(buf, sizeof(buf), "%d", tupno);
02427         Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
02428     }
02429 
02430     for (i = 0; i < tupdesc->natts; i++)
02431     {
02432         /* ignore dropped attributes */
02433         if (tupdesc->attrs[i]->attisdropped)
02434             continue;
02435 
02436         /************************************************************
02437          * Get the attribute name
02438          ************************************************************/
02439         attname = NameStr(tupdesc->attrs[i]->attname);
02440 
02441         /************************************************************
02442          * Get the attributes value
02443          ************************************************************/
02444         attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
02445 
02446         /************************************************************
02447          * Lookup the attribute type in the syscache
02448          * for the output function
02449          ************************************************************/
02450         typeTup = SearchSysCache1(TYPEOID,
02451                               ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
02452         if (!HeapTupleIsValid(typeTup))
02453             elog(ERROR, "cache lookup failed for type %u",
02454                  tupdesc->attrs[i]->atttypid);
02455 
02456         typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
02457         ReleaseSysCache(typeTup);
02458 
02459         /************************************************************
02460          * If there is a value, set the variable
02461          * If not, unset it
02462          *
02463          * Hmmm - Null attributes will cause functions to
02464          *        crash if they don't expect them - need something
02465          *        smarter here.
02466          ************************************************************/
02467         if (!isnull && OidIsValid(typoutput))
02468         {
02469             outputstr = OidOutputFunctionCall(typoutput, attr);
02470             UTF_BEGIN;
02471             Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
02472             UTF_END;
02473             pfree(outputstr);
02474         }
02475         else
02476             Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
02477     }
02478 }
02479 
02480 
02481 /**********************************************************************
02482  * pltcl_build_tuple_argument() - Build a string usable for 'array set'
02483  *                from all attributes of a given tuple
02484  **********************************************************************/
02485 static void
02486 pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
02487                            Tcl_DString *retval)
02488 {
02489     int         i;
02490     char       *outputstr;
02491     Datum       attr;
02492     bool        isnull;
02493 
02494     char       *attname;
02495     HeapTuple   typeTup;
02496     Oid         typoutput;
02497 
02498     for (i = 0; i < tupdesc->natts; i++)
02499     {
02500         /* ignore dropped attributes */
02501         if (tupdesc->attrs[i]->attisdropped)
02502             continue;
02503 
02504         /************************************************************
02505          * Get the attribute name
02506          ************************************************************/
02507         attname = NameStr(tupdesc->attrs[i]->attname);
02508 
02509         /************************************************************
02510          * Get the attributes value
02511          ************************************************************/
02512         attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
02513 
02514         /************************************************************
02515          * Lookup the attribute type in the syscache
02516          * for the output function
02517          ************************************************************/
02518         typeTup = SearchSysCache1(TYPEOID,
02519                               ObjectIdGetDatum(tupdesc->attrs[i]->atttypid));
02520         if (!HeapTupleIsValid(typeTup))
02521             elog(ERROR, "cache lookup failed for type %u",
02522                  tupdesc->attrs[i]->atttypid);
02523 
02524         typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
02525         ReleaseSysCache(typeTup);
02526 
02527         /************************************************************
02528          * If there is a value, append the attribute name and the
02529          * value to the list
02530          *
02531          * Hmmm - Null attributes will cause functions to
02532          *        crash if they don't expect them - need something
02533          *        smarter here.
02534          ************************************************************/
02535         if (!isnull && OidIsValid(typoutput))
02536         {
02537             outputstr = OidOutputFunctionCall(typoutput, attr);
02538             Tcl_DStringAppendElement(retval, attname);
02539             UTF_BEGIN;
02540             Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
02541             UTF_END;
02542             pfree(outputstr);
02543         }
02544     }
02545 }