LCOV - code coverage report
Current view: top level - src/pl/tcl - pltcl.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 91.5 % 1090 997
Test Date: 2026-04-07 14:16:30 Functions: 87.2 % 47 41
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /**********************************************************************
       2              :  * pltcl.c      - PostgreSQL support for Tcl as
       3              :  *                procedural language (PL)
       4              :  *
       5              :  *    src/pl/tcl/pltcl.c
       6              :  *
       7              :  **********************************************************************/
       8              : 
       9              : #include "postgres.h"
      10              : 
      11              : #include <tcl.h>
      12              : 
      13              : #include <unistd.h>
      14              : #include <fcntl.h>
      15              : 
      16              : #include "access/htup_details.h"
      17              : #include "access/xact.h"
      18              : #include "catalog/objectaccess.h"
      19              : #include "catalog/pg_proc.h"
      20              : #include "catalog/pg_type.h"
      21              : #include "commands/event_trigger.h"
      22              : #include "commands/trigger.h"
      23              : #include "executor/spi.h"
      24              : #include "fmgr.h"
      25              : #include "funcapi.h"
      26              : #include "mb/pg_wchar.h"
      27              : #include "miscadmin.h"
      28              : #include "parser/parse_func.h"
      29              : #include "parser/parse_type.h"
      30              : #include "pgstat.h"
      31              : #include "utils/acl.h"
      32              : #include "utils/builtins.h"
      33              : #include "utils/guc.h"
      34              : #include "utils/hsearch.h"
      35              : #include "utils/lsyscache.h"
      36              : #include "utils/memutils.h"
      37              : #include "utils/regproc.h"
      38              : #include "utils/rel.h"
      39              : #include "utils/syscache.h"
      40              : #include "utils/tuplestore.h"
      41              : #include "utils/typcache.h"
      42              : 
      43              : 
      44            9 : PG_MODULE_MAGIC_EXT(
      45              :                     .name = "pltcl",
      46              :                     .version = PG_VERSION
      47              : );
      48              : 
      49              : #define HAVE_TCL_VERSION(maj,min) \
      50              :     ((TCL_MAJOR_VERSION > maj) || \
      51              :      (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
      52              : 
      53              : /* Insist on Tcl >= 8.4 */
      54              : #if !HAVE_TCL_VERSION(8,4)
      55              : #error PostgreSQL only supports Tcl 8.4 or later.
      56              : #endif
      57              : 
      58              : /* Hack to deal with Tcl 8.6 const-ification without losing compatibility */
      59              : #ifndef CONST86
      60              : #define CONST86
      61              : #endif
      62              : 
      63              : #if !HAVE_TCL_VERSION(8,7)
      64              : typedef int Tcl_Size;
      65              : #endif
      66              : 
      67              : /* define our text domain for translations */
      68              : #undef TEXTDOMAIN
      69              : #define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
      70              : 
      71              : 
      72              : /*
      73              :  * Support for converting between UTF8 (which is what all strings going into
      74              :  * or out of Tcl should be) and the database encoding.
      75              :  *
      76              :  * If you just use utf_u2e() or utf_e2u() directly, they will leak some
      77              :  * palloc'd space when doing a conversion.  This is not worth worrying about
      78              :  * if it only happens, say, once per PL/Tcl function call.  If it does seem
      79              :  * worth worrying about, use the wrapper macros.
      80              :  */
      81              : 
      82              : static inline char *
      83          762 : utf_u2e(const char *src)
      84              : {
      85          762 :     return pg_any_to_server(src, strlen(src), PG_UTF8);
      86              : }
      87              : 
      88              : static inline char *
      89         1358 : utf_e2u(const char *src)
      90              : {
      91         1358 :     return pg_server_to_any(src, strlen(src), PG_UTF8);
      92              : }
      93              : 
      94              : #define UTF_BEGIN \
      95              :     do { \
      96              :         const char *_pltcl_utf_src = NULL; \
      97              :         char *_pltcl_utf_dst = NULL
      98              : 
      99              : #define UTF_END \
     100              :     if (_pltcl_utf_src != (const char *) _pltcl_utf_dst) \
     101              :             pfree(_pltcl_utf_dst); \
     102              :     } while (0)
     103              : 
     104              : #define UTF_U2E(x) \
     105              :     (_pltcl_utf_dst = utf_u2e(_pltcl_utf_src = (x)))
     106              : 
     107              : #define UTF_E2U(x) \
     108              :     (_pltcl_utf_dst = utf_e2u(_pltcl_utf_src = (x)))
     109              : 
     110              : 
     111              : /**********************************************************************
     112              :  * Information associated with a Tcl interpreter.  We have one interpreter
     113              :  * that is used for all pltclu (untrusted) functions.  For pltcl (trusted)
     114              :  * functions, there is a separate interpreter for each effective SQL userid.
     115              :  * (This is needed to ensure that an unprivileged user can't inject Tcl code
     116              :  * that'll be executed with the privileges of some other SQL user.)
     117              :  *
     118              :  * The pltcl_interp_desc structs are kept in a Postgres hash table indexed
     119              :  * by userid OID, with OID 0 used for the single untrusted interpreter.
     120              :  **********************************************************************/
     121              : typedef struct pltcl_interp_desc
     122              : {
     123              :     Oid         user_id;        /* Hash key (must be first!) */
     124              :     Tcl_Interp *interp;         /* The interpreter */
     125              :     Tcl_HashTable query_hash;   /* pltcl_query_desc structs */
     126              : } pltcl_interp_desc;
     127              : 
     128              : 
     129              : /**********************************************************************
     130              :  * The information we cache about loaded procedures
     131              :  *
     132              :  * The pltcl_proc_desc struct itself, as well as all subsidiary data,
     133              :  * is stored in the memory context identified by the fn_cxt field.
     134              :  * We can reclaim all the data by deleting that context, and should do so
     135              :  * when the fn_refcount goes to zero.  That will happen if we build a new
     136              :  * pltcl_proc_desc following an update of the pg_proc row.  If that happens
     137              :  * while the old proc is being executed, we mustn't remove the struct until
     138              :  * execution finishes.  When building a new pltcl_proc_desc, we unlink
     139              :  * Tcl's copy of the old procedure definition, similarly relying on Tcl's
     140              :  * internal reference counting to prevent that structure from disappearing
     141              :  * while it's in use.
     142              :  *
     143              :  * Note that the data in this struct is shared across all active calls;
     144              :  * nothing except the fn_refcount should be changed by a call instance.
     145              :  **********************************************************************/
     146              : typedef struct pltcl_proc_desc
     147              : {
     148              :     char       *user_proname;   /* user's name (from format_procedure) */
     149              :     char       *internal_proname;   /* Tcl proc name (NULL if deleted) */
     150              :     MemoryContext fn_cxt;       /* memory context for this procedure */
     151              :     unsigned long fn_refcount;  /* number of active references */
     152              :     TransactionId fn_xmin;      /* xmin of pg_proc row */
     153              :     ItemPointerData fn_tid;     /* TID of pg_proc row */
     154              :     bool        fn_readonly;    /* is function readonly? */
     155              :     bool        lanpltrusted;   /* is it pltcl (vs. pltclu)? */
     156              :     pltcl_interp_desc *interp_desc; /* interpreter to use */
     157              :     Oid         result_typid;   /* OID of fn's result type */
     158              :     FmgrInfo    result_in_func; /* input function for fn's result type */
     159              :     Oid         result_typioparam;  /* param to pass to same */
     160              :     bool        fn_retisset;    /* true if function returns a set */
     161              :     bool        fn_retistuple;  /* true if function returns composite */
     162              :     bool        fn_retisdomain; /* true if function returns domain */
     163              :     void       *domain_info;    /* opaque cache for domain checks */
     164              :     int         nargs;          /* number of arguments */
     165              :     /* these arrays have nargs entries: */
     166              :     FmgrInfo   *arg_out_func;   /* output fns for arg types */
     167              :     bool       *arg_is_rowtype; /* is each arg composite? */
     168              : } pltcl_proc_desc;
     169              : 
     170              : 
     171              : /**********************************************************************
     172              :  * The information we cache about prepared and saved plans
     173              :  **********************************************************************/
     174              : typedef struct pltcl_query_desc
     175              : {
     176              :     char        qname[20];
     177              :     SPIPlanPtr  plan;
     178              :     int         nargs;
     179              :     Oid        *argtypes;
     180              :     FmgrInfo   *arginfuncs;
     181              :     Oid        *argtypioparams;
     182              : } pltcl_query_desc;
     183              : 
     184              : 
     185              : /**********************************************************************
     186              :  * For speedy lookup, we maintain a hash table mapping from
     187              :  * function OID + trigger flag + user OID to pltcl_proc_desc pointers.
     188              :  * The reason the pltcl_proc_desc struct isn't directly part of the hash
     189              :  * entry is to simplify recovery from errors during compile_pltcl_function.
     190              :  *
     191              :  * Note: if the same function is called by multiple userIDs within a session,
     192              :  * there will be a separate pltcl_proc_desc entry for each userID in the case
     193              :  * of pltcl functions, but only one entry for pltclu functions, because we
     194              :  * set user_id = 0 for that case.
     195              :  **********************************************************************/
     196              : typedef struct pltcl_proc_key
     197              : {
     198              :     Oid         proc_id;        /* Function OID */
     199              : 
     200              :     /*
     201              :      * is_trigger is really a bool, but declare as Oid to ensure this struct
     202              :      * contains no padding
     203              :      */
     204              :     Oid         is_trigger;     /* is it a trigger function? */
     205              :     Oid         user_id;        /* User calling the function, or 0 */
     206              : } pltcl_proc_key;
     207              : 
     208              : typedef struct pltcl_proc_ptr
     209              : {
     210              :     pltcl_proc_key proc_key;    /* Hash key (must be first!) */
     211              :     pltcl_proc_desc *proc_ptr;
     212              : } pltcl_proc_ptr;
     213              : 
     214              : 
     215              : /**********************************************************************
     216              :  * Per-call state
     217              :  **********************************************************************/
     218              : typedef struct pltcl_call_state
     219              : {
     220              :     /* Call info struct, or NULL in a trigger */
     221              :     FunctionCallInfo fcinfo;
     222              : 
     223              :     /* Trigger data, if we're in a normal (not event) trigger; else NULL */
     224              :     TriggerData *trigdata;
     225              : 
     226              :     /* Function we're executing (NULL if not yet identified) */
     227              :     pltcl_proc_desc *prodesc;
     228              : 
     229              :     /*
     230              :      * Information for SRFs and functions returning composite types.
     231              :      * ret_tupdesc and attinmeta are set up if either fn_retistuple or
     232              :      * fn_retisset, since even a scalar-returning SRF needs a tuplestore.
     233              :      */
     234              :     TupleDesc   ret_tupdesc;    /* return rowtype, if retistuple or retisset */
     235              :     AttInMetadata *attinmeta;   /* metadata for building tuples of that type */
     236              : 
     237              :     ReturnSetInfo *rsi;         /* passed-in ReturnSetInfo, if any */
     238              :     Tuplestorestate *tuple_store;   /* SRFs accumulate result here */
     239              :     MemoryContext tuple_store_cxt;  /* context and resowner for tuplestore */
     240              :     ResourceOwner tuple_store_owner;
     241              : } pltcl_call_state;
     242              : 
     243              : 
     244              : /**********************************************************************
     245              :  * Global data
     246              :  **********************************************************************/
     247              : static char *pltcl_start_proc = NULL;
     248              : static char *pltclu_start_proc = NULL;
     249              : static bool pltcl_pm_init_done = false;
     250              : static Tcl_Interp *pltcl_hold_interp = NULL;
     251              : static HTAB *pltcl_interp_htab = NULL;
     252              : static HTAB *pltcl_proc_htab = NULL;
     253              : 
     254              : /* this is saved and restored by pltcl_handler */
     255              : static pltcl_call_state *pltcl_current_call_state = NULL;
     256              : 
     257              : /**********************************************************************
     258              :  * Lookup table for SQLSTATE condition names
     259              :  **********************************************************************/
     260              : typedef struct
     261              : {
     262              :     const char *label;
     263              :     int         sqlerrstate;
     264              : } TclExceptionNameMap;
     265              : 
     266              : static const TclExceptionNameMap exception_name_map[] = {
     267              : #include "pltclerrcodes.h"
     268              :     {NULL, 0}
     269              : };
     270              : 
     271              : /**********************************************************************
     272              :  * Forward declarations
     273              :  **********************************************************************/
     274              : 
     275              : static void pltcl_init_interp(pltcl_interp_desc *interp_desc,
     276              :                               Oid prolang, bool pltrusted);
     277              : static pltcl_interp_desc *pltcl_fetch_interp(Oid prolang, bool pltrusted);
     278              : static void call_pltcl_start_proc(Oid prolang, bool pltrusted);
     279              : static void start_proc_error_callback(void *arg);
     280              : 
     281              : static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted);
     282              : 
     283              : static Datum pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
     284              :                                 bool pltrusted);
     285              : static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
     286              :                                        bool pltrusted);
     287              : static void pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
     288              :                                         bool pltrusted);
     289              : 
     290              : static void throw_tcl_error(Tcl_Interp *interp, const char *proname);
     291              : 
     292              : static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, Oid tgreloid,
     293              :                                                bool is_event_trigger,
     294              :                                                bool pltrusted);
     295              : 
     296              : static int  pltcl_elog(ClientData cdata, Tcl_Interp *interp,
     297              :                        int objc, Tcl_Obj *const objv[]);
     298              : static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata);
     299              : static const char *pltcl_get_condition_name(int sqlstate);
     300              : static int  pltcl_quote(ClientData cdata, Tcl_Interp *interp,
     301              :                         int objc, Tcl_Obj *const objv[]);
     302              : static int  pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
     303              :                             int objc, Tcl_Obj *const objv[]);
     304              : static int  pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
     305              :                              int objc, Tcl_Obj *const objv[]);
     306              : static int  pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
     307              :                              int objc, Tcl_Obj *const objv[]);
     308              : static int  pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
     309              :                               int objc, Tcl_Obj *const objv[]);
     310              : static int  pltcl_process_SPI_result(Tcl_Interp *interp,
     311              :                                      const char *arrayname,
     312              :                                      Tcl_Obj *loop_body,
     313              :                                      int spi_rc,
     314              :                                      SPITupleTable *tuptable,
     315              :                                      uint64 ntuples);
     316              : static int  pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
     317              :                               int objc, Tcl_Obj *const objv[]);
     318              : static int  pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
     319              :                                    int objc, Tcl_Obj *const objv[]);
     320              : static int  pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp,
     321              :                                  int objc, Tcl_Obj *const objv[]);
     322              : static int  pltcl_commit(ClientData cdata, Tcl_Interp *interp,
     323              :                          int objc, Tcl_Obj *const objv[]);
     324              : static int  pltcl_rollback(ClientData cdata, Tcl_Interp *interp,
     325              :                            int objc, Tcl_Obj *const objv[]);
     326              : 
     327              : static void pltcl_subtrans_begin(MemoryContext oldcontext,
     328              :                                  ResourceOwner oldowner);
     329              : static void pltcl_subtrans_commit(MemoryContext oldcontext,
     330              :                                   ResourceOwner oldowner);
     331              : static void pltcl_subtrans_abort(Tcl_Interp *interp,
     332              :                                  MemoryContext oldcontext,
     333              :                                  ResourceOwner oldowner);
     334              : 
     335              : static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
     336              :                                    uint64 tupno, HeapTuple tuple, TupleDesc tupdesc);
     337              : static Tcl_Obj *pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, bool include_generated);
     338              : static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp,
     339              :                                           Tcl_Obj **kvObjv, int kvObjc,
     340              :                                           pltcl_call_state *call_state);
     341              : static void pltcl_init_tuple_store(pltcl_call_state *call_state);
     342              : 
     343              : 
     344              : /*
     345              :  * Hack to override Tcl's builtin Notifier subsystem.  This prevents the
     346              :  * backend from becoming multithreaded, which breaks all sorts of things.
     347              :  * That happens in the default version of Tcl_InitNotifier if the Tcl library
     348              :  * has been compiled with multithreading support (i.e. when TCL_THREADS is
     349              :  * defined under Unix, and in all cases under Windows).
     350              :  * It's okay to disable the notifier because we never enter the Tcl event loop
     351              :  * from Postgres, so the notifier capabilities are initialized, but never
     352              :  * used.  Only InitNotifier and DeleteFileHandler ever seem to get called
     353              :  * within Postgres, but we implement all the functions for completeness.
     354              :  */
     355              : static ClientData
     356            9 : pltcl_InitNotifier(void)
     357              : {
     358              :     static int  fakeThreadKey;  /* To give valid address for ClientData */
     359              : 
     360            9 :     return (ClientData) &(fakeThreadKey);
     361              : }
     362              : 
     363              : static void
     364            0 : pltcl_FinalizeNotifier(ClientData clientData)
     365              : {
     366            0 : }
     367              : 
     368              : static void
     369            1 : pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
     370              : {
     371            1 : }
     372              : 
     373              : static void
     374            0 : pltcl_AlertNotifier(ClientData clientData)
     375              : {
     376            0 : }
     377              : 
     378              : static void
     379            0 : pltcl_CreateFileHandler(int fd, int mask,
     380              :                         Tcl_FileProc *proc, ClientData clientData)
     381              : {
     382            0 : }
     383              : 
     384              : static void
     385           46 : pltcl_DeleteFileHandler(int fd)
     386              : {
     387           46 : }
     388              : 
     389              : static void
     390            0 : pltcl_ServiceModeHook(int mode)
     391              : {
     392            0 : }
     393              : 
     394              : static int
     395       396936 : pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
     396              : {
     397       396936 :     return 0;
     398              : }
     399              : 
     400              : 
     401              : /*
     402              :  * _PG_init()           - library load-time initialization
     403              :  *
     404              :  * DO NOT make this static nor change its name!
     405              :  *
     406              :  * The work done here must be safe to do in the postmaster process,
     407              :  * in case the pltcl library is preloaded in the postmaster.
     408              :  */
     409              : void
     410            9 : _PG_init(void)
     411              : {
     412              :     Tcl_NotifierProcs notifier;
     413              :     HASHCTL     hash_ctl;
     414              : 
     415              :     /* Be sure we do initialization only once (should be redundant now) */
     416            9 :     if (pltcl_pm_init_done)
     417            0 :         return;
     418              : 
     419            9 :     pg_bindtextdomain(TEXTDOMAIN);
     420              : 
     421              : #ifdef WIN32
     422              :     /* Required on win32 to prevent error loading init.tcl */
     423              :     Tcl_FindExecutable("");
     424              : #endif
     425              : 
     426              :     /*
     427              :      * Override the functions in the Notifier subsystem.  See comments above.
     428              :      */
     429            9 :     notifier.setTimerProc = pltcl_SetTimer;
     430            9 :     notifier.waitForEventProc = pltcl_WaitForEvent;
     431            9 :     notifier.createFileHandlerProc = pltcl_CreateFileHandler;
     432            9 :     notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
     433            9 :     notifier.initNotifierProc = pltcl_InitNotifier;
     434            9 :     notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
     435            9 :     notifier.alertNotifierProc = pltcl_AlertNotifier;
     436            9 :     notifier.serviceModeHookProc = pltcl_ServiceModeHook;
     437            9 :     Tcl_SetNotifier(&notifier);
     438              : 
     439              :     /************************************************************
     440              :      * Create the dummy hold interpreter to prevent close of
     441              :      * stdout and stderr on DeleteInterp
     442              :      ************************************************************/
     443            9 :     if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
     444            0 :         elog(ERROR, "could not create dummy Tcl interpreter");
     445            9 :     if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
     446            0 :         elog(ERROR, "could not initialize dummy Tcl interpreter");
     447              : 
     448              :     /************************************************************
     449              :      * Create the hash table for working interpreters
     450              :      ************************************************************/
     451            9 :     hash_ctl.keysize = sizeof(Oid);
     452            9 :     hash_ctl.entrysize = sizeof(pltcl_interp_desc);
     453            9 :     pltcl_interp_htab = hash_create("PL/Tcl interpreters",
     454              :                                     8,
     455              :                                     &hash_ctl,
     456              :                                     HASH_ELEM | HASH_BLOBS);
     457              : 
     458              :     /************************************************************
     459              :      * Create the hash table for function lookup
     460              :      ************************************************************/
     461            9 :     hash_ctl.keysize = sizeof(pltcl_proc_key);
     462            9 :     hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
     463            9 :     pltcl_proc_htab = hash_create("PL/Tcl functions",
     464              :                                   100,
     465              :                                   &hash_ctl,
     466              :                                   HASH_ELEM | HASH_BLOBS);
     467              : 
     468              :     /************************************************************
     469              :      * Define PL/Tcl's custom GUCs
     470              :      ************************************************************/
     471            9 :     DefineCustomStringVariable("pltcl.start_proc",
     472              :                                gettext_noop("PL/Tcl function to call once when pltcl is first used."),
     473              :                                NULL,
     474              :                                &pltcl_start_proc,
     475              :                                NULL,
     476              :                                PGC_SUSET, 0,
     477              :                                NULL, NULL, NULL);
     478            9 :     DefineCustomStringVariable("pltclu.start_proc",
     479              :                                gettext_noop("PL/TclU function to call once when pltclu is first used."),
     480              :                                NULL,
     481              :                                &pltclu_start_proc,
     482              :                                NULL,
     483              :                                PGC_SUSET, 0,
     484              :                                NULL, NULL, NULL);
     485              : 
     486            9 :     MarkGUCPrefixReserved("pltcl");
     487            9 :     MarkGUCPrefixReserved("pltclu");
     488              : 
     489            9 :     pltcl_pm_init_done = true;
     490              : }
     491              : 
     492              : /**********************************************************************
     493              :  * pltcl_init_interp() - initialize a new Tcl interpreter
     494              :  **********************************************************************/
     495              : static void
     496           11 : pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
     497              : {
     498              :     Tcl_Interp *interp;
     499              :     char        interpname[32];
     500              : 
     501              :     /************************************************************
     502              :      * Create the Tcl interpreter subsidiary to pltcl_hold_interp.
     503              :      * Note: Tcl automatically does Tcl_Init in the untrusted case,
     504              :      * and it's not wanted in the trusted case.
     505              :      ************************************************************/
     506           11 :     snprintf(interpname, sizeof(interpname), "subsidiary_%u", interp_desc->user_id);
     507           11 :     if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
     508              :                                   pltrusted ? 1 : 0)) == NULL)
     509            0 :         elog(ERROR, "could not create subsidiary Tcl interpreter");
     510              : 
     511              :     /************************************************************
     512              :      * Initialize the query hash table associated with interpreter
     513              :      ************************************************************/
     514           11 :     Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
     515              : 
     516              :     /************************************************************
     517              :      * Install the commands for SPI support in the interpreter
     518              :      ************************************************************/
     519           11 :     Tcl_CreateObjCommand(interp, "elog",
     520              :                          pltcl_elog, NULL, NULL);
     521           11 :     Tcl_CreateObjCommand(interp, "quote",
     522              :                          pltcl_quote, NULL, NULL);
     523           11 :     Tcl_CreateObjCommand(interp, "argisnull",
     524              :                          pltcl_argisnull, NULL, NULL);
     525           11 :     Tcl_CreateObjCommand(interp, "return_null",
     526              :                          pltcl_returnnull, NULL, NULL);
     527           11 :     Tcl_CreateObjCommand(interp, "return_next",
     528              :                          pltcl_returnnext, NULL, NULL);
     529           11 :     Tcl_CreateObjCommand(interp, "spi_exec",
     530              :                          pltcl_SPI_execute, NULL, NULL);
     531           11 :     Tcl_CreateObjCommand(interp, "spi_prepare",
     532              :                          pltcl_SPI_prepare, NULL, NULL);
     533           11 :     Tcl_CreateObjCommand(interp, "spi_execp",
     534              :                          pltcl_SPI_execute_plan, NULL, NULL);
     535           11 :     Tcl_CreateObjCommand(interp, "subtransaction",
     536              :                          pltcl_subtransaction, NULL, NULL);
     537           11 :     Tcl_CreateObjCommand(interp, "commit",
     538              :                          pltcl_commit, NULL, NULL);
     539           11 :     Tcl_CreateObjCommand(interp, "rollback",
     540              :                          pltcl_rollback, NULL, NULL);
     541              : 
     542              :     /************************************************************
     543              :      * Call the appropriate start_proc, if there is one.
     544              :      *
     545              :      * We must set interp_desc->interp before the call, else the start_proc
     546              :      * won't find the interpreter it's supposed to use.  But, if the
     547              :      * start_proc fails, we want to abandon use of the interpreter.
     548              :      ************************************************************/
     549           11 :     PG_TRY();
     550              :     {
     551           11 :         interp_desc->interp = interp;
     552           11 :         call_pltcl_start_proc(prolang, pltrusted);
     553              :     }
     554            3 :     PG_CATCH();
     555              :     {
     556            3 :         interp_desc->interp = NULL;
     557            3 :         Tcl_DeleteInterp(interp);
     558            3 :         PG_RE_THROW();
     559              :     }
     560            8 :     PG_END_TRY();
     561            8 : }
     562              : 
     563              : /**********************************************************************
     564              :  * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
     565              :  *
     566              :  * This also takes care of any on-first-use initialization required.
     567              :  **********************************************************************/
     568              : static pltcl_interp_desc *
     569           65 : pltcl_fetch_interp(Oid prolang, bool pltrusted)
     570              : {
     571              :     Oid         user_id;
     572              :     pltcl_interp_desc *interp_desc;
     573              :     bool        found;
     574              : 
     575              :     /* Find or create the interpreter hashtable entry for this userid */
     576           65 :     if (pltrusted)
     577           65 :         user_id = GetUserId();
     578              :     else
     579            0 :         user_id = InvalidOid;
     580              : 
     581           65 :     interp_desc = hash_search(pltcl_interp_htab, &user_id,
     582              :                               HASH_ENTER,
     583              :                               &found);
     584           65 :     if (!found)
     585            8 :         interp_desc->interp = NULL;
     586              : 
     587              :     /* If we haven't yet successfully made an interpreter, try to do that */
     588           65 :     if (!interp_desc->interp)
     589           11 :         pltcl_init_interp(interp_desc, prolang, pltrusted);
     590              : 
     591           62 :     return interp_desc;
     592              : }
     593              : 
     594              : 
     595              : /**********************************************************************
     596              :  * call_pltcl_start_proc()   - Call user-defined initialization proc, if any
     597              :  **********************************************************************/
     598              : static void
     599           11 : call_pltcl_start_proc(Oid prolang, bool pltrusted)
     600              : {
     601           11 :     LOCAL_FCINFO(fcinfo, 0);
     602              :     char       *start_proc;
     603              :     const char *gucname;
     604              :     ErrorContextCallback errcallback;
     605              :     List       *namelist;
     606              :     Oid         procOid;
     607              :     HeapTuple   procTup;
     608              :     Form_pg_proc procStruct;
     609              :     AclResult   aclresult;
     610              :     FmgrInfo    finfo;
     611              :     PgStat_FunctionCallUsage fcusage;
     612              : 
     613              :     /* select appropriate GUC */
     614           11 :     start_proc = pltrusted ? pltcl_start_proc : pltclu_start_proc;
     615           11 :     gucname = pltrusted ? "pltcl.start_proc" : "pltclu.start_proc";
     616              : 
     617              :     /* Nothing to do if it's empty or unset */
     618           11 :     if (start_proc == NULL || start_proc[0] == '\0')
     619            7 :         return;
     620              : 
     621              :     /* Set up errcontext callback to make errors more helpful */
     622            4 :     errcallback.callback = start_proc_error_callback;
     623            4 :     errcallback.arg = unconstify(char *, gucname);
     624            4 :     errcallback.previous = error_context_stack;
     625            4 :     error_context_stack = &errcallback;
     626              : 
     627              :     /* Parse possibly-qualified identifier and look up the function */
     628            4 :     namelist = stringToQualifiedNameList(start_proc, NULL);
     629            4 :     procOid = LookupFuncName(namelist, 0, NULL, false);
     630              : 
     631              :     /* Current user must have permission to call function */
     632            2 :     aclresult = object_aclcheck(ProcedureRelationId, procOid, GetUserId(), ACL_EXECUTE);
     633            2 :     if (aclresult != ACLCHECK_OK)
     634            0 :         aclcheck_error(aclresult, OBJECT_FUNCTION, start_proc);
     635              : 
     636              :     /* Get the function's pg_proc entry */
     637            2 :     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
     638            2 :     if (!HeapTupleIsValid(procTup))
     639            0 :         elog(ERROR, "cache lookup failed for function %u", procOid);
     640            2 :     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
     641              : 
     642              :     /* It must be same language as the function we're currently calling */
     643            2 :     if (procStruct->prolang != prolang)
     644            0 :         ereport(ERROR,
     645              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     646              :                  errmsg("function \"%s\" is in the wrong language",
     647              :                         start_proc)));
     648              : 
     649              :     /*
     650              :      * It must not be SECURITY DEFINER, either.  This together with the
     651              :      * language match check ensures that the function will execute in the same
     652              :      * Tcl interpreter we just finished initializing.
     653              :      */
     654            2 :     if (procStruct->prosecdef)
     655            1 :         ereport(ERROR,
     656              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     657              :                  errmsg("function \"%s\" must not be SECURITY DEFINER",
     658              :                         start_proc)));
     659              : 
     660              :     /* A-OK */
     661            1 :     ReleaseSysCache(procTup);
     662              : 
     663              :     /*
     664              :      * Call the function using the normal SQL function call mechanism.  We
     665              :      * could perhaps cheat and jump directly to pltcl_handler(), but it seems
     666              :      * better to do it this way so that the call is exposed to, eg, call
     667              :      * statistics collection.
     668              :      */
     669            1 :     InvokeFunctionExecuteHook(procOid);
     670            1 :     fmgr_info(procOid, &finfo);
     671            1 :     InitFunctionCallInfoData(*fcinfo, &finfo,
     672              :                              0,
     673              :                              InvalidOid, NULL, NULL);
     674            1 :     pgstat_init_function_usage(fcinfo, &fcusage);
     675            1 :     (void) FunctionCallInvoke(fcinfo);
     676            1 :     pgstat_end_function_usage(&fcusage, true);
     677              : 
     678              :     /* Pop the error context stack */
     679            1 :     error_context_stack = errcallback.previous;
     680              : }
     681              : 
     682              : /*
     683              :  * Error context callback for errors occurring during start_proc processing.
     684              :  */
     685              : static void
     686            4 : start_proc_error_callback(void *arg)
     687              : {
     688            4 :     const char *gucname = (const char *) arg;
     689              : 
     690              :     /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
     691            4 :     errcontext("processing %s parameter", gucname);
     692            4 : }
     693              : 
     694              : 
     695              : /**********************************************************************
     696              :  * pltcl_call_handler       - This is the only visible function
     697              :  *                of the PL interpreter. The PostgreSQL
     698              :  *                function manager and trigger manager
     699              :  *                call this function for execution of
     700              :  *                PL/Tcl procedures.
     701              :  **********************************************************************/
     702            9 : PG_FUNCTION_INFO_V1(pltcl_call_handler);
     703              : 
     704              : /* keep non-static */
     705              : Datum
     706          223 : pltcl_call_handler(PG_FUNCTION_ARGS)
     707              : {
     708          223 :     return pltcl_handler(fcinfo, true);
     709              : }
     710              : 
     711              : /*
     712              :  * Alternative handler for unsafe functions
     713              :  */
     714            0 : PG_FUNCTION_INFO_V1(pltclu_call_handler);
     715              : 
     716              : /* keep non-static */
     717              : Datum
     718            0 : pltclu_call_handler(PG_FUNCTION_ARGS)
     719              : {
     720            0 :     return pltcl_handler(fcinfo, false);
     721              : }
     722              : 
     723              : 
     724              : /**********************************************************************
     725              :  * pltcl_handler()      - Handler for function and trigger calls, for
     726              :  *                        both trusted and untrusted interpreters.
     727              :  **********************************************************************/
     728              : static Datum
     729          223 : pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
     730              : {
     731          223 :     Datum       retval = (Datum) 0;
     732              :     pltcl_call_state current_call_state;
     733              :     pltcl_call_state *save_call_state;
     734              : 
     735              :     /*
     736              :      * Initialize current_call_state to nulls/zeroes; in particular, set its
     737              :      * prodesc pointer to null.  Anything that sets it non-null should
     738              :      * increase the prodesc's fn_refcount at the same time.  We'll decrease
     739              :      * the refcount, and then delete the prodesc if it's no longer referenced,
     740              :      * on the way out of this function.  This ensures that prodescs live as
     741              :      * long as needed even if somebody replaces the originating pg_proc row
     742              :      * while they're executing.
     743              :      */
     744          223 :     memset(&current_call_state, 0, sizeof(current_call_state));
     745              : 
     746              :     /*
     747              :      * Ensure that static pointer is saved/restored properly
     748              :      */
     749          223 :     save_call_state = pltcl_current_call_state;
     750          223 :     pltcl_current_call_state = &current_call_state;
     751              : 
     752          223 :     PG_TRY();
     753              :     {
     754              :         /*
     755              :          * Determine if called as function or trigger and call appropriate
     756              :          * subhandler
     757              :          */
     758          223 :         if (CALLED_AS_TRIGGER(fcinfo))
     759              :         {
     760              :             /* invoke the trigger handler */
     761           58 :             retval = PointerGetDatum(pltcl_trigger_handler(fcinfo,
     762              :                                                            &current_call_state,
     763              :                                                            pltrusted));
     764              :         }
     765          165 :         else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
     766              :         {
     767              :             /* invoke the event trigger handler */
     768           10 :             pltcl_event_trigger_handler(fcinfo, &current_call_state, pltrusted);
     769           10 :             retval = (Datum) 0;
     770              :         }
     771              :         else
     772              :         {
     773              :             /* invoke the regular function handler */
     774          155 :             current_call_state.fcinfo = fcinfo;
     775          155 :             retval = pltcl_func_handler(fcinfo, &current_call_state, pltrusted);
     776              :         }
     777              :     }
     778           55 :     PG_FINALLY();
     779              :     {
     780              :         /* Restore static pointer, then clean up the prodesc refcount if any */
     781              :         /*
     782              :          * (We're being paranoid in case an error is thrown in context
     783              :          * deletion)
     784              :          */
     785          223 :         pltcl_current_call_state = save_call_state;
     786          223 :         if (current_call_state.prodesc != NULL)
     787              :         {
     788              :             Assert(current_call_state.prodesc->fn_refcount > 0);
     789          220 :             if (--current_call_state.prodesc->fn_refcount == 0)
     790            1 :                 MemoryContextDelete(current_call_state.prodesc->fn_cxt);
     791              :         }
     792              :     }
     793          223 :     PG_END_TRY();
     794              : 
     795          168 :     return retval;
     796              : }
     797              : 
     798              : 
     799              : /**********************************************************************
     800              :  * pltcl_func_handler()     - Handler for regular function calls
     801              :  **********************************************************************/
     802              : static Datum
     803          155 : pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
     804              :                    bool pltrusted)
     805              : {
     806              :     bool        nonatomic;
     807              :     pltcl_proc_desc *prodesc;
     808              :     Tcl_Interp *volatile interp;
     809              :     Tcl_Obj    *tcl_cmd;
     810              :     int         i;
     811              :     int         tcl_rc;
     812              :     Datum       retval;
     813              : 
     814          346 :     nonatomic = fcinfo->context &&
     815          168 :         IsA(fcinfo->context, CallContext) &&
     816           13 :         !castNode(CallContext, fcinfo->context)->atomic;
     817              : 
     818              :     /* Connect to SPI manager */
     819          155 :     SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
     820              : 
     821              :     /* Find or compile the function */
     822          155 :     prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
     823              :                                      false, pltrusted);
     824              : 
     825          152 :     call_state->prodesc = prodesc;
     826          152 :     prodesc->fn_refcount++;
     827              : 
     828          152 :     interp = prodesc->interp_desc->interp;
     829              : 
     830              :     /*
     831              :      * If we're a SRF, check caller can handle materialize mode, and save
     832              :      * relevant info into call_state.  We must ensure that the returned
     833              :      * tuplestore is owned by the caller's context, even if we first create it
     834              :      * inside a subtransaction.
     835              :      */
     836          152 :     if (prodesc->fn_retisset)
     837              :     {
     838            5 :         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
     839              : 
     840            5 :         if (!rsi || !IsA(rsi, ReturnSetInfo))
     841            0 :             ereport(ERROR,
     842              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     843              :                      errmsg("set-valued function called in context that cannot accept a set")));
     844              : 
     845            5 :         if (!(rsi->allowedModes & SFRM_Materialize))
     846            0 :             ereport(ERROR,
     847              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     848              :                      errmsg("materialize mode required, but it is not allowed in this context")));
     849              : 
     850            5 :         call_state->rsi = rsi;
     851            5 :         call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
     852            5 :         call_state->tuple_store_owner = CurrentResourceOwner;
     853              :     }
     854              : 
     855              :     /************************************************************
     856              :      * Create the tcl command to call the internal
     857              :      * proc in the Tcl interpreter
     858              :      ************************************************************/
     859          152 :     tcl_cmd = Tcl_NewObj();
     860          152 :     Tcl_ListObjAppendElement(NULL, tcl_cmd,
     861          152 :                              Tcl_NewStringObj(prodesc->internal_proname, -1));
     862              : 
     863              :     /* We hold a refcount on tcl_cmd just to be sure it stays around */
     864          152 :     Tcl_IncrRefCount(tcl_cmd);
     865              : 
     866              :     /************************************************************
     867              :      * Add all call arguments to the command
     868              :      ************************************************************/
     869          152 :     PG_TRY();
     870              :     {
     871          350 :         for (i = 0; i < prodesc->nargs; i++)
     872              :         {
     873          198 :             if (prodesc->arg_is_rowtype[i])
     874              :             {
     875              :                 /**************************************************
     876              :                  * For tuple values, add a list for 'array set ...'
     877              :                  **************************************************/
     878            7 :                 if (fcinfo->args[i].isnull)
     879            0 :                     Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
     880              :                 else
     881              :                 {
     882              :                     HeapTupleHeader td;
     883              :                     Oid         tupType;
     884              :                     int32       tupTypmod;
     885              :                     TupleDesc   tupdesc;
     886              :                     HeapTupleData tmptup;
     887              :                     Tcl_Obj    *list_tmp;
     888              : 
     889            7 :                     td = DatumGetHeapTupleHeader(fcinfo->args[i].value);
     890              :                     /* Extract rowtype info and find a tupdesc */
     891            7 :                     tupType = HeapTupleHeaderGetTypeId(td);
     892            7 :                     tupTypmod = HeapTupleHeaderGetTypMod(td);
     893            7 :                     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     894              :                     /* Build a temporary HeapTuple control structure */
     895            7 :                     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
     896            7 :                     tmptup.t_data = td;
     897              : 
     898            7 :                     list_tmp = pltcl_build_tuple_argument(&tmptup, tupdesc, true);
     899            7 :                     Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);
     900              : 
     901            7 :                     ReleaseTupleDesc(tupdesc);
     902              :                 }
     903              :             }
     904              :             else
     905              :             {
     906              :                 /**************************************************
     907              :                  * Single values are added as string element
     908              :                  * of their external representation
     909              :                  **************************************************/
     910          191 :                 if (fcinfo->args[i].isnull)
     911            2 :                     Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
     912              :                 else
     913              :                 {
     914              :                     char       *tmp;
     915              : 
     916          189 :                     tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
     917              :                                              fcinfo->args[i].value);
     918          189 :                     UTF_BEGIN;
     919          189 :                     Tcl_ListObjAppendElement(NULL, tcl_cmd,
     920          189 :                                              Tcl_NewStringObj(UTF_E2U(tmp), -1));
     921          189 :                     UTF_END;
     922          189 :                     pfree(tmp);
     923              :                 }
     924              :             }
     925              :         }
     926              :     }
     927            0 :     PG_CATCH();
     928              :     {
     929              :         /* Release refcount to free tcl_cmd */
     930            0 :         Tcl_DecrRefCount(tcl_cmd);
     931            0 :         PG_RE_THROW();
     932              :     }
     933          152 :     PG_END_TRY();
     934              : 
     935              :     /************************************************************
     936              :      * Call the Tcl function
     937              :      *
     938              :      * We assume no PG error can be thrown directly from this call.
     939              :      ************************************************************/
     940          152 :     tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
     941              : 
     942              :     /* Release refcount to free tcl_cmd (and all subsidiary objects) */
     943          152 :     Tcl_DecrRefCount(tcl_cmd);
     944              : 
     945              :     /************************************************************
     946              :      * Check for errors reported by Tcl.
     947              :      ************************************************************/
     948          152 :     if (tcl_rc != TCL_OK)
     949           38 :         throw_tcl_error(interp, prodesc->user_proname);
     950              : 
     951              :     /************************************************************
     952              :      * Disconnect from SPI manager and then create the return
     953              :      * value datum (if the input function does a palloc for it
     954              :      * this must not be allocated in the SPI memory context
     955              :      * because SPI_finish would free it).  But don't try to call
     956              :      * the result_in_func if we've been told to return a NULL;
     957              :      * the Tcl result may not be a valid value of the result type
     958              :      * in that case.
     959              :      ************************************************************/
     960          114 :     if (SPI_finish() != SPI_OK_FINISH)
     961            0 :         elog(ERROR, "SPI_finish() failed");
     962              : 
     963          114 :     if (prodesc->fn_retisset)
     964              :     {
     965            3 :         ReturnSetInfo *rsi = call_state->rsi;
     966              : 
     967              :         /* We already checked this is OK */
     968            3 :         rsi->returnMode = SFRM_Materialize;
     969              : 
     970              :         /* If we produced any tuples, send back the result */
     971            3 :         if (call_state->tuple_store)
     972              :         {
     973            3 :             rsi->setResult = call_state->tuple_store;
     974            3 :             if (call_state->ret_tupdesc)
     975              :             {
     976              :                 MemoryContext oldcxt;
     977              : 
     978            3 :                 oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
     979            3 :                 rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
     980            3 :                 MemoryContextSwitchTo(oldcxt);
     981              :             }
     982              :         }
     983            3 :         retval = (Datum) 0;
     984            3 :         fcinfo->isnull = true;
     985              :     }
     986          111 :     else if (fcinfo->isnull)
     987              :     {
     988            1 :         retval = InputFunctionCall(&prodesc->result_in_func,
     989              :                                    NULL,
     990              :                                    prodesc->result_typioparam,
     991              :                                    -1);
     992              :     }
     993          110 :     else if (prodesc->fn_retistuple)
     994              :     {
     995              :         TupleDesc   td;
     996              :         HeapTuple   tup;
     997              :         Tcl_Obj    *resultObj;
     998              :         Tcl_Obj   **resultObjv;
     999              :         Tcl_Size    resultObjc;
    1000              : 
    1001              :         /*
    1002              :          * Set up data about result type.  XXX it's tempting to consider
    1003              :          * caching this in the prodesc, in the common case where the rowtype
    1004              :          * is determined by the function not the calling query.  But we'd have
    1005              :          * to be able to deal with ADD/DROP/ALTER COLUMN events when the
    1006              :          * result type is a named composite type, so it's not exactly trivial.
    1007              :          * Maybe worth improving someday.
    1008              :          */
    1009           16 :         switch (get_call_result_type(fcinfo, NULL, &td))
    1010              :         {
    1011           12 :             case TYPEFUNC_COMPOSITE:
    1012              :                 /* success */
    1013           12 :                 break;
    1014            3 :             case TYPEFUNC_COMPOSITE_DOMAIN:
    1015              :                 Assert(prodesc->fn_retisdomain);
    1016            3 :                 break;
    1017            1 :             case TYPEFUNC_RECORD:
    1018              :                 /* failed to determine actual type of RECORD */
    1019            1 :                 ereport(ERROR,
    1020              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1021              :                          errmsg("function returning record called in context "
    1022              :                                 "that cannot accept type record")));
    1023              :                 break;
    1024            0 :             default:
    1025              :                 /* result type isn't composite? */
    1026            0 :                 elog(ERROR, "return type must be a row type");
    1027              :                 break;
    1028              :         }
    1029              : 
    1030              :         Assert(!call_state->ret_tupdesc);
    1031              :         Assert(!call_state->attinmeta);
    1032           15 :         call_state->ret_tupdesc = td;
    1033           15 :         call_state->attinmeta = TupleDescGetAttInMetadata(td);
    1034              : 
    1035              :         /* Convert function result to tuple */
    1036           15 :         resultObj = Tcl_GetObjResult(interp);
    1037           15 :         if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
    1038            1 :             ereport(ERROR,
    1039              :                     (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
    1040              :                      errmsg("could not parse function return value: %s",
    1041              :                             utf_u2e(Tcl_GetStringResult(interp)))));
    1042              : 
    1043           14 :         tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
    1044              :                                        call_state);
    1045           10 :         retval = HeapTupleGetDatum(tup);
    1046              :     }
    1047              :     else
    1048           94 :         retval = InputFunctionCall(&prodesc->result_in_func,
    1049              :                                    utf_u2e(Tcl_GetStringResult(interp)),
    1050              :                                    prodesc->result_typioparam,
    1051              :                                    -1);
    1052              : 
    1053          108 :     return retval;
    1054              : }
    1055              : 
    1056              : 
    1057              : /**********************************************************************
    1058              :  * pltcl_trigger_handler()  - Handler for trigger calls
    1059              :  **********************************************************************/
    1060              : static HeapTuple
    1061           58 : pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
    1062              :                       bool pltrusted)
    1063              : {
    1064              :     pltcl_proc_desc *prodesc;
    1065              :     Tcl_Interp *volatile interp;
    1066           58 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1067              :     char       *stroid;
    1068              :     TupleDesc   tupdesc;
    1069              :     volatile HeapTuple rettup;
    1070              :     Tcl_Obj    *tcl_cmd;
    1071              :     Tcl_Obj    *tcl_trigtup;
    1072              :     int         tcl_rc;
    1073              :     int         i;
    1074              :     const char *result;
    1075              :     Tcl_Size    result_Objc;
    1076              :     Tcl_Obj   **result_Objv;
    1077              :     int         rc PG_USED_FOR_ASSERTS_ONLY;
    1078              : 
    1079           58 :     call_state->trigdata = trigdata;
    1080              : 
    1081              :     /* Connect to SPI manager */
    1082           58 :     SPI_connect();
    1083              : 
    1084              :     /* Make transition tables visible to this SPI connection */
    1085           58 :     rc = SPI_register_trigger_data(trigdata);
    1086              :     Assert(rc >= 0);
    1087              : 
    1088              :     /* Find or compile the function */
    1089          116 :     prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
    1090           58 :                                      RelationGetRelid(trigdata->tg_relation),
    1091              :                                      false, /* not an event trigger */
    1092              :                                      pltrusted);
    1093              : 
    1094           58 :     call_state->prodesc = prodesc;
    1095           58 :     prodesc->fn_refcount++;
    1096              : 
    1097           58 :     interp = prodesc->interp_desc->interp;
    1098              : 
    1099           58 :     tupdesc = RelationGetDescr(trigdata->tg_relation);
    1100              : 
    1101              :     /************************************************************
    1102              :      * Create the tcl command to call the internal
    1103              :      * proc in the interpreter
    1104              :      ************************************************************/
    1105           58 :     tcl_cmd = Tcl_NewObj();
    1106           58 :     Tcl_IncrRefCount(tcl_cmd);
    1107              : 
    1108           58 :     PG_TRY();
    1109              :     {
    1110              :         /* The procedure name (note this is all ASCII, so no utf_e2u) */
    1111           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1112           58 :                                  Tcl_NewStringObj(prodesc->internal_proname, -1));
    1113              : 
    1114              :         /* The trigger name for argument TG_name */
    1115           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1116           58 :                                  Tcl_NewStringObj(utf_e2u(trigdata->tg_trigger->tgname), -1));
    1117              : 
    1118              :         /* The oid of the trigger relation for argument TG_relid */
    1119              :         /* Consider not converting to a string for more performance? */
    1120           58 :         stroid = DatumGetCString(DirectFunctionCall1(oidout,
    1121              :                                                      ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
    1122           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1123              :                                  Tcl_NewStringObj(stroid, -1));
    1124           58 :         pfree(stroid);
    1125              : 
    1126              :         /* The name of the table the trigger is acting on: TG_table_name */
    1127           58 :         stroid = SPI_getrelname(trigdata->tg_relation);
    1128           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1129           58 :                                  Tcl_NewStringObj(utf_e2u(stroid), -1));
    1130           58 :         pfree(stroid);
    1131              : 
    1132              :         /* The schema of the table the trigger is acting on: TG_table_schema */
    1133           58 :         stroid = SPI_getnspname(trigdata->tg_relation);
    1134           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1135           58 :                                  Tcl_NewStringObj(utf_e2u(stroid), -1));
    1136           58 :         pfree(stroid);
    1137              : 
    1138              :         /* A list of attribute names for argument TG_relatts */
    1139           58 :         tcl_trigtup = Tcl_NewObj();
    1140           58 :         Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
    1141          265 :         for (i = 0; i < tupdesc->natts; i++)
    1142              :         {
    1143          207 :             Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    1144              : 
    1145          207 :             if (att->attisdropped)
    1146           13 :                 Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
    1147              :             else
    1148          194 :                 Tcl_ListObjAppendElement(NULL, tcl_trigtup,
    1149          194 :                                          Tcl_NewStringObj(utf_e2u(NameStr(att->attname)), -1));
    1150              :         }
    1151           58 :         Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
    1152              : 
    1153              :         /* The when part of the event for TG_when */
    1154           58 :         if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
    1155           47 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1156              :                                      Tcl_NewStringObj("BEFORE", -1));
    1157           11 :         else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
    1158            8 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1159              :                                      Tcl_NewStringObj("AFTER", -1));
    1160            3 :         else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
    1161            3 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1162              :                                      Tcl_NewStringObj("INSTEAD OF", -1));
    1163              :         else
    1164            0 :             elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
    1165              : 
    1166              :         /* The level part of the event for TG_level */
    1167           58 :         if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
    1168              :         {
    1169           50 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1170              :                                      Tcl_NewStringObj("ROW", -1));
    1171              : 
    1172              :             /*
    1173              :              * Now the command part of the event for TG_op and data for NEW
    1174              :              * and OLD
    1175              :              *
    1176              :              * Note: In BEFORE trigger, stored generated columns are not
    1177              :              * computed yet, so don't make them accessible in NEW row.
    1178              :              */
    1179           50 :             if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
    1180              :             {
    1181           30 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1182              :                                          Tcl_NewStringObj("INSERT", -1));
    1183              : 
    1184           30 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1185              :                                          pltcl_build_tuple_argument(trigdata->tg_trigtuple,
    1186              :                                                                     tupdesc,
    1187           30 :                                                                     !TRIGGER_FIRED_BEFORE(trigdata->tg_event)));
    1188           30 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
    1189              : 
    1190           30 :                 rettup = trigdata->tg_trigtuple;
    1191              :             }
    1192           20 :             else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
    1193              :             {
    1194            8 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1195              :                                          Tcl_NewStringObj("DELETE", -1));
    1196              : 
    1197            8 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
    1198            8 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1199              :                                          pltcl_build_tuple_argument(trigdata->tg_trigtuple,
    1200              :                                                                     tupdesc,
    1201              :                                                                     true));
    1202              : 
    1203            8 :                 rettup = trigdata->tg_trigtuple;
    1204              :             }
    1205           12 :             else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
    1206              :             {
    1207           12 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1208              :                                          Tcl_NewStringObj("UPDATE", -1));
    1209              : 
    1210           12 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1211              :                                          pltcl_build_tuple_argument(trigdata->tg_newtuple,
    1212              :                                                                     tupdesc,
    1213           12 :                                                                     !TRIGGER_FIRED_BEFORE(trigdata->tg_event)));
    1214           12 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1215              :                                          pltcl_build_tuple_argument(trigdata->tg_trigtuple,
    1216              :                                                                     tupdesc,
    1217              :                                                                     true));
    1218              : 
    1219           12 :                 rettup = trigdata->tg_newtuple;
    1220              :             }
    1221              :             else
    1222            0 :                 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
    1223              :         }
    1224            8 :         else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
    1225              :         {
    1226            8 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1227              :                                      Tcl_NewStringObj("STATEMENT", -1));
    1228              : 
    1229            8 :             if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
    1230            3 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1231              :                                          Tcl_NewStringObj("INSERT", -1));
    1232            5 :             else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
    1233            1 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1234              :                                          Tcl_NewStringObj("DELETE", -1));
    1235            4 :             else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
    1236            3 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1237              :                                          Tcl_NewStringObj("UPDATE", -1));
    1238            1 :             else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
    1239            1 :                 Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1240              :                                          Tcl_NewStringObj("TRUNCATE", -1));
    1241              :             else
    1242            0 :                 elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
    1243              : 
    1244            8 :             Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
    1245            8 :             Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
    1246              : 
    1247            8 :             rettup = (HeapTuple) NULL;
    1248              :         }
    1249              :         else
    1250            0 :             elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
    1251              : 
    1252              :         /* Finally append the arguments from CREATE TRIGGER */
    1253          135 :         for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
    1254           77 :             Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1255           77 :                                      Tcl_NewStringObj(utf_e2u(trigdata->tg_trigger->tgargs[i]), -1));
    1256              :     }
    1257            0 :     PG_CATCH();
    1258              :     {
    1259            0 :         Tcl_DecrRefCount(tcl_cmd);
    1260            0 :         PG_RE_THROW();
    1261              :     }
    1262           58 :     PG_END_TRY();
    1263              : 
    1264              :     /************************************************************
    1265              :      * Call the Tcl function
    1266              :      *
    1267              :      * We assume no PG error can be thrown directly from this call.
    1268              :      ************************************************************/
    1269           58 :     tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
    1270              : 
    1271              :     /* Release refcount to free tcl_cmd (and all subsidiary objects) */
    1272           58 :     Tcl_DecrRefCount(tcl_cmd);
    1273              : 
    1274              :     /************************************************************
    1275              :      * Check for errors reported by Tcl.
    1276              :      ************************************************************/
    1277           58 :     if (tcl_rc != TCL_OK)
    1278            7 :         throw_tcl_error(interp, prodesc->user_proname);
    1279              : 
    1280              :     /************************************************************
    1281              :      * Exit SPI environment.
    1282              :      ************************************************************/
    1283           51 :     if (SPI_finish() != SPI_OK_FINISH)
    1284            0 :         elog(ERROR, "SPI_finish() failed");
    1285              : 
    1286              :     /************************************************************
    1287              :      * The return value from the procedure might be one of
    1288              :      * the magic strings OK or SKIP, or a list from array get.
    1289              :      * We can check for OK or SKIP without worrying about encoding.
    1290              :      ************************************************************/
    1291           51 :     result = Tcl_GetStringResult(interp);
    1292              : 
    1293           51 :     if (strcmp(result, "OK") == 0)
    1294           40 :         return rettup;
    1295           11 :     if (strcmp(result, "SKIP") == 0)
    1296            1 :         return (HeapTuple) NULL;
    1297              : 
    1298              :     /************************************************************
    1299              :      * Otherwise, the return value should be a column name/value list
    1300              :      * specifying the modified tuple to return.
    1301              :      ************************************************************/
    1302           10 :     if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
    1303              :                                &result_Objc, &result_Objv) != TCL_OK)
    1304            0 :         ereport(ERROR,
    1305              :                 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    1306              :                  errmsg("could not parse trigger return value: %s",
    1307              :                         utf_u2e(Tcl_GetStringResult(interp)))));
    1308              : 
    1309              :     /* Convert function result to tuple */
    1310           10 :     rettup = pltcl_build_tuple_result(interp, result_Objv, result_Objc,
    1311              :                                       call_state);
    1312              : 
    1313            9 :     return rettup;
    1314              : }
    1315              : 
    1316              : /**********************************************************************
    1317              :  * pltcl_event_trigger_handler()    - Handler for event trigger calls
    1318              :  **********************************************************************/
    1319              : static void
    1320           10 : pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state,
    1321              :                             bool pltrusted)
    1322              : {
    1323              :     pltcl_proc_desc *prodesc;
    1324              :     Tcl_Interp *volatile interp;
    1325           10 :     EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
    1326              :     Tcl_Obj    *tcl_cmd;
    1327              :     int         tcl_rc;
    1328              : 
    1329              :     /* Connect to SPI manager */
    1330           10 :     SPI_connect();
    1331              : 
    1332              :     /* Find or compile the function */
    1333           10 :     prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
    1334              :                                      InvalidOid, true, pltrusted);
    1335              : 
    1336           10 :     call_state->prodesc = prodesc;
    1337           10 :     prodesc->fn_refcount++;
    1338              : 
    1339           10 :     interp = prodesc->interp_desc->interp;
    1340              : 
    1341              :     /* Create the tcl command and call the internal proc */
    1342           10 :     tcl_cmd = Tcl_NewObj();
    1343           10 :     Tcl_IncrRefCount(tcl_cmd);
    1344           10 :     Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1345           10 :                              Tcl_NewStringObj(prodesc->internal_proname, -1));
    1346           10 :     Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1347           10 :                              Tcl_NewStringObj(utf_e2u(tdata->event), -1));
    1348           10 :     Tcl_ListObjAppendElement(NULL, tcl_cmd,
    1349           10 :                              Tcl_NewStringObj(utf_e2u(GetCommandTagName(tdata->tag)),
    1350              :                                               -1));
    1351              : 
    1352           10 :     tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
    1353              : 
    1354              :     /* Release refcount to free tcl_cmd (and all subsidiary objects) */
    1355           10 :     Tcl_DecrRefCount(tcl_cmd);
    1356              : 
    1357              :     /* Check for errors reported by Tcl. */
    1358           10 :     if (tcl_rc != TCL_OK)
    1359            0 :         throw_tcl_error(interp, prodesc->user_proname);
    1360              : 
    1361           10 :     if (SPI_finish() != SPI_OK_FINISH)
    1362            0 :         elog(ERROR, "SPI_finish() failed");
    1363           10 : }
    1364              : 
    1365              : 
    1366              : /**********************************************************************
    1367              :  * throw_tcl_error  - ereport an error returned from the Tcl interpreter
    1368              :  *
    1369              :  * Caution: use this only to report errors returned by Tcl_EvalObjEx() or
    1370              :  * other variants of Tcl_Eval().  Other functions may not fill "errorInfo",
    1371              :  * so it could be unset or even contain details from some previous error.
    1372              :  **********************************************************************/
    1373              : static void
    1374           45 : throw_tcl_error(Tcl_Interp *interp, const char *proname)
    1375              : {
    1376              :     /*
    1377              :      * Caution is needed here because Tcl_GetVar could overwrite the
    1378              :      * interpreter result (even though it's not really supposed to), and we
    1379              :      * can't control the order of evaluation of ereport arguments. Hence, make
    1380              :      * real sure we have our own copy of the result string before invoking
    1381              :      * Tcl_GetVar.
    1382              :      */
    1383              :     char       *emsg;
    1384              :     char       *econtext;
    1385              :     int         emsglen;
    1386              : 
    1387           45 :     emsg = pstrdup(utf_u2e(Tcl_GetStringResult(interp)));
    1388           45 :     econtext = utf_u2e(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
    1389              : 
    1390              :     /*
    1391              :      * Typically, the first line of errorInfo matches the primary error
    1392              :      * message (the interpreter result); don't print that twice if so.
    1393              :      */
    1394           45 :     emsglen = strlen(emsg);
    1395           45 :     if (strncmp(emsg, econtext, emsglen) == 0 &&
    1396           45 :         econtext[emsglen] == '\n')
    1397           45 :         econtext += emsglen + 1;
    1398              : 
    1399              :     /* Tcl likes to prefix the next line with some spaces, too */
    1400          225 :     while (*econtext == ' ')
    1401          180 :         econtext++;
    1402              : 
    1403              :     /* Note: proname will already contain quoting if any is needed */
    1404           45 :     ereport(ERROR,
    1405              :             (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
    1406              :              errmsg("%s", emsg),
    1407              :              errcontext("%s\nin PL/Tcl function %s",
    1408              :                         econtext, proname)));
    1409              : }
    1410              : 
    1411              : 
    1412              : /**********************************************************************
    1413              :  * compile_pltcl_function   - compile (or hopefully just look up) function
    1414              :  *
    1415              :  * tgreloid is the OID of the relation when compiling a trigger, or zero
    1416              :  * (InvalidOid) when compiling a plain function.
    1417              :  **********************************************************************/
    1418              : static pltcl_proc_desc *
    1419          223 : compile_pltcl_function(Oid fn_oid, Oid tgreloid,
    1420              :                        bool is_event_trigger, bool pltrusted)
    1421              : {
    1422              :     HeapTuple   procTup;
    1423              :     Form_pg_proc procStruct;
    1424              :     pltcl_proc_key proc_key;
    1425              :     pltcl_proc_ptr *proc_ptr;
    1426              :     bool        found;
    1427              :     pltcl_proc_desc *prodesc;
    1428              :     pltcl_proc_desc *old_prodesc;
    1429          223 :     volatile MemoryContext proc_cxt = NULL;
    1430              :     Tcl_DString proc_internal_def;
    1431              :     Tcl_DString proc_internal_name;
    1432              :     Tcl_DString proc_internal_body;
    1433              : 
    1434              :     /* We'll need the pg_proc tuple in any case... */
    1435          223 :     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
    1436          223 :     if (!HeapTupleIsValid(procTup))
    1437            0 :         elog(ERROR, "cache lookup failed for function %u", fn_oid);
    1438          223 :     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
    1439              : 
    1440              :     /*
    1441              :      * Look up function in pltcl_proc_htab; if it's not there, create an entry
    1442              :      * and set the entry's proc_ptr to NULL.
    1443              :      */
    1444          223 :     proc_key.proc_id = fn_oid;
    1445          223 :     proc_key.is_trigger = OidIsValid(tgreloid);
    1446          223 :     proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
    1447              : 
    1448          223 :     proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
    1449              :                            HASH_ENTER,
    1450              :                            &found);
    1451          223 :     if (!found)
    1452           60 :         proc_ptr->proc_ptr = NULL;
    1453              : 
    1454          223 :     prodesc = proc_ptr->proc_ptr;
    1455              : 
    1456              :     /************************************************************
    1457              :      * If it's present, must check whether it's still up to date.
    1458              :      * This is needed because CREATE OR REPLACE FUNCTION can modify the
    1459              :      * function's pg_proc entry without changing its OID.
    1460              :      ************************************************************/
    1461          223 :     if (prodesc != NULL &&
    1462          160 :         prodesc->internal_proname != NULL &&
    1463          160 :         prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
    1464          158 :         ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
    1465              :     {
    1466              :         /* It's still up-to-date, so we can use it */
    1467          158 :         ReleaseSysCache(procTup);
    1468          158 :         return prodesc;
    1469              :     }
    1470              : 
    1471              :     /************************************************************
    1472              :      * If we haven't found it in the hashtable, we analyze
    1473              :      * the functions arguments and returntype and store
    1474              :      * the in-/out-functions in the prodesc block and create
    1475              :      * a new hashtable entry for it.
    1476              :      *
    1477              :      * Then we load the procedure into the Tcl interpreter.
    1478              :      ************************************************************/
    1479           65 :     Tcl_DStringInit(&proc_internal_def);
    1480           65 :     Tcl_DStringInit(&proc_internal_name);
    1481           65 :     Tcl_DStringInit(&proc_internal_body);
    1482           65 :     PG_TRY();
    1483              :     {
    1484           65 :         bool        is_trigger = OidIsValid(tgreloid);
    1485              :         Tcl_CmdInfo cmdinfo;
    1486              :         const char *user_proname;
    1487              :         const char *internal_proname;
    1488              :         bool        need_underscore;
    1489              :         HeapTuple   typeTup;
    1490              :         Form_pg_type typeStruct;
    1491              :         char        proc_internal_args[33 * FUNC_MAX_ARGS];
    1492              :         Datum       prosrcdatum;
    1493              :         char       *proc_source;
    1494              :         char        buf[48];
    1495              :         pltcl_interp_desc *interp_desc;
    1496              :         Tcl_Interp *interp;
    1497              :         int         i;
    1498              :         int         tcl_rc;
    1499              :         MemoryContext oldcontext;
    1500              : 
    1501              :         /************************************************************
    1502              :          * Identify the interpreter to use for the function
    1503              :          ************************************************************/
    1504           65 :         interp_desc = pltcl_fetch_interp(procStruct->prolang, pltrusted);
    1505           62 :         interp = interp_desc->interp;
    1506              : 
    1507              :         /************************************************************
    1508              :          * If redefining the function, try to remove the old internal
    1509              :          * procedure from Tcl's namespace.  The point of this is partly to
    1510              :          * allow re-use of the same internal proc name, and partly to avoid
    1511              :          * leaking the Tcl procedure object if we end up not choosing the same
    1512              :          * name.  We assume that Tcl is smart enough to not physically delete
    1513              :          * the procedure object if it's currently being executed.
    1514              :          ************************************************************/
    1515           62 :         if (prodesc != NULL &&
    1516            2 :             prodesc->internal_proname != NULL)
    1517              :         {
    1518              :             /* We simply ignore any error */
    1519            2 :             (void) Tcl_DeleteCommand(interp, prodesc->internal_proname);
    1520              :             /* Don't do this more than once */
    1521            2 :             prodesc->internal_proname = NULL;
    1522              :         }
    1523              : 
    1524              :         /************************************************************
    1525              :          * Build the proc name we'll use in error messages.
    1526              :          ************************************************************/
    1527           62 :         user_proname = format_procedure(fn_oid);
    1528              : 
    1529              :         /************************************************************
    1530              :          * Build the internal proc name from the user_proname and/or OID.
    1531              :          * The internal name must be all-ASCII since we don't want to deal
    1532              :          * with encoding conversions.  We don't want to worry about Tcl
    1533              :          * quoting rules either, so use only the characters of the function
    1534              :          * name that are ASCII alphanumerics, plus underscores to separate
    1535              :          * function name and arguments.  If what we end up with isn't
    1536              :          * unique (that is, it matches some existing Tcl command name),
    1537              :          * append the function OID (perhaps repeatedly) so that it is unique.
    1538              :          ************************************************************/
    1539              : 
    1540              :         /* For historical reasons, use a function-type-specific prefix */
    1541           62 :         if (is_event_trigger)
    1542            1 :             Tcl_DStringAppend(&proc_internal_name,
    1543              :                               "__PLTcl_evttrigger_", -1);
    1544           61 :         else if (is_trigger)
    1545            8 :             Tcl_DStringAppend(&proc_internal_name,
    1546              :                               "__PLTcl_trigger_", -1);
    1547              :         else
    1548           53 :             Tcl_DStringAppend(&proc_internal_name,
    1549              :                               "__PLTcl_proc_", -1);
    1550              :         /* Now add what we can from the user_proname */
    1551           62 :         need_underscore = false;
    1552         1415 :         for (const char *ptr = user_proname; *ptr; ptr++)
    1553              :         {
    1554         1353 :             if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    1555              :                        "abcdefghijklmnopqrstuvwxyz"
    1556         1353 :                        "0123456789_", *ptr) != NULL)
    1557              :             {
    1558              :                 /* Done this way to avoid adding a trailing underscore */
    1559         1211 :                 if (need_underscore)
    1560              :                 {
    1561           48 :                     Tcl_DStringAppend(&proc_internal_name, "_", 1);
    1562           48 :                     need_underscore = false;
    1563              :                 }
    1564         1211 :                 Tcl_DStringAppend(&proc_internal_name, ptr, 1);
    1565              :             }
    1566          142 :             else if (strchr("(, ", *ptr) != NULL)
    1567           76 :                 need_underscore = true;
    1568              :         }
    1569              :         /* If this name already exists, append fn_oid; repeat as needed */
    1570          125 :         while (Tcl_GetCommandInfo(interp,
    1571           63 :                                   Tcl_DStringValue(&proc_internal_name),
    1572              :                                   &cmdinfo))
    1573              :         {
    1574            1 :             snprintf(buf, sizeof(buf), "_%u", fn_oid);
    1575            1 :             Tcl_DStringAppend(&proc_internal_name, buf, -1);
    1576              :         }
    1577           62 :         internal_proname = Tcl_DStringValue(&proc_internal_name);
    1578              : 
    1579              :         /************************************************************
    1580              :          * Allocate a context that will hold all PG data for the procedure.
    1581              :          ************************************************************/
    1582           62 :         proc_cxt = AllocSetContextCreate(TopMemoryContext,
    1583              :                                          "PL/Tcl function",
    1584              :                                          ALLOCSET_SMALL_SIZES);
    1585              : 
    1586              :         /************************************************************
    1587              :          * Allocate and fill a new procedure description block.
    1588              :          * struct prodesc and subsidiary data must all live in proc_cxt.
    1589              :          ************************************************************/
    1590           62 :         oldcontext = MemoryContextSwitchTo(proc_cxt);
    1591           62 :         prodesc = palloc0_object(pltcl_proc_desc);
    1592           62 :         prodesc->user_proname = pstrdup(user_proname);
    1593           62 :         MemoryContextSetIdentifier(proc_cxt, prodesc->user_proname);
    1594           62 :         prodesc->internal_proname = pstrdup(internal_proname);
    1595           62 :         prodesc->fn_cxt = proc_cxt;
    1596           62 :         prodesc->fn_refcount = 0;
    1597           62 :         prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
    1598           62 :         prodesc->fn_tid = procTup->t_self;
    1599           62 :         prodesc->nargs = procStruct->pronargs;
    1600           62 :         prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
    1601           62 :         prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
    1602           62 :         MemoryContextSwitchTo(oldcontext);
    1603              : 
    1604              :         /* Remember if function is STABLE/IMMUTABLE */
    1605           62 :         prodesc->fn_readonly =
    1606           62 :             (procStruct->provolatile != PROVOLATILE_VOLATILE);
    1607              :         /* And whether it is trusted */
    1608           62 :         prodesc->lanpltrusted = pltrusted;
    1609              :         /* Save the associated interpreter, too */
    1610           62 :         prodesc->interp_desc = interp_desc;
    1611              : 
    1612              :         /************************************************************
    1613              :          * Get the required information for input conversion of the
    1614              :          * return value.
    1615              :          ************************************************************/
    1616           62 :         if (!is_trigger && !is_event_trigger)
    1617              :         {
    1618           53 :             Oid         rettype = procStruct->prorettype;
    1619              : 
    1620           53 :             typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
    1621           53 :             if (!HeapTupleIsValid(typeTup))
    1622            0 :                 elog(ERROR, "cache lookup failed for type %u", rettype);
    1623           53 :             typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
    1624              : 
    1625              :             /* Disallow pseudotype result, except VOID and RECORD */
    1626           53 :             if (typeStruct->typtype == TYPTYPE_PSEUDO)
    1627              :             {
    1628           24 :                 if (rettype == VOIDOID ||
    1629              :                     rettype == RECORDOID)
    1630              :                      /* okay */ ;
    1631            0 :                 else if (rettype == TRIGGEROID ||
    1632              :                          rettype == EVENT_TRIGGEROID)
    1633            0 :                     ereport(ERROR,
    1634              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1635              :                              errmsg("trigger functions can only be called as triggers")));
    1636              :                 else
    1637            0 :                     ereport(ERROR,
    1638              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1639              :                              errmsg("PL/Tcl functions cannot return type %s",
    1640              :                                     format_type_be(rettype))));
    1641              :             }
    1642              : 
    1643           53 :             prodesc->result_typid = rettype;
    1644           53 :             fmgr_info_cxt(typeStruct->typinput,
    1645              :                           &(prodesc->result_in_func),
    1646              :                           proc_cxt);
    1647           53 :             prodesc->result_typioparam = getTypeIOParam(typeTup);
    1648              : 
    1649           53 :             prodesc->fn_retisset = procStruct->proretset;
    1650           53 :             prodesc->fn_retistuple = type_is_rowtype(rettype);
    1651           53 :             prodesc->fn_retisdomain = (typeStruct->typtype == TYPTYPE_DOMAIN);
    1652           53 :             prodesc->domain_info = NULL;
    1653              : 
    1654           53 :             ReleaseSysCache(typeTup);
    1655              :         }
    1656              : 
    1657              :         /************************************************************
    1658              :          * Get the required information for output conversion
    1659              :          * of all procedure arguments, and set up argument naming info.
    1660              :          ************************************************************/
    1661           62 :         if (!is_trigger && !is_event_trigger)
    1662              :         {
    1663           53 :             proc_internal_args[0] = '\0';
    1664          101 :             for (i = 0; i < prodesc->nargs; i++)
    1665              :             {
    1666           48 :                 Oid         argtype = procStruct->proargtypes.values[i];
    1667              : 
    1668           48 :                 typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
    1669           48 :                 if (!HeapTupleIsValid(typeTup))
    1670            0 :                     elog(ERROR, "cache lookup failed for type %u", argtype);
    1671           48 :                 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
    1672              : 
    1673              :                 /* Disallow pseudotype argument, except RECORD */
    1674           48 :                 if (typeStruct->typtype == TYPTYPE_PSEUDO &&
    1675              :                     argtype != RECORDOID)
    1676            0 :                     ereport(ERROR,
    1677              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1678              :                              errmsg("PL/Tcl functions cannot accept type %s",
    1679              :                                     format_type_be(argtype))));
    1680              : 
    1681           48 :                 if (type_is_rowtype(argtype))
    1682              :                 {
    1683            4 :                     prodesc->arg_is_rowtype[i] = true;
    1684            4 :                     snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
    1685              :                 }
    1686              :                 else
    1687              :                 {
    1688           44 :                     prodesc->arg_is_rowtype[i] = false;
    1689           44 :                     fmgr_info_cxt(typeStruct->typoutput,
    1690           44 :                                   &(prodesc->arg_out_func[i]),
    1691              :                                   proc_cxt);
    1692           44 :                     snprintf(buf, sizeof(buf), "%d", i + 1);
    1693              :                 }
    1694              : 
    1695           48 :                 if (i > 0)
    1696           14 :                     strcat(proc_internal_args, " ");
    1697           48 :                 strcat(proc_internal_args, buf);
    1698              : 
    1699           48 :                 ReleaseSysCache(typeTup);
    1700              :             }
    1701              :         }
    1702            9 :         else if (is_trigger)
    1703              :         {
    1704              :             /* trigger procedure has fixed args */
    1705            8 :             strcpy(proc_internal_args,
    1706              :                    "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");
    1707              :         }
    1708            1 :         else if (is_event_trigger)
    1709              :         {
    1710              :             /* event trigger procedure has fixed args */
    1711            1 :             strcpy(proc_internal_args, "TG_event TG_tag");
    1712              :         }
    1713              : 
    1714              :         /************************************************************
    1715              :          * Create the tcl command to define the internal
    1716              :          * procedure
    1717              :          *
    1718              :          * Leave this code as DString - performance is not critical here,
    1719              :          * and we don't want to duplicate the knowledge of the Tcl quoting
    1720              :          * rules that's embedded in Tcl_DStringAppendElement.
    1721              :          ************************************************************/
    1722           62 :         Tcl_DStringAppendElement(&proc_internal_def, "proc");
    1723           62 :         Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
    1724           62 :         Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
    1725              : 
    1726              :         /************************************************************
    1727              :          * prefix procedure body with
    1728              :          * upvar #0 <internal_proname> GD
    1729              :          * and with appropriate setting of arguments
    1730              :          ************************************************************/
    1731           62 :         Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
    1732           62 :         Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
    1733           62 :         Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
    1734           62 :         if (is_trigger)
    1735              :         {
    1736            8 :             Tcl_DStringAppend(&proc_internal_body,
    1737              :                               "array set NEW $__PLTcl_Tup_NEW\n", -1);
    1738            8 :             Tcl_DStringAppend(&proc_internal_body,
    1739              :                               "array set OLD $__PLTcl_Tup_OLD\n", -1);
    1740            8 :             Tcl_DStringAppend(&proc_internal_body,
    1741              :                               "set i 0\n"
    1742              :                               "set v 0\n"
    1743              :                               "foreach v $args {\n"
    1744              :                               "  incr i\n"
    1745              :                               "  set $i $v\n"
    1746              :                               "}\n"
    1747              :                               "unset i v\n\n", -1);
    1748              :         }
    1749           54 :         else if (is_event_trigger)
    1750              :         {
    1751              :             /* no argument support for event triggers */
    1752              :         }
    1753              :         else
    1754              :         {
    1755          101 :             for (i = 0; i < prodesc->nargs; i++)
    1756              :             {
    1757           48 :                 if (prodesc->arg_is_rowtype[i])
    1758              :                 {
    1759            4 :                     snprintf(buf, sizeof(buf),
    1760              :                              "array set %d $__PLTcl_Tup_%d\n",
    1761              :                              i + 1, i + 1);
    1762            4 :                     Tcl_DStringAppend(&proc_internal_body, buf, -1);
    1763              :                 }
    1764              :             }
    1765              :         }
    1766              : 
    1767              :         /************************************************************
    1768              :          * Add user's function definition to proc body
    1769              :          ************************************************************/
    1770           62 :         prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
    1771              :                                              Anum_pg_proc_prosrc);
    1772           62 :         proc_source = TextDatumGetCString(prosrcdatum);
    1773           62 :         UTF_BEGIN;
    1774           62 :         Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
    1775           62 :         UTF_END;
    1776           62 :         pfree(proc_source);
    1777           62 :         Tcl_DStringAppendElement(&proc_internal_def,
    1778           62 :                                  Tcl_DStringValue(&proc_internal_body));
    1779              : 
    1780              :         /************************************************************
    1781              :          * Create the procedure in the interpreter
    1782              :          ************************************************************/
    1783          124 :         tcl_rc = Tcl_EvalEx(interp,
    1784           62 :                             Tcl_DStringValue(&proc_internal_def),
    1785              :                             Tcl_DStringLength(&proc_internal_def),
    1786              :                             TCL_EVAL_GLOBAL);
    1787           62 :         if (tcl_rc != TCL_OK)
    1788            0 :             ereport(ERROR,
    1789              :                     (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
    1790              :                      errmsg("could not create internal procedure \"%s\": %s",
    1791              :                             internal_proname,
    1792              :                             utf_u2e(Tcl_GetStringResult(interp)))));
    1793              :     }
    1794            3 :     PG_CATCH();
    1795              :     {
    1796              :         /*
    1797              :          * If we failed anywhere above, clean up whatever got allocated.  It
    1798              :          * should all be in the proc_cxt, except for the DStrings.
    1799              :          */
    1800            3 :         if (proc_cxt)
    1801            0 :             MemoryContextDelete(proc_cxt);
    1802            3 :         Tcl_DStringFree(&proc_internal_def);
    1803            3 :         Tcl_DStringFree(&proc_internal_name);
    1804            3 :         Tcl_DStringFree(&proc_internal_body);
    1805            3 :         PG_RE_THROW();
    1806              :     }
    1807           62 :     PG_END_TRY();
    1808              : 
    1809              :     /*
    1810              :      * Install the new proc description block in the hashtable, incrementing
    1811              :      * its refcount (the hashtable link counts as a reference).  Then, if
    1812              :      * there was a previous definition of the function, decrement that one's
    1813              :      * refcount, and delete it if no longer referenced.  The order of
    1814              :      * operations here is important: if something goes wrong during the
    1815              :      * MemoryContextDelete, leaking some memory for the old definition is OK,
    1816              :      * but we don't want to corrupt the live hashtable entry.  (Likewise,
    1817              :      * freeing the DStrings is pretty low priority if that happens.)
    1818              :      */
    1819           62 :     old_prodesc = proc_ptr->proc_ptr;
    1820              : 
    1821           62 :     proc_ptr->proc_ptr = prodesc;
    1822           62 :     prodesc->fn_refcount++;
    1823              : 
    1824           62 :     if (old_prodesc != NULL)
    1825              :     {
    1826              :         Assert(old_prodesc->fn_refcount > 0);
    1827            2 :         if (--old_prodesc->fn_refcount == 0)
    1828            1 :             MemoryContextDelete(old_prodesc->fn_cxt);
    1829              :     }
    1830              : 
    1831           62 :     Tcl_DStringFree(&proc_internal_def);
    1832           62 :     Tcl_DStringFree(&proc_internal_name);
    1833           62 :     Tcl_DStringFree(&proc_internal_body);
    1834              : 
    1835           62 :     ReleaseSysCache(procTup);
    1836              : 
    1837           62 :     return prodesc;
    1838              : }
    1839              : 
    1840              : 
    1841              : /**********************************************************************
    1842              :  * pltcl_elog()     - elog() support for PLTcl
    1843              :  **********************************************************************/
    1844              : static int
    1845          266 : pltcl_elog(ClientData cdata, Tcl_Interp *interp,
    1846              :            int objc, Tcl_Obj *const objv[])
    1847              : {
    1848              :     volatile int level;
    1849              :     MemoryContext oldcontext;
    1850              :     int         priIndex;
    1851              : 
    1852              :     static const char *logpriorities[] = {
    1853              :         "DEBUG", "LOG", "INFO", "NOTICE",
    1854              :         "WARNING", "ERROR", "FATAL", (const char *) NULL
    1855              :     };
    1856              : 
    1857              :     static const int loglevels[] = {
    1858              :         DEBUG2, LOG, INFO, NOTICE,
    1859              :         WARNING, ERROR, FATAL
    1860              :     };
    1861              : 
    1862          266 :     if (objc != 3)
    1863              :     {
    1864            1 :         Tcl_WrongNumArgs(interp, 1, objv, "level msg");
    1865            1 :         return TCL_ERROR;
    1866              :     }
    1867              : 
    1868          265 :     if (Tcl_GetIndexFromObj(interp, objv[1], logpriorities, "priority",
    1869              :                             TCL_EXACT, &priIndex) != TCL_OK)
    1870            1 :         return TCL_ERROR;
    1871              : 
    1872          264 :     level = loglevels[priIndex];
    1873              : 
    1874          264 :     if (level == ERROR)
    1875              :     {
    1876              :         /*
    1877              :          * We just pass the error back to Tcl.  If it's not caught, it'll
    1878              :          * eventually get converted to a PG error when we reach the call
    1879              :          * handler.
    1880              :          */
    1881            6 :         Tcl_SetObjResult(interp, objv[2]);
    1882            6 :         return TCL_ERROR;
    1883              :     }
    1884              : 
    1885              :     /*
    1886              :      * For non-error messages, just pass 'em to ereport().  We do not expect
    1887              :      * that this will fail, but just on the off chance it does, report the
    1888              :      * error back to Tcl.  Note we are assuming that ereport() can't have any
    1889              :      * internal failures that are so bad as to require a transaction abort.
    1890              :      *
    1891              :      * This path is also used for FATAL errors, which aren't going to come
    1892              :      * back to us at all.
    1893              :      */
    1894          258 :     oldcontext = CurrentMemoryContext;
    1895          258 :     PG_TRY();
    1896              :     {
    1897          258 :         UTF_BEGIN;
    1898          258 :         ereport(level,
    1899              :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
    1900              :                  errmsg("%s", UTF_U2E(Tcl_GetString(objv[2])))));
    1901          258 :         UTF_END;
    1902              :     }
    1903            0 :     PG_CATCH();
    1904              :     {
    1905              :         ErrorData  *edata;
    1906              : 
    1907              :         /* Must reset elog.c's state */
    1908            0 :         MemoryContextSwitchTo(oldcontext);
    1909            0 :         edata = CopyErrorData();
    1910            0 :         FlushErrorState();
    1911              : 
    1912              :         /* Pass the error data to Tcl */
    1913            0 :         pltcl_construct_errorCode(interp, edata);
    1914            0 :         UTF_BEGIN;
    1915            0 :         Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
    1916            0 :         UTF_END;
    1917            0 :         FreeErrorData(edata);
    1918              : 
    1919            0 :         return TCL_ERROR;
    1920              :     }
    1921          258 :     PG_END_TRY();
    1922              : 
    1923          258 :     return TCL_OK;
    1924              : }
    1925              : 
    1926              : 
    1927              : /**********************************************************************
    1928              :  * pltcl_construct_errorCode()      - construct a Tcl errorCode
    1929              :  *      list with detailed information from the PostgreSQL server
    1930              :  **********************************************************************/
    1931              : static void
    1932           18 : pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
    1933              : {
    1934           18 :     Tcl_Obj    *obj = Tcl_NewObj();
    1935              : 
    1936           18 :     Tcl_ListObjAppendElement(interp, obj,
    1937              :                              Tcl_NewStringObj("POSTGRES", -1));
    1938           18 :     Tcl_ListObjAppendElement(interp, obj,
    1939              :                              Tcl_NewStringObj(PG_VERSION, -1));
    1940           18 :     Tcl_ListObjAppendElement(interp, obj,
    1941              :                              Tcl_NewStringObj("SQLSTATE", -1));
    1942           18 :     Tcl_ListObjAppendElement(interp, obj,
    1943           18 :                              Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode), -1));
    1944           18 :     Tcl_ListObjAppendElement(interp, obj,
    1945              :                              Tcl_NewStringObj("condition", -1));
    1946           18 :     Tcl_ListObjAppendElement(interp, obj,
    1947              :                              Tcl_NewStringObj(pltcl_get_condition_name(edata->sqlerrcode), -1));
    1948           18 :     Tcl_ListObjAppendElement(interp, obj,
    1949              :                              Tcl_NewStringObj("message", -1));
    1950           18 :     UTF_BEGIN;
    1951           18 :     Tcl_ListObjAppendElement(interp, obj,
    1952           18 :                              Tcl_NewStringObj(UTF_E2U(edata->message), -1));
    1953           18 :     UTF_END;
    1954           18 :     if (edata->detail)
    1955              :     {
    1956            3 :         Tcl_ListObjAppendElement(interp, obj,
    1957              :                                  Tcl_NewStringObj("detail", -1));
    1958            3 :         UTF_BEGIN;
    1959            3 :         Tcl_ListObjAppendElement(interp, obj,
    1960            3 :                                  Tcl_NewStringObj(UTF_E2U(edata->detail), -1));
    1961            3 :         UTF_END;
    1962              :     }
    1963           18 :     if (edata->hint)
    1964              :     {
    1965            1 :         Tcl_ListObjAppendElement(interp, obj,
    1966              :                                  Tcl_NewStringObj("hint", -1));
    1967            1 :         UTF_BEGIN;
    1968            1 :         Tcl_ListObjAppendElement(interp, obj,
    1969            1 :                                  Tcl_NewStringObj(UTF_E2U(edata->hint), -1));
    1970            1 :         UTF_END;
    1971              :     }
    1972           18 :     if (edata->context)
    1973              :     {
    1974            9 :         Tcl_ListObjAppendElement(interp, obj,
    1975              :                                  Tcl_NewStringObj("context", -1));
    1976            9 :         UTF_BEGIN;
    1977            9 :         Tcl_ListObjAppendElement(interp, obj,
    1978            9 :                                  Tcl_NewStringObj(UTF_E2U(edata->context), -1));
    1979            9 :         UTF_END;
    1980              :     }
    1981           18 :     if (edata->schema_name)
    1982              :     {
    1983            3 :         Tcl_ListObjAppendElement(interp, obj,
    1984              :                                  Tcl_NewStringObj("schema", -1));
    1985            3 :         UTF_BEGIN;
    1986            3 :         Tcl_ListObjAppendElement(interp, obj,
    1987            3 :                                  Tcl_NewStringObj(UTF_E2U(edata->schema_name), -1));
    1988            3 :         UTF_END;
    1989              :     }
    1990           18 :     if (edata->table_name)
    1991              :     {
    1992            3 :         Tcl_ListObjAppendElement(interp, obj,
    1993              :                                  Tcl_NewStringObj("table", -1));
    1994            3 :         UTF_BEGIN;
    1995            3 :         Tcl_ListObjAppendElement(interp, obj,
    1996            3 :                                  Tcl_NewStringObj(UTF_E2U(edata->table_name), -1));
    1997            3 :         UTF_END;
    1998              :     }
    1999           18 :     if (edata->column_name)
    2000              :     {
    2001            1 :         Tcl_ListObjAppendElement(interp, obj,
    2002              :                                  Tcl_NewStringObj("column", -1));
    2003            1 :         UTF_BEGIN;
    2004            1 :         Tcl_ListObjAppendElement(interp, obj,
    2005            1 :                                  Tcl_NewStringObj(UTF_E2U(edata->column_name), -1));
    2006            1 :         UTF_END;
    2007              :     }
    2008           18 :     if (edata->datatype_name)
    2009              :     {
    2010            1 :         Tcl_ListObjAppendElement(interp, obj,
    2011              :                                  Tcl_NewStringObj("datatype", -1));
    2012            1 :         UTF_BEGIN;
    2013            1 :         Tcl_ListObjAppendElement(interp, obj,
    2014            1 :                                  Tcl_NewStringObj(UTF_E2U(edata->datatype_name), -1));
    2015            1 :         UTF_END;
    2016              :     }
    2017           18 :     if (edata->constraint_name)
    2018              :     {
    2019            3 :         Tcl_ListObjAppendElement(interp, obj,
    2020              :                                  Tcl_NewStringObj("constraint", -1));
    2021            3 :         UTF_BEGIN;
    2022            3 :         Tcl_ListObjAppendElement(interp, obj,
    2023            3 :                                  Tcl_NewStringObj(UTF_E2U(edata->constraint_name), -1));
    2024            3 :         UTF_END;
    2025              :     }
    2026              :     /* cursorpos is never interesting here; report internal query/pos */
    2027           18 :     if (edata->internalquery)
    2028              :     {
    2029            4 :         Tcl_ListObjAppendElement(interp, obj,
    2030              :                                  Tcl_NewStringObj("statement", -1));
    2031            4 :         UTF_BEGIN;
    2032            4 :         Tcl_ListObjAppendElement(interp, obj,
    2033            4 :                                  Tcl_NewStringObj(UTF_E2U(edata->internalquery), -1));
    2034            4 :         UTF_END;
    2035              :     }
    2036           18 :     if (edata->internalpos > 0)
    2037              :     {
    2038            4 :         Tcl_ListObjAppendElement(interp, obj,
    2039              :                                  Tcl_NewStringObj("cursor_position", -1));
    2040            4 :         Tcl_ListObjAppendElement(interp, obj,
    2041              :                                  Tcl_NewIntObj(edata->internalpos));
    2042              :     }
    2043           18 :     if (edata->filename)
    2044              :     {
    2045           18 :         Tcl_ListObjAppendElement(interp, obj,
    2046              :                                  Tcl_NewStringObj("filename", -1));
    2047           18 :         UTF_BEGIN;
    2048           18 :         Tcl_ListObjAppendElement(interp, obj,
    2049           18 :                                  Tcl_NewStringObj(UTF_E2U(edata->filename), -1));
    2050           18 :         UTF_END;
    2051              :     }
    2052           18 :     if (edata->lineno > 0)
    2053              :     {
    2054           18 :         Tcl_ListObjAppendElement(interp, obj,
    2055              :                                  Tcl_NewStringObj("lineno", -1));
    2056           18 :         Tcl_ListObjAppendElement(interp, obj,
    2057              :                                  Tcl_NewIntObj(edata->lineno));
    2058              :     }
    2059           18 :     if (edata->funcname)
    2060              :     {
    2061           18 :         Tcl_ListObjAppendElement(interp, obj,
    2062              :                                  Tcl_NewStringObj("funcname", -1));
    2063           18 :         UTF_BEGIN;
    2064           18 :         Tcl_ListObjAppendElement(interp, obj,
    2065           18 :                                  Tcl_NewStringObj(UTF_E2U(edata->funcname), -1));
    2066           18 :         UTF_END;
    2067              :     }
    2068              : 
    2069           18 :     Tcl_SetObjErrorCode(interp, obj);
    2070           18 : }
    2071              : 
    2072              : 
    2073              : /**********************************************************************
    2074              :  * pltcl_get_condition_name()   - find name for SQLSTATE
    2075              :  **********************************************************************/
    2076              : static const char *
    2077           18 : pltcl_get_condition_name(int sqlstate)
    2078              : {
    2079              :     int         i;
    2080              : 
    2081         2279 :     for (i = 0; exception_name_map[i].label != NULL; i++)
    2082              :     {
    2083         2279 :         if (exception_name_map[i].sqlerrstate == sqlstate)
    2084           18 :             return exception_name_map[i].label;
    2085              :     }
    2086            0 :     return "unrecognized_sqlstate";
    2087              : }
    2088              : 
    2089              : 
    2090              : /**********************************************************************
    2091              :  * pltcl_quote()    - quote literal strings that are to
    2092              :  *            be used in SPI_execute query strings
    2093              :  **********************************************************************/
    2094              : static int
    2095           11 : pltcl_quote(ClientData cdata, Tcl_Interp *interp,
    2096              :             int objc, Tcl_Obj *const objv[])
    2097              : {
    2098              :     char       *tmp;
    2099              :     const char *cp1;
    2100              :     char       *cp2;
    2101              :     Tcl_Size    length;
    2102              : 
    2103              :     /************************************************************
    2104              :      * Check call syntax
    2105              :      ************************************************************/
    2106           11 :     if (objc != 2)
    2107              :     {
    2108            1 :         Tcl_WrongNumArgs(interp, 1, objv, "string");
    2109            1 :         return TCL_ERROR;
    2110              :     }
    2111              : 
    2112              :     /************************************************************
    2113              :      * Allocate space for the maximum the string can
    2114              :      * grow to and initialize pointers
    2115              :      ************************************************************/
    2116           10 :     cp1 = Tcl_GetStringFromObj(objv[1], &length);
    2117           10 :     tmp = palloc(length * 2 + 1);
    2118           10 :     cp2 = tmp;
    2119              : 
    2120              :     /************************************************************
    2121              :      * Walk through string and double every quote and backslash
    2122              :      ************************************************************/
    2123           56 :     while (*cp1)
    2124              :     {
    2125           46 :         if (*cp1 == '\'')
    2126            1 :             *cp2++ = '\'';
    2127              :         else
    2128              :         {
    2129           45 :             if (*cp1 == '\\')
    2130            1 :                 *cp2++ = '\\';
    2131              :         }
    2132           46 :         *cp2++ = *cp1++;
    2133              :     }
    2134              : 
    2135              :     /************************************************************
    2136              :      * Terminate the string and set it as result
    2137              :      ************************************************************/
    2138           10 :     *cp2 = '\0';
    2139           10 :     Tcl_SetObjResult(interp, Tcl_NewStringObj(tmp, -1));
    2140           10 :     pfree(tmp);
    2141           10 :     return TCL_OK;
    2142              : }
    2143              : 
    2144              : 
    2145              : /**********************************************************************
    2146              :  * pltcl_argisnull()    - determine if a specific argument is NULL
    2147              :  **********************************************************************/
    2148              : static int
    2149            7 : pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
    2150              :                 int objc, Tcl_Obj *const objv[])
    2151              : {
    2152              :     int         argno;
    2153            7 :     FunctionCallInfo fcinfo = pltcl_current_call_state->fcinfo;
    2154              : 
    2155              :     /************************************************************
    2156              :      * Check call syntax
    2157              :      ************************************************************/
    2158            7 :     if (objc != 2)
    2159              :     {
    2160            1 :         Tcl_WrongNumArgs(interp, 1, objv, "argno");
    2161            1 :         return TCL_ERROR;
    2162              :     }
    2163              : 
    2164              :     /************************************************************
    2165              :      * Check that we're called as a normal function
    2166              :      ************************************************************/
    2167            6 :     if (fcinfo == NULL)
    2168              :     {
    2169            1 :         Tcl_SetObjResult(interp,
    2170              :                          Tcl_NewStringObj("argisnull cannot be used in triggers", -1));
    2171            1 :         return TCL_ERROR;
    2172              :     }
    2173              : 
    2174              :     /************************************************************
    2175              :      * Get the argument number
    2176              :      ************************************************************/
    2177            5 :     if (Tcl_GetIntFromObj(interp, objv[1], &argno) != TCL_OK)
    2178            1 :         return TCL_ERROR;
    2179              : 
    2180              :     /************************************************************
    2181              :      * Check that the argno is valid
    2182              :      ************************************************************/
    2183            4 :     argno--;
    2184            4 :     if (argno < 0 || argno >= fcinfo->nargs)
    2185              :     {
    2186            1 :         Tcl_SetObjResult(interp,
    2187              :                          Tcl_NewStringObj("argno out of range", -1));
    2188            1 :         return TCL_ERROR;
    2189              :     }
    2190              : 
    2191              :     /************************************************************
    2192              :      * Get the requested NULL state
    2193              :      ************************************************************/
    2194            3 :     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(PG_ARGISNULL(argno)));
    2195            3 :     return TCL_OK;
    2196              : }
    2197              : 
    2198              : 
    2199              : /**********************************************************************
    2200              :  * pltcl_returnnull()   - Cause a NULL return from the current function
    2201              :  **********************************************************************/
    2202              : static int
    2203            3 : pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
    2204              :                  int objc, Tcl_Obj *const objv[])
    2205              : {
    2206            3 :     FunctionCallInfo fcinfo = pltcl_current_call_state->fcinfo;
    2207              : 
    2208              :     /************************************************************
    2209              :      * Check call syntax
    2210              :      ************************************************************/
    2211            3 :     if (objc != 1)
    2212              :     {
    2213            1 :         Tcl_WrongNumArgs(interp, 1, objv, "");
    2214            1 :         return TCL_ERROR;
    2215              :     }
    2216              : 
    2217              :     /************************************************************
    2218              :      * Check that we're called as a normal function
    2219              :      ************************************************************/
    2220            2 :     if (fcinfo == NULL)
    2221              :     {
    2222            1 :         Tcl_SetObjResult(interp,
    2223              :                          Tcl_NewStringObj("return_null cannot be used in triggers", -1));
    2224            1 :         return TCL_ERROR;
    2225              :     }
    2226              : 
    2227              :     /************************************************************
    2228              :      * Set the NULL return flag and cause Tcl to return from the
    2229              :      * procedure.
    2230              :      ************************************************************/
    2231            1 :     fcinfo->isnull = true;
    2232              : 
    2233            1 :     return TCL_RETURN;
    2234              : }
    2235              : 
    2236              : 
    2237              : /**********************************************************************
    2238              :  * pltcl_returnnext()   - Add a row to the result tuplestore in a SRF.
    2239              :  **********************************************************************/
    2240              : static int
    2241           18 : pltcl_returnnext(ClientData cdata, Tcl_Interp *interp,
    2242              :                  int objc, Tcl_Obj *const objv[])
    2243              : {
    2244           18 :     pltcl_call_state *call_state = pltcl_current_call_state;
    2245           18 :     FunctionCallInfo fcinfo = call_state->fcinfo;
    2246           18 :     pltcl_proc_desc *prodesc = call_state->prodesc;
    2247           18 :     MemoryContext oldcontext = CurrentMemoryContext;
    2248           18 :     ResourceOwner oldowner = CurrentResourceOwner;
    2249           18 :     volatile int result = TCL_OK;
    2250              : 
    2251              :     /*
    2252              :      * Check that we're called as a set-returning function
    2253              :      */
    2254           18 :     if (fcinfo == NULL)
    2255              :     {
    2256            0 :         Tcl_SetObjResult(interp,
    2257              :                          Tcl_NewStringObj("return_next cannot be used in triggers", -1));
    2258            0 :         return TCL_ERROR;
    2259              :     }
    2260              : 
    2261           18 :     if (!prodesc->fn_retisset)
    2262              :     {
    2263            1 :         Tcl_SetObjResult(interp,
    2264              :                          Tcl_NewStringObj("return_next cannot be used in non-set-returning functions", -1));
    2265            1 :         return TCL_ERROR;
    2266              :     }
    2267              : 
    2268              :     /*
    2269              :      * Check call syntax
    2270              :      */
    2271           17 :     if (objc != 2)
    2272              :     {
    2273            0 :         Tcl_WrongNumArgs(interp, 1, objv, "result");
    2274            0 :         return TCL_ERROR;
    2275              :     }
    2276              : 
    2277              :     /*
    2278              :      * The rest might throw elog(ERROR), so must run in a subtransaction.
    2279              :      *
    2280              :      * A small advantage of using a subtransaction is that it provides a
    2281              :      * short-lived memory context for free, so we needn't worry about leaking
    2282              :      * memory here.  To use that context, call BeginInternalSubTransaction
    2283              :      * directly instead of going through pltcl_subtrans_begin.
    2284              :      */
    2285           17 :     BeginInternalSubTransaction(NULL);
    2286           17 :     PG_TRY();
    2287              :     {
    2288              :         /* Set up tuple store if first output row */
    2289           17 :         if (call_state->tuple_store == NULL)
    2290            5 :             pltcl_init_tuple_store(call_state);
    2291              : 
    2292           17 :         if (prodesc->fn_retistuple)
    2293              :         {
    2294              :             Tcl_Obj   **rowObjv;
    2295              :             Tcl_Size    rowObjc;
    2296              : 
    2297              :             /* result should be a list, so break it down */
    2298            7 :             if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
    2299            0 :                 result = TCL_ERROR;
    2300              :             else
    2301              :             {
    2302              :                 HeapTuple   tuple;
    2303              : 
    2304            7 :                 tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
    2305              :                                                  call_state);
    2306            5 :                 tuplestore_puttuple(call_state->tuple_store, tuple);
    2307              :             }
    2308              :         }
    2309              :         else
    2310              :         {
    2311              :             Datum       retval;
    2312           10 :             bool        isNull = false;
    2313              : 
    2314              :             /* for paranoia's sake, check that tupdesc has exactly one column */
    2315           10 :             if (call_state->ret_tupdesc->natts != 1)
    2316            0 :                 elog(ERROR, "wrong result type supplied in return_next");
    2317              : 
    2318           10 :             retval = InputFunctionCall(&prodesc->result_in_func,
    2319           10 :                                        utf_u2e((char *) Tcl_GetString(objv[1])),
    2320              :                                        prodesc->result_typioparam,
    2321              :                                        -1);
    2322           10 :             tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
    2323              :                                  &retval, &isNull);
    2324              :         }
    2325              : 
    2326           15 :         pltcl_subtrans_commit(oldcontext, oldowner);
    2327              :     }
    2328            2 :     PG_CATCH();
    2329              :     {
    2330            2 :         pltcl_subtrans_abort(interp, oldcontext, oldowner);
    2331            2 :         return TCL_ERROR;
    2332              :     }
    2333           15 :     PG_END_TRY();
    2334              : 
    2335           15 :     return result;
    2336              : }
    2337              : 
    2338              : 
    2339              : /*----------
    2340              :  * Support for running SPI operations inside subtransactions
    2341              :  *
    2342              :  * Intended usage pattern is:
    2343              :  *
    2344              :  *  MemoryContext oldcontext = CurrentMemoryContext;
    2345              :  *  ResourceOwner oldowner = CurrentResourceOwner;
    2346              :  *
    2347              :  *  ...
    2348              :  *  pltcl_subtrans_begin(oldcontext, oldowner);
    2349              :  *  PG_TRY();
    2350              :  *  {
    2351              :  *      do something risky;
    2352              :  *      pltcl_subtrans_commit(oldcontext, oldowner);
    2353              :  *  }
    2354              :  *  PG_CATCH();
    2355              :  *  {
    2356              :  *      pltcl_subtrans_abort(interp, oldcontext, oldowner);
    2357              :  *      return TCL_ERROR;
    2358              :  *  }
    2359              :  *  PG_END_TRY();
    2360              :  *  return TCL_OK;
    2361              :  *----------
    2362              :  */
    2363              : static void
    2364          124 : pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
    2365              : {
    2366          124 :     BeginInternalSubTransaction(NULL);
    2367              : 
    2368              :     /* Want to run inside function's memory context */
    2369          124 :     MemoryContextSwitchTo(oldcontext);
    2370          124 : }
    2371              : 
    2372              : static void
    2373          129 : pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
    2374              : {
    2375              :     /* Commit the inner transaction, return to outer xact context */
    2376          129 :     ReleaseCurrentSubTransaction();
    2377          129 :     MemoryContextSwitchTo(oldcontext);
    2378          129 :     CurrentResourceOwner = oldowner;
    2379          129 : }
    2380              : 
    2381              : static void
    2382           12 : pltcl_subtrans_abort(Tcl_Interp *interp,
    2383              :                      MemoryContext oldcontext, ResourceOwner oldowner)
    2384              : {
    2385              :     ErrorData  *edata;
    2386              : 
    2387              :     /* Save error info */
    2388           12 :     MemoryContextSwitchTo(oldcontext);
    2389           12 :     edata = CopyErrorData();
    2390           12 :     FlushErrorState();
    2391              : 
    2392              :     /* Abort the inner transaction */
    2393           12 :     RollbackAndReleaseCurrentSubTransaction();
    2394           12 :     MemoryContextSwitchTo(oldcontext);
    2395           12 :     CurrentResourceOwner = oldowner;
    2396              : 
    2397              :     /* Pass the error data to Tcl */
    2398           12 :     pltcl_construct_errorCode(interp, edata);
    2399           12 :     UTF_BEGIN;
    2400           12 :     Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
    2401           12 :     UTF_END;
    2402           12 :     FreeErrorData(edata);
    2403           12 : }
    2404              : 
    2405              : 
    2406              : /**********************************************************************
    2407              :  * pltcl_SPI_execute()      - The builtin SPI_execute command
    2408              :  *                for the Tcl interpreter
    2409              :  **********************************************************************/
    2410              : static int
    2411           65 : pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
    2412              :                   int objc, Tcl_Obj *const objv[])
    2413              : {
    2414              :     int         my_rc;
    2415              :     int         spi_rc;
    2416              :     int         query_idx;
    2417              :     int         i;
    2418              :     int         optIndex;
    2419           65 :     int         count = 0;
    2420           65 :     const char *volatile arrayname = NULL;
    2421           65 :     Tcl_Obj    *volatile loop_body = NULL;
    2422           65 :     MemoryContext oldcontext = CurrentMemoryContext;
    2423           65 :     ResourceOwner oldowner = CurrentResourceOwner;
    2424              : 
    2425              :     enum options
    2426              :     {
    2427              :         OPT_ARRAY, OPT_COUNT
    2428              :     };
    2429              : 
    2430              :     static const char *options[] = {
    2431              :         "-array", "-count", (const char *) NULL
    2432              :     };
    2433              : 
    2434              :     /************************************************************
    2435              :      * Check the call syntax and get the options
    2436              :      ************************************************************/
    2437           65 :     if (objc < 2)
    2438              :     {
    2439            1 :         Tcl_WrongNumArgs(interp, 1, objv,
    2440              :                          "?-count n? ?-array name? query ?loop body?");
    2441            1 :         return TCL_ERROR;
    2442              :     }
    2443              : 
    2444           64 :     i = 1;
    2445          136 :     while (i < objc)
    2446              :     {
    2447           72 :         if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
    2448              :                                 TCL_EXACT, &optIndex) != TCL_OK)
    2449           61 :             break;
    2450              : 
    2451           11 :         if (++i >= objc)
    2452              :         {
    2453            2 :             Tcl_SetObjResult(interp,
    2454              :                              Tcl_NewStringObj("missing argument to -count or -array", -1));
    2455            2 :             return TCL_ERROR;
    2456              :         }
    2457              : 
    2458            9 :         switch ((enum options) optIndex)
    2459              :         {
    2460            8 :             case OPT_ARRAY:
    2461            8 :                 arrayname = Tcl_GetString(objv[i++]);
    2462            8 :                 break;
    2463              : 
    2464            1 :             case OPT_COUNT:
    2465            1 :                 if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
    2466            1 :                     return TCL_ERROR;
    2467            0 :                 break;
    2468              :         }
    2469              :     }
    2470              : 
    2471           61 :     query_idx = i;
    2472           61 :     if (query_idx >= objc || query_idx + 2 < objc)
    2473              :     {
    2474            1 :         Tcl_WrongNumArgs(interp, query_idx - 1, objv, "query ?loop body?");
    2475            1 :         return TCL_ERROR;
    2476              :     }
    2477              : 
    2478           60 :     if (query_idx + 1 < objc)
    2479            8 :         loop_body = objv[query_idx + 1];
    2480              : 
    2481              :     /************************************************************
    2482              :      * Execute the query inside a sub-transaction, so we can cope with
    2483              :      * errors sanely
    2484              :      ************************************************************/
    2485              : 
    2486           60 :     pltcl_subtrans_begin(oldcontext, oldowner);
    2487              : 
    2488           60 :     PG_TRY();
    2489              :     {
    2490           60 :         UTF_BEGIN;
    2491           60 :         spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
    2492           60 :                              pltcl_current_call_state->prodesc->fn_readonly, count);
    2493           52 :         UTF_END;
    2494              : 
    2495           52 :         my_rc = pltcl_process_SPI_result(interp,
    2496              :                                          arrayname,
    2497              :                                          loop_body,
    2498              :                                          spi_rc,
    2499              :                                          SPI_tuptable,
    2500              :                                          SPI_processed);
    2501              : 
    2502           52 :         pltcl_subtrans_commit(oldcontext, oldowner);
    2503              :     }
    2504            8 :     PG_CATCH();
    2505              :     {
    2506            8 :         pltcl_subtrans_abort(interp, oldcontext, oldowner);
    2507            8 :         return TCL_ERROR;
    2508              :     }
    2509           52 :     PG_END_TRY();
    2510              : 
    2511           52 :     return my_rc;
    2512              : }
    2513              : 
    2514              : /*
    2515              :  * Process the result from SPI_execute or SPI_execute_plan
    2516              :  *
    2517              :  * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
    2518              :  */
    2519              : static int
    2520          101 : pltcl_process_SPI_result(Tcl_Interp *interp,
    2521              :                          const char *arrayname,
    2522              :                          Tcl_Obj *loop_body,
    2523              :                          int spi_rc,
    2524              :                          SPITupleTable *tuptable,
    2525              :                          uint64 ntuples)
    2526              : {
    2527          101 :     int         my_rc = TCL_OK;
    2528              :     int         loop_rc;
    2529              :     HeapTuple  *tuples;
    2530              :     TupleDesc   tupdesc;
    2531              : 
    2532          101 :     switch (spi_rc)
    2533              :     {
    2534           37 :         case SPI_OK_SELINTO:
    2535              :         case SPI_OK_INSERT:
    2536              :         case SPI_OK_DELETE:
    2537              :         case SPI_OK_UPDATE:
    2538              :         case SPI_OK_MERGE:
    2539           37 :             Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
    2540           37 :             break;
    2541              : 
    2542            1 :         case SPI_OK_UTILITY:
    2543              :         case SPI_OK_REWRITTEN:
    2544            1 :             if (tuptable == NULL)
    2545              :             {
    2546            1 :                 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
    2547            1 :                 break;
    2548              :             }
    2549              :             /* fall through for utility returning tuples */
    2550              :             pg_fallthrough;
    2551              : 
    2552              :         case SPI_OK_SELECT:
    2553              :         case SPI_OK_INSERT_RETURNING:
    2554              :         case SPI_OK_DELETE_RETURNING:
    2555              :         case SPI_OK_UPDATE_RETURNING:
    2556              :         case SPI_OK_MERGE_RETURNING:
    2557              : 
    2558              :             /*
    2559              :              * Process the tuples we got
    2560              :              */
    2561           62 :             tuples = tuptable->vals;
    2562           62 :             tupdesc = tuptable->tupdesc;
    2563              : 
    2564           62 :             if (loop_body == NULL)
    2565              :             {
    2566              :                 /*
    2567              :                  * If there is no loop body given, just set the variables from
    2568              :                  * the first tuple (if any)
    2569              :                  */
    2570           50 :                 if (ntuples > 0)
    2571           29 :                     pltcl_set_tuple_values(interp, arrayname, 0,
    2572              :                                            tuples[0], tupdesc);
    2573              :             }
    2574              :             else
    2575              :             {
    2576              :                 /*
    2577              :                  * There is a loop body - process all tuples and evaluate the
    2578              :                  * body on each
    2579              :                  */
    2580              :                 uint64      i;
    2581              : 
    2582           26 :                 for (i = 0; i < ntuples; i++)
    2583              :                 {
    2584           22 :                     pltcl_set_tuple_values(interp, arrayname, i,
    2585           22 :                                            tuples[i], tupdesc);
    2586              : 
    2587           22 :                     loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
    2588              : 
    2589           22 :                     if (loop_rc == TCL_OK)
    2590           12 :                         continue;
    2591           10 :                     if (loop_rc == TCL_CONTINUE)
    2592            2 :                         continue;
    2593            8 :                     if (loop_rc == TCL_RETURN)
    2594              :                     {
    2595            2 :                         my_rc = TCL_RETURN;
    2596            2 :                         break;
    2597              :                     }
    2598            6 :                     if (loop_rc == TCL_BREAK)
    2599            2 :                         break;
    2600            4 :                     my_rc = TCL_ERROR;
    2601            4 :                     break;
    2602              :                 }
    2603              :             }
    2604              : 
    2605           62 :             if (my_rc == TCL_OK)
    2606              :             {
    2607           56 :                 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
    2608              :             }
    2609           62 :             break;
    2610              : 
    2611            1 :         default:
    2612            1 :             Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
    2613              :                              SPI_result_code_string(spi_rc), NULL);
    2614            1 :             my_rc = TCL_ERROR;
    2615            1 :             break;
    2616              :     }
    2617              : 
    2618          101 :     SPI_freetuptable(tuptable);
    2619              : 
    2620          101 :     return my_rc;
    2621              : }
    2622              : 
    2623              : 
    2624              : /**********************************************************************
    2625              :  * pltcl_SPI_prepare()      - Builtin support for prepared plans
    2626              :  *                The Tcl command SPI_prepare
    2627              :  *                always saves the plan using
    2628              :  *                SPI_keepplan and returns a key for
    2629              :  *                access. There is no chance to prepare
    2630              :  *                and not save the plan currently.
    2631              :  **********************************************************************/
    2632              : static int
    2633           17 : pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
    2634              :                   int objc, Tcl_Obj *const objv[])
    2635              : {
    2636           17 :     volatile MemoryContext plan_cxt = NULL;
    2637              :     Tcl_Size    nargs;
    2638              :     Tcl_Obj   **argsObj;
    2639              :     pltcl_query_desc *qdesc;
    2640              :     int         i;
    2641              :     Tcl_HashEntry *hashent;
    2642              :     int         hashnew;
    2643              :     Tcl_HashTable *query_hash;
    2644           17 :     MemoryContext oldcontext = CurrentMemoryContext;
    2645           17 :     ResourceOwner oldowner = CurrentResourceOwner;
    2646              : 
    2647              :     /************************************************************
    2648              :      * Check the call syntax
    2649              :      ************************************************************/
    2650           17 :     if (objc != 3)
    2651              :     {
    2652            1 :         Tcl_WrongNumArgs(interp, 1, objv, "query argtypes");
    2653            1 :         return TCL_ERROR;
    2654              :     }
    2655              : 
    2656              :     /************************************************************
    2657              :      * Split the argument type list
    2658              :      ************************************************************/
    2659           16 :     if (Tcl_ListObjGetElements(interp, objv[2], &nargs, &argsObj) != TCL_OK)
    2660            1 :         return TCL_ERROR;
    2661              : 
    2662              :     /************************************************************
    2663              :      * Allocate the new querydesc structure
    2664              :      *
    2665              :      * struct qdesc and subsidiary data all live in plan_cxt.  Note that if the
    2666              :      * function is recompiled for whatever reason, permanent memory leaks
    2667              :      * occur.  FIXME someday.
    2668              :      ************************************************************/
    2669           15 :     plan_cxt = AllocSetContextCreate(TopMemoryContext,
    2670              :                                      "PL/Tcl spi_prepare query",
    2671              :                                      ALLOCSET_SMALL_SIZES);
    2672           15 :     MemoryContextSwitchTo(plan_cxt);
    2673           15 :     qdesc = palloc0_object(pltcl_query_desc);
    2674           15 :     snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
    2675           15 :     qdesc->nargs = nargs;
    2676           15 :     qdesc->argtypes = (Oid *) palloc(nargs * sizeof(Oid));
    2677           15 :     qdesc->arginfuncs = (FmgrInfo *) palloc(nargs * sizeof(FmgrInfo));
    2678           15 :     qdesc->argtypioparams = (Oid *) palloc(nargs * sizeof(Oid));
    2679           15 :     MemoryContextSwitchTo(oldcontext);
    2680              : 
    2681              :     /************************************************************
    2682              :      * Execute the prepare inside a sub-transaction, so we can cope with
    2683              :      * errors sanely
    2684              :      ************************************************************/
    2685              : 
    2686           15 :     pltcl_subtrans_begin(oldcontext, oldowner);
    2687              : 
    2688           15 :     PG_TRY();
    2689              :     {
    2690              :         /************************************************************
    2691              :          * Resolve argument type names and then look them up by oid
    2692              :          * in the system cache, and remember the required information
    2693              :          * for input conversion.
    2694              :          ************************************************************/
    2695           34 :         for (i = 0; i < nargs; i++)
    2696              :         {
    2697              :             Oid         typId,
    2698              :                         typInput,
    2699              :                         typIOParam;
    2700              :             int32       typmod;
    2701              : 
    2702           20 :             (void) parseTypeString(Tcl_GetString(argsObj[i]),
    2703              :                                    &typId, &typmod, NULL);
    2704              : 
    2705           19 :             getTypeInputInfo(typId, &typInput, &typIOParam);
    2706              : 
    2707           19 :             qdesc->argtypes[i] = typId;
    2708           19 :             fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
    2709           19 :             qdesc->argtypioparams[i] = typIOParam;
    2710              :         }
    2711              : 
    2712              :         /************************************************************
    2713              :          * Prepare the plan and check for errors
    2714              :          ************************************************************/
    2715           14 :         UTF_BEGIN;
    2716           14 :         qdesc->plan = SPI_prepare(UTF_U2E(Tcl_GetString(objv[1])),
    2717              :                                   nargs, qdesc->argtypes);
    2718           13 :         UTF_END;
    2719              : 
    2720           13 :         if (qdesc->plan == NULL)
    2721            0 :             elog(ERROR, "SPI_prepare() failed");
    2722              : 
    2723              :         /************************************************************
    2724              :          * Save the plan into permanent memory (right now it's in the
    2725              :          * SPI procCxt, which will go away at function end).
    2726              :          ************************************************************/
    2727           13 :         if (SPI_keepplan(qdesc->plan))
    2728            0 :             elog(ERROR, "SPI_keepplan() failed");
    2729              : 
    2730           13 :         pltcl_subtrans_commit(oldcontext, oldowner);
    2731              :     }
    2732            2 :     PG_CATCH();
    2733              :     {
    2734            2 :         pltcl_subtrans_abort(interp, oldcontext, oldowner);
    2735              : 
    2736            2 :         MemoryContextDelete(plan_cxt);
    2737              : 
    2738            2 :         return TCL_ERROR;
    2739              :     }
    2740           13 :     PG_END_TRY();
    2741              : 
    2742              :     /************************************************************
    2743              :      * Insert a hashtable entry for the plan and return
    2744              :      * the key to the caller
    2745              :      ************************************************************/
    2746           13 :     query_hash = &pltcl_current_call_state->prodesc->interp_desc->query_hash;
    2747              : 
    2748           13 :     hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
    2749           13 :     Tcl_SetHashValue(hashent, (ClientData) qdesc);
    2750              : 
    2751              :     /* qname is ASCII, so no need for encoding conversion */
    2752           13 :     Tcl_SetObjResult(interp, Tcl_NewStringObj(qdesc->qname, -1));
    2753           13 :     return TCL_OK;
    2754              : }
    2755              : 
    2756              : 
    2757              : /**********************************************************************
    2758              :  * pltcl_SPI_execute_plan()     - Execute a prepared plan
    2759              :  **********************************************************************/
    2760              : static int
    2761           55 : pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
    2762              :                        int objc, Tcl_Obj *const objv[])
    2763              : {
    2764              :     int         my_rc;
    2765              :     int         spi_rc;
    2766              :     int         i;
    2767              :     int         j;
    2768              :     int         optIndex;
    2769              :     Tcl_HashEntry *hashent;
    2770              :     pltcl_query_desc *qdesc;
    2771           55 :     const char *nulls = NULL;
    2772           55 :     const char *arrayname = NULL;
    2773           55 :     Tcl_Obj    *loop_body = NULL;
    2774           55 :     int         count = 0;
    2775              :     Tcl_Size    callObjc;
    2776           55 :     Tcl_Obj   **callObjv = NULL;
    2777              :     Datum      *argvalues;
    2778           55 :     MemoryContext oldcontext = CurrentMemoryContext;
    2779           55 :     ResourceOwner oldowner = CurrentResourceOwner;
    2780              :     Tcl_HashTable *query_hash;
    2781              : 
    2782              :     enum options
    2783              :     {
    2784              :         OPT_ARRAY, OPT_COUNT, OPT_NULLS
    2785              :     };
    2786              : 
    2787              :     static const char *options[] = {
    2788              :         "-array", "-count", "-nulls", (const char *) NULL
    2789              :     };
    2790              : 
    2791              :     /************************************************************
    2792              :      * Get the options and check syntax
    2793              :      ************************************************************/
    2794           55 :     i = 1;
    2795          154 :     while (i < objc)
    2796              :     {
    2797           98 :         if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
    2798              :                                 TCL_EXACT, &optIndex) != TCL_OK)
    2799           50 :             break;
    2800              : 
    2801           48 :         if (++i >= objc)
    2802              :         {
    2803            3 :             Tcl_SetObjResult(interp,
    2804              :                              Tcl_NewStringObj("missing argument to -array, -count or -nulls", -1));
    2805            3 :             return TCL_ERROR;
    2806              :         }
    2807              : 
    2808           45 :         switch ((enum options) optIndex)
    2809              :         {
    2810            4 :             case OPT_ARRAY:
    2811            4 :                 arrayname = Tcl_GetString(objv[i++]);
    2812            4 :                 break;
    2813              : 
    2814           41 :             case OPT_COUNT:
    2815           41 :                 if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
    2816            1 :                     return TCL_ERROR;
    2817           40 :                 break;
    2818              : 
    2819            0 :             case OPT_NULLS:
    2820            0 :                 nulls = Tcl_GetString(objv[i++]);
    2821            0 :                 break;
    2822              :         }
    2823              :     }
    2824              : 
    2825              :     /************************************************************
    2826              :      * Get the prepared plan descriptor by its key
    2827              :      ************************************************************/
    2828           51 :     if (i >= objc)
    2829              :     {
    2830            1 :         Tcl_SetObjResult(interp,
    2831              :                          Tcl_NewStringObj("missing argument to -count or -array", -1));
    2832            1 :         return TCL_ERROR;
    2833              :     }
    2834              : 
    2835           50 :     query_hash = &pltcl_current_call_state->prodesc->interp_desc->query_hash;
    2836              : 
    2837           50 :     hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
    2838           50 :     if (hashent == NULL)
    2839              :     {
    2840            1 :         Tcl_AppendResult(interp, "invalid queryid '", Tcl_GetString(objv[i]), "'", NULL);
    2841            1 :         return TCL_ERROR;
    2842              :     }
    2843           49 :     qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
    2844           49 :     i++;
    2845              : 
    2846              :     /************************************************************
    2847              :      * If a nulls string is given, check for correct length
    2848              :      ************************************************************/
    2849           49 :     if (nulls != NULL)
    2850              :     {
    2851            0 :         if (strlen(nulls) != qdesc->nargs)
    2852              :         {
    2853            0 :             Tcl_SetObjResult(interp,
    2854              :                              Tcl_NewStringObj("length of nulls string doesn't match number of arguments",
    2855              :                                               -1));
    2856            0 :             return TCL_ERROR;
    2857              :         }
    2858              :     }
    2859              : 
    2860              :     /************************************************************
    2861              :      * If there was an argtype list on preparation, we need
    2862              :      * an argument value list now
    2863              :      ************************************************************/
    2864           49 :     if (qdesc->nargs > 0)
    2865              :     {
    2866           45 :         if (i >= objc)
    2867              :         {
    2868            0 :             Tcl_SetObjResult(interp,
    2869              :                              Tcl_NewStringObj("argument list length doesn't match number of arguments for query",
    2870              :                                               -1));
    2871            0 :             return TCL_ERROR;
    2872              :         }
    2873              : 
    2874              :         /************************************************************
    2875              :          * Split the argument values
    2876              :          ************************************************************/
    2877           45 :         if (Tcl_ListObjGetElements(interp, objv[i++], &callObjc, &callObjv) != TCL_OK)
    2878            0 :             return TCL_ERROR;
    2879              : 
    2880              :         /************************************************************
    2881              :          * Check that the number of arguments matches
    2882              :          ************************************************************/
    2883           45 :         if (callObjc != qdesc->nargs)
    2884              :         {
    2885            0 :             Tcl_SetObjResult(interp,
    2886              :                              Tcl_NewStringObj("argument list length doesn't match number of arguments for query",
    2887              :                                               -1));
    2888            0 :             return TCL_ERROR;
    2889              :         }
    2890              :     }
    2891              :     else
    2892            4 :         callObjc = 0;
    2893              : 
    2894              :     /************************************************************
    2895              :      * Get loop body if present
    2896              :      ************************************************************/
    2897           49 :     if (i < objc)
    2898            4 :         loop_body = objv[i++];
    2899              : 
    2900           49 :     if (i != objc)
    2901              :     {
    2902            0 :         Tcl_WrongNumArgs(interp, 1, objv,
    2903              :                          "?-count n? ?-array name? ?-nulls string? "
    2904              :                          "query ?args? ?loop body?");
    2905            0 :         return TCL_ERROR;
    2906              :     }
    2907              : 
    2908              :     /************************************************************
    2909              :      * Execute the plan inside a sub-transaction, so we can cope with
    2910              :      * errors sanely
    2911              :      ************************************************************/
    2912              : 
    2913           49 :     pltcl_subtrans_begin(oldcontext, oldowner);
    2914              : 
    2915           49 :     PG_TRY();
    2916              :     {
    2917              :         /************************************************************
    2918              :          * Setup the value array for SPI_execute_plan() using
    2919              :          * the type specific input functions
    2920              :          ************************************************************/
    2921           49 :         argvalues = (Datum *) palloc(callObjc * sizeof(Datum));
    2922              : 
    2923          142 :         for (j = 0; j < callObjc; j++)
    2924              :         {
    2925           93 :             if (nulls && nulls[j] == 'n')
    2926              :             {
    2927            0 :                 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
    2928              :                                                  NULL,
    2929            0 :                                                  qdesc->argtypioparams[j],
    2930              :                                                  -1);
    2931              :             }
    2932              :             else
    2933              :             {
    2934           93 :                 UTF_BEGIN;
    2935          279 :                 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
    2936           93 :                                                  UTF_U2E(Tcl_GetString(callObjv[j])),
    2937           93 :                                                  qdesc->argtypioparams[j],
    2938              :                                                  -1);
    2939           93 :                 UTF_END;
    2940              :             }
    2941              :         }
    2942              : 
    2943              :         /************************************************************
    2944              :          * Execute the plan
    2945              :          ************************************************************/
    2946           98 :         spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
    2947           49 :                                   pltcl_current_call_state->prodesc->fn_readonly,
    2948              :                                   count);
    2949              : 
    2950           49 :         my_rc = pltcl_process_SPI_result(interp,
    2951              :                                          arrayname,
    2952              :                                          loop_body,
    2953              :                                          spi_rc,
    2954              :                                          SPI_tuptable,
    2955              :                                          SPI_processed);
    2956              : 
    2957           49 :         pltcl_subtrans_commit(oldcontext, oldowner);
    2958              :     }
    2959            0 :     PG_CATCH();
    2960              :     {
    2961            0 :         pltcl_subtrans_abort(interp, oldcontext, oldowner);
    2962            0 :         return TCL_ERROR;
    2963              :     }
    2964           49 :     PG_END_TRY();
    2965              : 
    2966           49 :     return my_rc;
    2967              : }
    2968              : 
    2969              : 
    2970              : /**********************************************************************
    2971              :  * pltcl_subtransaction()   - Execute some Tcl code in a subtransaction
    2972              :  *
    2973              :  * The subtransaction is aborted if the Tcl code fragment returns TCL_ERROR,
    2974              :  * otherwise it's subcommitted.
    2975              :  **********************************************************************/
    2976              : static int
    2977            8 : pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp,
    2978              :                      int objc, Tcl_Obj *const objv[])
    2979              : {
    2980            8 :     MemoryContext oldcontext = CurrentMemoryContext;
    2981            8 :     ResourceOwner oldowner = CurrentResourceOwner;
    2982              :     int         retcode;
    2983              : 
    2984            8 :     if (objc != 2)
    2985              :     {
    2986            0 :         Tcl_WrongNumArgs(interp, 1, objv, "command");
    2987            0 :         return TCL_ERROR;
    2988              :     }
    2989              : 
    2990              :     /*
    2991              :      * Note: we don't use pltcl_subtrans_begin and friends here because we
    2992              :      * don't want the error handling in pltcl_subtrans_abort.  But otherwise
    2993              :      * the processing should be about the same as in those functions.
    2994              :      */
    2995            8 :     BeginInternalSubTransaction(NULL);
    2996            8 :     MemoryContextSwitchTo(oldcontext);
    2997              : 
    2998            8 :     retcode = Tcl_EvalObjEx(interp, objv[1], 0);
    2999              : 
    3000            8 :     if (retcode == TCL_ERROR)
    3001              :     {
    3002              :         /* Rollback the subtransaction */
    3003            5 :         RollbackAndReleaseCurrentSubTransaction();
    3004              :     }
    3005              :     else
    3006              :     {
    3007              :         /* Commit the subtransaction */
    3008            3 :         ReleaseCurrentSubTransaction();
    3009              :     }
    3010              : 
    3011              :     /* In either case, restore previous memory context and resource owner */
    3012            8 :     MemoryContextSwitchTo(oldcontext);
    3013            8 :     CurrentResourceOwner = oldowner;
    3014              : 
    3015            8 :     return retcode;
    3016              : }
    3017              : 
    3018              : 
    3019              : /**********************************************************************
    3020              :  * pltcl_commit()
    3021              :  *
    3022              :  * Commit the transaction and start a new one.
    3023              :  **********************************************************************/
    3024              : static int
    3025           10 : pltcl_commit(ClientData cdata, Tcl_Interp *interp,
    3026              :              int objc, Tcl_Obj *const objv[])
    3027              : {
    3028           10 :     MemoryContext oldcontext = CurrentMemoryContext;
    3029              : 
    3030           10 :     PG_TRY();
    3031              :     {
    3032           10 :         SPI_commit();
    3033              :     }
    3034            5 :     PG_CATCH();
    3035              :     {
    3036              :         ErrorData  *edata;
    3037              : 
    3038              :         /* Save error info */
    3039            5 :         MemoryContextSwitchTo(oldcontext);
    3040            5 :         edata = CopyErrorData();
    3041            5 :         FlushErrorState();
    3042              : 
    3043              :         /* Pass the error data to Tcl */
    3044            5 :         pltcl_construct_errorCode(interp, edata);
    3045            5 :         UTF_BEGIN;
    3046            5 :         Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
    3047            5 :         UTF_END;
    3048            5 :         FreeErrorData(edata);
    3049              : 
    3050            5 :         return TCL_ERROR;
    3051              :     }
    3052            5 :     PG_END_TRY();
    3053              : 
    3054            5 :     return TCL_OK;
    3055              : }
    3056              : 
    3057              : 
    3058              : /**********************************************************************
    3059              :  * pltcl_rollback()
    3060              :  *
    3061              :  * Abort the transaction and start a new one.
    3062              :  **********************************************************************/
    3063              : static int
    3064            6 : pltcl_rollback(ClientData cdata, Tcl_Interp *interp,
    3065              :                int objc, Tcl_Obj *const objv[])
    3066              : {
    3067            6 :     MemoryContext oldcontext = CurrentMemoryContext;
    3068              : 
    3069            6 :     PG_TRY();
    3070              :     {
    3071            6 :         SPI_rollback();
    3072              :     }
    3073            1 :     PG_CATCH();
    3074              :     {
    3075              :         ErrorData  *edata;
    3076              : 
    3077              :         /* Save error info */
    3078            1 :         MemoryContextSwitchTo(oldcontext);
    3079            1 :         edata = CopyErrorData();
    3080            1 :         FlushErrorState();
    3081              : 
    3082              :         /* Pass the error data to Tcl */
    3083            1 :         pltcl_construct_errorCode(interp, edata);
    3084            1 :         UTF_BEGIN;
    3085            1 :         Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
    3086            1 :         UTF_END;
    3087            1 :         FreeErrorData(edata);
    3088              : 
    3089            1 :         return TCL_ERROR;
    3090              :     }
    3091            5 :     PG_END_TRY();
    3092              : 
    3093            5 :     return TCL_OK;
    3094              : }
    3095              : 
    3096              : 
    3097              : /**********************************************************************
    3098              :  * pltcl_set_tuple_values() - Set variables for all attributes
    3099              :  *                of a given tuple
    3100              :  *
    3101              :  * Note: arrayname is presumed to be UTF8; it usually came from Tcl
    3102              :  **********************************************************************/
    3103              : static void
    3104           51 : pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
    3105              :                        uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
    3106              : {
    3107              :     int         i;
    3108              :     char       *outputstr;
    3109              :     Datum       attr;
    3110              :     bool        isnull;
    3111              :     const char *attname;
    3112              :     Oid         typoutput;
    3113              :     bool        typisvarlena;
    3114              :     const char **arrptr;
    3115              :     const char **nameptr;
    3116           51 :     const char *nullname = NULL;
    3117              : 
    3118              :     /************************************************************
    3119              :      * Prepare pointers for Tcl_SetVar2Ex() below
    3120              :      ************************************************************/
    3121           51 :     if (arrayname == NULL)
    3122              :     {
    3123           29 :         arrptr = &attname;
    3124           29 :         nameptr = &nullname;
    3125              :     }
    3126              :     else
    3127              :     {
    3128           22 :         arrptr = &arrayname;
    3129           22 :         nameptr = &attname;
    3130              : 
    3131              :         /*
    3132              :          * When outputting to an array, fill the ".tupno" element with the
    3133              :          * current tuple number.  This will be overridden below if ".tupno" is
    3134              :          * in use as an actual field name in the rowtype.
    3135              :          */
    3136           22 :         Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
    3137              :     }
    3138              : 
    3139          122 :     for (i = 0; i < tupdesc->natts; i++)
    3140              :     {
    3141           71 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    3142              : 
    3143              :         /* ignore dropped attributes */
    3144           71 :         if (att->attisdropped)
    3145            0 :             continue;
    3146              : 
    3147              :         /************************************************************
    3148              :          * Get the attribute name
    3149              :          ************************************************************/
    3150           71 :         UTF_BEGIN;
    3151           71 :         attname = pstrdup(UTF_E2U(NameStr(att->attname)));
    3152           71 :         UTF_END;
    3153              : 
    3154              :         /************************************************************
    3155              :          * Get the attributes value
    3156              :          ************************************************************/
    3157           71 :         attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
    3158              : 
    3159              :         /************************************************************
    3160              :          * If there is a value, set the variable
    3161              :          * If not, unset it
    3162              :          *
    3163              :          * Hmmm - Null attributes will cause functions to
    3164              :          *        crash if they don't expect them - need something
    3165              :          *        smarter here.
    3166              :          ************************************************************/
    3167           71 :         if (!isnull)
    3168              :         {
    3169           71 :             getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
    3170           71 :             outputstr = OidOutputFunctionCall(typoutput, attr);
    3171           71 :             UTF_BEGIN;
    3172           71 :             Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
    3173           71 :                           Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
    3174           71 :             UTF_END;
    3175           71 :             pfree(outputstr);
    3176              :         }
    3177              :         else
    3178            0 :             Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
    3179              : 
    3180           71 :         pfree(unconstify(char *, attname));
    3181              :     }
    3182           51 : }
    3183              : 
    3184              : 
    3185              : /**********************************************************************
    3186              :  * pltcl_build_tuple_argument() - Build a list object usable for 'array set'
    3187              :  *                from all attributes of a given tuple
    3188              :  **********************************************************************/
    3189              : static Tcl_Obj *
    3190           69 : pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
    3191              : {
    3192           69 :     Tcl_Obj    *retobj = Tcl_NewObj();
    3193              :     int         i;
    3194              :     char       *outputstr;
    3195              :     Datum       attr;
    3196              :     bool        isnull;
    3197              :     char       *attname;
    3198              :     Oid         typoutput;
    3199              :     bool        typisvarlena;
    3200              : 
    3201          293 :     for (i = 0; i < tupdesc->natts; i++)
    3202              :     {
    3203          224 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
    3204              : 
    3205              :         /* ignore dropped attributes */
    3206          224 :         if (att->attisdropped)
    3207            8 :             continue;
    3208              : 
    3209          216 :         if (att->attgenerated)
    3210              :         {
    3211              :             /* don't include unless requested */
    3212           18 :             if (!include_generated)
    3213            6 :                 continue;
    3214              :             /* never include virtual columns */
    3215           12 :             if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    3216            6 :                 continue;
    3217              :         }
    3218              : 
    3219              :         /************************************************************
    3220              :          * Get the attribute name
    3221              :          ************************************************************/
    3222          204 :         attname = NameStr(att->attname);
    3223              : 
    3224              :         /************************************************************
    3225              :          * Get the attributes value
    3226              :          ************************************************************/
    3227          204 :         attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
    3228              : 
    3229              :         /************************************************************
    3230              :          * If there is a value, append the attribute name and the
    3231              :          * value to the list
    3232              :          *
    3233              :          * Hmmm - Null attributes will cause functions to
    3234              :          *        crash if they don't expect them - need something
    3235              :          *        smarter here.
    3236              :          ************************************************************/
    3237          204 :         if (!isnull)
    3238              :         {
    3239          200 :             getTypeOutputInfo(att->atttypid,
    3240              :                               &typoutput, &typisvarlena);
    3241          200 :             outputstr = OidOutputFunctionCall(typoutput, attr);
    3242          200 :             UTF_BEGIN;
    3243          200 :             Tcl_ListObjAppendElement(NULL, retobj,
    3244          200 :                                      Tcl_NewStringObj(UTF_E2U(attname), -1));
    3245          200 :             UTF_END;
    3246          200 :             UTF_BEGIN;
    3247          200 :             Tcl_ListObjAppendElement(NULL, retobj,
    3248          200 :                                      Tcl_NewStringObj(UTF_E2U(outputstr), -1));
    3249          200 :             UTF_END;
    3250          200 :             pfree(outputstr);
    3251              :         }
    3252              :     }
    3253              : 
    3254           69 :     return retobj;
    3255              : }
    3256              : 
    3257              : /**********************************************************************
    3258              :  * pltcl_build_tuple_result() - Build a tuple of function's result rowtype
    3259              :  *                from a Tcl list of column names and values
    3260              :  *
    3261              :  * In a trigger function, we build a tuple of the trigger table's rowtype.
    3262              :  *
    3263              :  * Note: this function leaks memory.  Even if we made it clean up its own
    3264              :  * mess, there's no way to prevent the datatype input functions it calls
    3265              :  * from leaking.  Run it in a short-lived context, unless we're about to
    3266              :  * exit the procedure anyway.
    3267              :  **********************************************************************/
    3268              : static HeapTuple
    3269           31 : pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc,
    3270              :                          pltcl_call_state *call_state)
    3271              : {
    3272              :     HeapTuple   tuple;
    3273              :     TupleDesc   tupdesc;
    3274              :     AttInMetadata *attinmeta;
    3275              :     char      **values;
    3276              :     int         i;
    3277              : 
    3278           31 :     if (call_state->ret_tupdesc)
    3279              :     {
    3280           21 :         tupdesc = call_state->ret_tupdesc;
    3281           21 :         attinmeta = call_state->attinmeta;
    3282              :     }
    3283           10 :     else if (call_state->trigdata)
    3284              :     {
    3285           10 :         tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
    3286           10 :         attinmeta = TupleDescGetAttInMetadata(tupdesc);
    3287              :     }
    3288              :     else
    3289              :     {
    3290            0 :         elog(ERROR, "PL/Tcl function does not return a tuple");
    3291              :         tupdesc = NULL;         /* keep compiler quiet */
    3292              :         attinmeta = NULL;
    3293              :     }
    3294              : 
    3295           31 :     values = (char **) palloc0(tupdesc->natts * sizeof(char *));
    3296              : 
    3297           31 :     if (kvObjc % 2 != 0)
    3298            2 :         ereport(ERROR,
    3299              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3300              :                  errmsg("column name/value list must have even number of elements")));
    3301              : 
    3302           98 :     for (i = 0; i < kvObjc; i += 2)
    3303              :     {
    3304           73 :         char       *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
    3305           73 :         int         attn = SPI_fnumber(tupdesc, fieldName);
    3306              : 
    3307              :         /*
    3308              :          * We silently ignore ".tupno", if it's present but doesn't match any
    3309              :          * actual output column.  This allows direct use of a row returned by
    3310              :          * pltcl_set_tuple_values().
    3311              :          */
    3312           73 :         if (attn == SPI_ERROR_NOATTRIBUTE)
    3313              :         {
    3314            3 :             if (strcmp(fieldName, ".tupno") == 0)
    3315            0 :                 continue;
    3316            3 :             ereport(ERROR,
    3317              :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    3318              :                      errmsg("column name/value list contains nonexistent column name \"%s\"",
    3319              :                             fieldName)));
    3320              :         }
    3321              : 
    3322           70 :         if (attn <= 0)
    3323            0 :             ereport(ERROR,
    3324              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3325              :                      errmsg("cannot set system attribute \"%s\"",
    3326              :                             fieldName)));
    3327              : 
    3328           70 :         if (TupleDescAttr(tupdesc, attn - 1)->attgenerated)
    3329            1 :             ereport(ERROR,
    3330              :                     (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    3331              :                      errmsg("cannot set generated column \"%s\"",
    3332              :                             fieldName)));
    3333              : 
    3334           69 :         values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
    3335              :     }
    3336              : 
    3337           25 :     tuple = BuildTupleFromCStrings(attinmeta, values);
    3338              : 
    3339              :     /* if result type is domain-over-composite, check domain constraints */
    3340           25 :     if (call_state->prodesc->fn_retisdomain)
    3341            3 :         domain_check(HeapTupleGetDatum(tuple), false,
    3342            3 :                      call_state->prodesc->result_typid,
    3343            3 :                      &call_state->prodesc->domain_info,
    3344            3 :                      call_state->prodesc->fn_cxt);
    3345              : 
    3346           24 :     return tuple;
    3347              : }
    3348              : 
    3349              : /**********************************************************************
    3350              :  * pltcl_init_tuple_store() - Initialize the result tuplestore for a SRF
    3351              :  **********************************************************************/
    3352              : static void
    3353            5 : pltcl_init_tuple_store(pltcl_call_state *call_state)
    3354              : {
    3355            5 :     ReturnSetInfo *rsi = call_state->rsi;
    3356              :     MemoryContext oldcxt;
    3357              :     ResourceOwner oldowner;
    3358              : 
    3359              :     /* Should be in a SRF */
    3360              :     Assert(rsi);
    3361              :     /* Should be first time through */
    3362              :     Assert(!call_state->tuple_store);
    3363              :     Assert(!call_state->attinmeta);
    3364              : 
    3365              :     /* We expect caller to provide an appropriate result tupdesc */
    3366              :     Assert(rsi->expectedDesc);
    3367            5 :     call_state->ret_tupdesc = rsi->expectedDesc;
    3368              : 
    3369              :     /*
    3370              :      * Switch to the right memory context and resource owner for storing the
    3371              :      * tuplestore. If we're within a subtransaction opened for an exception
    3372              :      * block, for example, we must still create the tuplestore in the resource
    3373              :      * owner that was active when this function was entered, and not in the
    3374              :      * subtransaction's resource owner.
    3375              :      */
    3376            5 :     oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
    3377            5 :     oldowner = CurrentResourceOwner;
    3378            5 :     CurrentResourceOwner = call_state->tuple_store_owner;
    3379              : 
    3380            5 :     call_state->tuple_store =
    3381            5 :         tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
    3382              :                               false, work_mem);
    3383              : 
    3384              :     /* Build attinmeta in this context, too */
    3385            5 :     call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
    3386              : 
    3387            5 :     CurrentResourceOwner = oldowner;
    3388            5 :     MemoryContextSwitchTo(oldcxt);
    3389            5 : }
        

Generated by: LCOV version 2.0-1