LCOV - differential code coverage report
Current view: top level - src/pl/tcl - pltcl.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 90.6 % 1071 970 8 80 13 32 562 4 372 56 584 6
Current Date: 2023-04-08 15:15:32 Functions: 87.2 % 47 41 6 38 3 6 38
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a