LCOV - code coverage report
Current view: top level - src/pl/tcl - pltcl.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 91.5 % 1091 998
Test Date: 2026-07-25 22:15:46 Functions: 87.2 % 47 41
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 68.0 % 607 413

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

Generated by: LCOV version 2.0-1