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

Generated by: LCOV version 2.0-1