PostgreSQL Source Code  git master
pltcl.c File Reference
#include "postgres.h"
#include <tcl.h>
#include <unistd.h>
#include <fcntl.h>
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "fmgr.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "pgstat.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/regproc.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "pltclerrcodes.h"
Include dependency graph for pltcl.c:

Go to the source code of this file.

Data Structures

struct  pltcl_interp_desc
 
struct  pltcl_proc_desc
 
struct  pltcl_query_desc
 
struct  pltcl_proc_key
 
struct  pltcl_proc_ptr
 
struct  pltcl_call_state
 
struct  TclExceptionNameMap
 

Macros

#define HAVE_TCL_VERSION(maj, min)
 
#define CONST86
 
#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")
 
#define UTF_BEGIN
 
#define UTF_END
 
#define UTF_U2E(x)   (_pltcl_utf_dst = utf_u2e(_pltcl_utf_src = (x)))
 
#define UTF_E2U(x)   (_pltcl_utf_dst = utf_e2u(_pltcl_utf_src = (x)))
 

Typedefs

typedef struct pltcl_interp_desc pltcl_interp_desc
 
typedef struct pltcl_proc_desc pltcl_proc_desc
 
typedef struct pltcl_query_desc pltcl_query_desc
 
typedef struct pltcl_proc_key pltcl_proc_key
 
typedef struct pltcl_proc_ptr pltcl_proc_ptr
 
typedef struct pltcl_call_state pltcl_call_state
 

Functions

static char * utf_u2e (const char *src)
 
static char * utf_e2u (const char *src)
 
void _PG_init (void)
 
static void pltcl_init_interp (pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
 
static pltcl_interp_descpltcl_fetch_interp (Oid prolang, bool pltrusted)
 
static void call_pltcl_start_proc (Oid prolang, bool pltrusted)
 
static void start_proc_error_callback (void *arg)
 
static Datum pltcl_handler (PG_FUNCTION_ARGS, bool pltrusted)
 
static Datum pltcl_func_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static HeapTuple pltcl_trigger_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static void pltcl_event_trigger_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static void throw_tcl_error (Tcl_Interp *interp, const char *proname)
 
static pltcl_proc_desccompile_pltcl_function (Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
 
static int pltcl_elog (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static void pltcl_construct_errorCode (Tcl_Interp *interp, ErrorData *edata)
 
static const char * pltcl_get_condition_name (int sqlstate)
 
static int pltcl_quote (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_argisnull (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_returnnull (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_returnnext (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_SPI_execute (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_process_SPI_result (Tcl_Interp *interp, const char *arrayname, Tcl_Obj *loop_body, int spi_rc, SPITupleTable *tuptable, uint64 ntuples)
 
static int pltcl_SPI_prepare (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_SPI_execute_plan (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_subtransaction (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_commit (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_rollback (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static void pltcl_subtrans_begin (MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_subtrans_commit (MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_subtrans_abort (Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_set_tuple_values (Tcl_Interp *interp, const char *arrayname, uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
 
static Tcl_Obj * pltcl_build_tuple_argument (HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
 
static HeapTuple pltcl_build_tuple_result (Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
 
static void pltcl_init_tuple_store (pltcl_call_state *call_state)
 
static ClientData pltcl_InitNotifier (void)
 
static void pltcl_FinalizeNotifier (ClientData clientData)
 
static void pltcl_SetTimer (CONST86 Tcl_Time *timePtr)
 
static void pltcl_AlertNotifier (ClientData clientData)
 
static void pltcl_CreateFileHandler (int fd, int mask, Tcl_FileProc *proc, ClientData clientData)
 
static void pltcl_DeleteFileHandler (int fd)
 
static void pltcl_ServiceModeHook (int mode)
 
static int pltcl_WaitForEvent (CONST86 Tcl_Time *timePtr)
 
 PG_FUNCTION_INFO_V1 (pltcl_call_handler)
 
Datum pltcl_call_handler (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (pltclu_call_handler)
 
Datum pltclu_call_handler (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 
static char * pltcl_start_proc = NULL
 
static char * pltclu_start_proc = NULL
 
static bool pltcl_pm_init_done = false
 
static Tcl_Interp * pltcl_hold_interp = NULL
 
static HTABpltcl_interp_htab = NULL
 
static HTABpltcl_proc_htab = NULL
 
static pltcl_call_statepltcl_current_call_state = NULL
 
static const TclExceptionNameMap exception_name_map []
 

Macro Definition Documentation

◆ CONST86

#define CONST86

Definition at line 55 of file pltcl.c.

◆ HAVE_TCL_VERSION

#define HAVE_TCL_VERSION (   maj,
  min 
)
Value:
((TCL_MAJOR_VERSION > maj) || \
(TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))

Definition at line 44 of file pltcl.c.

◆ TEXTDOMAIN

#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")

Definition at line 60 of file pltcl.c.

Referenced by _PG_init().

◆ UTF_BEGIN

◆ UTF_E2U

#define UTF_E2U (   x)    (_pltcl_utf_dst = utf_e2u(_pltcl_utf_src = (x)))

◆ UTF_END

#define UTF_END

◆ UTF_U2E

#define UTF_U2E (   x)    (_pltcl_utf_dst = utf_u2e(_pltcl_utf_src = (x)))

Definition at line 95 of file pltcl.c.

Referenced by pltcl_elog(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

Typedef Documentation

◆ pltcl_call_state

◆ pltcl_interp_desc

◆ pltcl_proc_desc

◆ pltcl_proc_key

◆ pltcl_proc_ptr

◆ pltcl_query_desc

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 400 of file pltcl.c.

References DefineCustomStringVariable(), elog, HASHCTL::entrysize, ERROR, gettext_noop, HASH_BLOBS, hash_create(), HASH_ELEM, HASHCTL::keysize, pg_bindtextdomain(), PGC_SUSET, pltcl_AlertNotifier(), pltcl_CreateFileHandler(), pltcl_DeleteFileHandler(), pltcl_FinalizeNotifier(), pltcl_hold_interp, pltcl_InitNotifier(), pltcl_pm_init_done, pltcl_ServiceModeHook(), pltcl_SetTimer(), pltcl_start_proc, pltcl_WaitForEvent(), pltclu_start_proc, and TEXTDOMAIN.

401 {
402  Tcl_NotifierProcs notifier;
403  HASHCTL hash_ctl;
404 
405  /* Be sure we do initialization only once (should be redundant now) */
406  if (pltcl_pm_init_done)
407  return;
408 
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  */
419  notifier.setTimerProc = pltcl_SetTimer;
420  notifier.waitForEventProc = pltcl_WaitForEvent;
421  notifier.createFileHandlerProc = pltcl_CreateFileHandler;
422  notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
423  notifier.initNotifierProc = pltcl_InitNotifier;
424  notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
425  notifier.alertNotifierProc = pltcl_AlertNotifier;
426  notifier.serviceModeHookProc = pltcl_ServiceModeHook;
427  Tcl_SetNotifier(&notifier);
428 
429  /************************************************************
430  * Create the dummy hold interpreter to prevent close of
431  * stdout and stderr on DeleteInterp
432  ************************************************************/
433  if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
434  elog(ERROR, "could not create master Tcl interpreter");
435  if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
436  elog(ERROR, "could not initialize master Tcl interpreter");
437 
438  /************************************************************
439  * Create the hash table for working interpreters
440  ************************************************************/
441  memset(&hash_ctl, 0, sizeof(hash_ctl));
442  hash_ctl.keysize = sizeof(Oid);
443  hash_ctl.entrysize = sizeof(pltcl_interp_desc);
444  pltcl_interp_htab = hash_create("PL/Tcl interpreters",
445  8,
446  &hash_ctl,
448 
449  /************************************************************
450  * Create the hash table for function lookup
451  ************************************************************/
452  memset(&hash_ctl, 0, sizeof(hash_ctl));
453  hash_ctl.keysize = sizeof(pltcl_proc_key);
454  hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
455  pltcl_proc_htab = hash_create("PL/Tcl functions",
456  100,
457  &hash_ctl,
459 
460  /************************************************************
461  * Define PL/Tcl's custom GUCs
462  ************************************************************/
463  DefineCustomStringVariable("pltcl.start_proc",
464  gettext_noop("PL/Tcl function to call once when pltcl is first used."),
465  NULL,
467  NULL,
468  PGC_SUSET, 0,
469  NULL, NULL, NULL);
470  DefineCustomStringVariable("pltclu.start_proc",
471  gettext_noop("PL/TclU function to call once when pltclu is first used."),
472  NULL,
474  NULL,
475  PGC_SUSET, 0,
476  NULL, NULL, NULL);
477 
478  pltcl_pm_init_done = true;
479 }
#define TEXTDOMAIN
Definition: pltcl.c:60
struct pltcl_proc_ptr pltcl_proc_ptr
#define HASH_ELEM
Definition: hsearch.h:87
struct pltcl_proc_key pltcl_proc_key
static void pltcl_DeleteFileHandler(int fd)
Definition: pltcl.c:375
Size entrysize
Definition: hsearch.h:73
#define gettext_noop(x)
Definition: c.h:1148
static void pltcl_ServiceModeHook(int mode)
Definition: pltcl.c:380
struct pltcl_interp_desc pltcl_interp_desc
unsigned int Oid
Definition: postgres_ext.h:31
static void pltcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData clientData)
Definition: pltcl.c:369
static void pltcl_AlertNotifier(ClientData clientData)
Definition: pltcl.c:364
static bool pltcl_pm_init_done
Definition: pltcl.c:238
static char * pltcl_start_proc
Definition: pltcl.c:236
static int pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:385
#define ERROR
Definition: elog.h:43
static ClientData pltcl_InitNotifier(void)
Definition: pltcl.c:346
Definition: guc.h:75
static void pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:359
static Tcl_Interp * pltcl_hold_interp
Definition: pltcl.c:239
#define HASH_BLOBS
Definition: hsearch.h:88
void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, const char *bootValue, GucContext context, int flags, GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:8712
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
Size keysize
Definition: hsearch.h:72
static HTAB * pltcl_proc_htab
Definition: pltcl.c:241
static void pltcl_FinalizeNotifier(ClientData clientData)
Definition: pltcl.c:354
#define elog(elevel,...)
Definition: elog.h:228
void pg_bindtextdomain(const char *domain)
Definition: miscinit.c:1607
static HTAB * pltcl_interp_htab
Definition: pltcl.c:240
static char * pltclu_start_proc
Definition: pltcl.c:237

◆ call_pltcl_start_proc()

static void call_pltcl_start_proc ( Oid  prolang,
bool  pltrusted 
)
static

Definition at line 588 of file pltcl.c.

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, ErrorContextCallback::arg, ErrorContextCallback::callback, elog, ereport, errcode(), errmsg(), ERROR, error_context_stack, fmgr_info(), FunctionCallInvoke, GETSTRUCT, GetUserId(), HeapTupleIsValid, InitFunctionCallInfoData, InvalidOid, InvokeFunctionExecuteHook, LOCAL_FCINFO, LookupFuncName(), OBJECT_FUNCTION, ObjectIdGetDatum, pg_proc_aclcheck(), pgstat_end_function_usage(), pgstat_init_function_usage(), pltcl_start_proc, pltclu_start_proc, ErrorContextCallback::previous, PROCOID, ReleaseSysCache(), SearchSysCache1(), start_proc_error_callback(), stringToQualifiedNameList(), and unconstify.

Referenced by pltcl_init_interp().

589 {
590  LOCAL_FCINFO(fcinfo, 0);
591  char *start_proc;
592  const char *gucname;
593  ErrorContextCallback errcallback;
594  List *namelist;
595  Oid procOid;
596  HeapTuple procTup;
597  Form_pg_proc procStruct;
598  AclResult aclresult;
599  FmgrInfo finfo;
600  PgStat_FunctionCallUsage fcusage;
601 
602  /* select appropriate GUC */
603  start_proc = pltrusted ? pltcl_start_proc : pltclu_start_proc;
604  gucname = pltrusted ? "pltcl.start_proc" : "pltclu.start_proc";
605 
606  /* Nothing to do if it's empty or unset */
607  if (start_proc == NULL || start_proc[0] == '\0')
608  return;
609 
610  /* Set up errcontext callback to make errors more helpful */
611  errcallback.callback = start_proc_error_callback;
612  errcallback.arg = unconstify(char *, gucname);
613  errcallback.previous = error_context_stack;
614  error_context_stack = &errcallback;
615 
616  /* Parse possibly-qualified identifier and look up the function */
617  namelist = stringToQualifiedNameList(start_proc);
618  procOid = LookupFuncName(namelist, 0, NULL, false);
619 
620  /* Current user must have permission to call function */
621  aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
622  if (aclresult != ACLCHECK_OK)
623  aclcheck_error(aclresult, OBJECT_FUNCTION, start_proc);
624 
625  /* Get the function's pg_proc entry */
626  procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
627  if (!HeapTupleIsValid(procTup))
628  elog(ERROR, "cache lookup failed for function %u", procOid);
629  procStruct = (Form_pg_proc) GETSTRUCT(procTup);
630 
631  /* It must be same language as the function we're currently calling */
632  if (procStruct->prolang != prolang)
633  ereport(ERROR,
634  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
635  errmsg("function \"%s\" is in the wrong language",
636  start_proc)));
637 
638  /*
639  * It must not be SECURITY DEFINER, either. This together with the
640  * language match check ensures that the function will execute in the same
641  * Tcl interpreter we just finished initializing.
642  */
643  if (procStruct->prosecdef)
644  ereport(ERROR,
645  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
646  errmsg("function \"%s\" must not be SECURITY DEFINER",
647  start_proc)));
648 
649  /* A-OK */
650  ReleaseSysCache(procTup);
651 
652  /*
653  * Call the function using the normal SQL function call mechanism. We
654  * could perhaps cheat and jump directly to pltcl_handler(), but it seems
655  * better to do it this way so that the call is exposed to, eg, call
656  * statistics collection.
657  */
658  InvokeFunctionExecuteHook(procOid);
659  fmgr_info(procOid, &finfo);
660  InitFunctionCallInfoData(*fcinfo, &finfo,
661  0,
662  InvalidOid, NULL, NULL);
663  pgstat_init_function_usage(fcinfo, &fcusage);
664  (void) FunctionCallInvoke(fcinfo);
665  pgstat_end_function_usage(&fcusage, true);
666 
667  /* Pop the error context stack */
668  error_context_stack = errcallback.previous;
669 }
Definition: fmgr.h:56
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
Oid GetUserId(void)
Definition: miscinit.c:380
int errcode(int sqlerrcode)
Definition: elog.c:608
unsigned int Oid
Definition: postgres_ext.h:31
void(* callback)(void *arg)
Definition: elog.h:256
struct ErrorContextCallback * previous
Definition: elog.h:255
static char * pltcl_start_proc
Definition: pltcl.c:236
ErrorContextCallback * error_context_stack
Definition: elog.c:91
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3352
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
void pgstat_init_function_usage(FunctionCallInfo fcinfo, PgStat_FunctionCallUsage *fcu)
Definition: pgstat.c:1620
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2101
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:167
#define ereport(elevel, rest)
Definition: elog.h:141
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:191
#define unconstify(underlying_type, expr)
Definition: c.h:1194
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
AclResult
Definition: acl.h:177
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:133
#define InvalidOid
Definition: postgres_ext.h:36
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
static void start_proc_error_callback(void *arg)
Definition: pltcl.c:675
void pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
Definition: pgstat.c:1692
List * stringToQualifiedNameList(const char *string)
Definition: regproc.c:1687
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define ACL_EXECUTE
Definition: parsenodes.h:81
#define elog(elevel,...)
Definition: elog.h:228
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4653
static char * pltclu_start_proc
Definition: pltcl.c:237
Definition: pg_list.h:50

◆ compile_pltcl_function()

static pltcl_proc_desc * compile_pltcl_function ( Oid  fn_oid,
Oid  tgreloid,
bool  is_event_trigger,
bool  pltrusted 
)
static

Definition at line 1381 of file pltcl.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, Assert, buf, pltcl_proc_desc::domain_info, elog, ereport, errcode(), errmsg(), ERROR, fmgr_info_cxt(), pltcl_proc_desc::fn_cxt, pltcl_proc_desc::fn_readonly, pltcl_proc_desc::fn_refcount, pltcl_proc_desc::fn_retisdomain, pltcl_proc_desc::fn_retisset, pltcl_proc_desc::fn_retistuple, pltcl_proc_desc::fn_tid, pltcl_proc_desc::fn_xmin, format_type_be(), FUNC_MAX_ARGS, GETSTRUCT, getTypeIOParam(), GetUserId(), HASH_ENTER, hash_search(), HeapTupleHeaderGetRawXmin, HeapTupleIsValid, i, pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, InvalidOid, pltcl_proc_key::is_trigger, ItemPointerEquals(), pltcl_proc_desc::lanpltrusted, MemoryContextDelete(), MemoryContextSetIdentifier(), MemoryContextSwitchTo(), NameStr, pltcl_proc_desc::nargs, ObjectIdGetDatum, OidIsValid, palloc0(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_fetch_interp(), pltcl_proc_key::proc_id, pltcl_proc_ptr::proc_ptr, PROCOID, pstrdup(), ReleaseSysCache(), pltcl_proc_desc::result_in_func, pltcl_proc_desc::result_typid, pltcl_proc_desc::result_typioparam, SearchSysCache1(), snprintf, SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_self, TextDatumGetCString, TopMemoryContext, type_is_rowtype(), TYPEOID, pltcl_proc_key::user_id, pltcl_proc_desc::user_proname, UTF_BEGIN, UTF_E2U, UTF_END, and utf_u2e().

Referenced by pltcl_event_trigger_handler(), pltcl_func_handler(), and pltcl_trigger_handler().

1383 {
1384  HeapTuple procTup;
1385  Form_pg_proc procStruct;
1386  pltcl_proc_key proc_key;
1387  pltcl_proc_ptr *proc_ptr;
1388  bool found;
1389  pltcl_proc_desc *prodesc;
1390  pltcl_proc_desc *old_prodesc;
1391  volatile MemoryContext proc_cxt = NULL;
1392  Tcl_DString proc_internal_def;
1393  Tcl_DString proc_internal_body;
1394 
1395  /* We'll need the pg_proc tuple in any case... */
1396  procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
1397  if (!HeapTupleIsValid(procTup))
1398  elog(ERROR, "cache lookup failed for function %u", fn_oid);
1399  procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1400 
1401  /*
1402  * Look up function in pltcl_proc_htab; if it's not there, create an entry
1403  * and set the entry's proc_ptr to NULL.
1404  */
1405  proc_key.proc_id = fn_oid;
1406  proc_key.is_trigger = OidIsValid(tgreloid);
1407  proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
1408 
1409  proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
1410  HASH_ENTER,
1411  &found);
1412  if (!found)
1413  proc_ptr->proc_ptr = NULL;
1414 
1415  prodesc = proc_ptr->proc_ptr;
1416 
1417  /************************************************************
1418  * If it's present, must check whether it's still up to date.
1419  * This is needed because CREATE OR REPLACE FUNCTION can modify the
1420  * function's pg_proc entry without changing its OID.
1421  ************************************************************/
1422  if (prodesc != NULL &&
1423  prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
1424  ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
1425  {
1426  /* It's still up-to-date, so we can use it */
1427  ReleaseSysCache(procTup);
1428  return prodesc;
1429  }
1430 
1431  /************************************************************
1432  * If we haven't found it in the hashtable, we analyze
1433  * the functions arguments and returntype and store
1434  * the in-/out-functions in the prodesc block and create
1435  * a new hashtable entry for it.
1436  *
1437  * Then we load the procedure into the Tcl interpreter.
1438  ************************************************************/
1439  Tcl_DStringInit(&proc_internal_def);
1440  Tcl_DStringInit(&proc_internal_body);
1441  PG_TRY();
1442  {
1443  bool is_trigger = OidIsValid(tgreloid);
1444  char internal_proname[128];
1445  HeapTuple typeTup;
1446  Form_pg_type typeStruct;
1447  char proc_internal_args[33 * FUNC_MAX_ARGS];
1448  Datum prosrcdatum;
1449  bool isnull;
1450  char *proc_source;
1451  char buf[48];
1452  Tcl_Interp *interp;
1453  int i;
1454  int tcl_rc;
1455  MemoryContext oldcontext;
1456 
1457  /************************************************************
1458  * Build our internal proc name from the function's Oid. Append
1459  * "_trigger" when appropriate to ensure the normal and trigger
1460  * cases are kept separate. Note name must be all-ASCII.
1461  ************************************************************/
1462  if (is_event_trigger)
1463  snprintf(internal_proname, sizeof(internal_proname),
1464  "__PLTcl_proc_%u_evttrigger", fn_oid);
1465  else if (is_trigger)
1466  snprintf(internal_proname, sizeof(internal_proname),
1467  "__PLTcl_proc_%u_trigger", fn_oid);
1468  else
1469  snprintf(internal_proname, sizeof(internal_proname),
1470  "__PLTcl_proc_%u", fn_oid);
1471 
1472  /************************************************************
1473  * Allocate a context that will hold all PG data for the procedure.
1474  ************************************************************/
1476  "PL/Tcl function",
1478 
1479  /************************************************************
1480  * Allocate and fill a new procedure description block.
1481  * struct prodesc and subsidiary data must all live in proc_cxt.
1482  ************************************************************/
1483  oldcontext = MemoryContextSwitchTo(proc_cxt);
1484  prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
1485  prodesc->user_proname = pstrdup(NameStr(procStruct->proname));
1486  MemoryContextSetIdentifier(proc_cxt, prodesc->user_proname);
1487  prodesc->internal_proname = pstrdup(internal_proname);
1488  prodesc->fn_cxt = proc_cxt;
1489  prodesc->fn_refcount = 0;
1490  prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
1491  prodesc->fn_tid = procTup->t_self;
1492  prodesc->nargs = procStruct->pronargs;
1493  prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
1494  prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
1495  MemoryContextSwitchTo(oldcontext);
1496 
1497  /* Remember if function is STABLE/IMMUTABLE */
1498  prodesc->fn_readonly =
1499  (procStruct->provolatile != PROVOLATILE_VOLATILE);
1500  /* And whether it is trusted */
1501  prodesc->lanpltrusted = pltrusted;
1502 
1503  /************************************************************
1504  * Identify the interpreter to use for the function
1505  ************************************************************/
1506  prodesc->interp_desc = pltcl_fetch_interp(procStruct->prolang,
1507  prodesc->lanpltrusted);
1508  interp = prodesc->interp_desc->interp;
1509 
1510  /************************************************************
1511  * Get the required information for input conversion of the
1512  * return value.
1513  ************************************************************/
1514  if (!is_trigger && !is_event_trigger)
1515  {
1516  Oid rettype = procStruct->prorettype;
1517 
1518  typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
1519  if (!HeapTupleIsValid(typeTup))
1520  elog(ERROR, "cache lookup failed for type %u", rettype);
1521  typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1522 
1523  /* Disallow pseudotype result, except VOID and RECORD */
1524  if (typeStruct->typtype == TYPTYPE_PSEUDO)
1525  {
1526  if (rettype == VOIDOID ||
1527  rettype == RECORDOID)
1528  /* okay */ ;
1529  else if (rettype == TRIGGEROID ||
1530  rettype == EVTTRIGGEROID)
1531  ereport(ERROR,
1532  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1533  errmsg("trigger functions can only be called as triggers")));
1534  else
1535  ereport(ERROR,
1536  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1537  errmsg("PL/Tcl functions cannot return type %s",
1538  format_type_be(rettype))));
1539  }
1540 
1541  prodesc->result_typid = rettype;
1542  fmgr_info_cxt(typeStruct->typinput,
1543  &(prodesc->result_in_func),
1544  proc_cxt);
1545  prodesc->result_typioparam = getTypeIOParam(typeTup);
1546 
1547  prodesc->fn_retisset = procStruct->proretset;
1548  prodesc->fn_retistuple = type_is_rowtype(rettype);
1549  prodesc->fn_retisdomain = (typeStruct->typtype == TYPTYPE_DOMAIN);
1550  prodesc->domain_info = NULL;
1551 
1552  ReleaseSysCache(typeTup);
1553  }
1554 
1555  /************************************************************
1556  * Get the required information for output conversion
1557  * of all procedure arguments, and set up argument naming info.
1558  ************************************************************/
1559  if (!is_trigger && !is_event_trigger)
1560  {
1561  proc_internal_args[0] = '\0';
1562  for (i = 0; i < prodesc->nargs; i++)
1563  {
1564  Oid argtype = procStruct->proargtypes.values[i];
1565 
1566  typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
1567  if (!HeapTupleIsValid(typeTup))
1568  elog(ERROR, "cache lookup failed for type %u", argtype);
1569  typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1570 
1571  /* Disallow pseudotype argument, except RECORD */
1572  if (typeStruct->typtype == TYPTYPE_PSEUDO &&
1573  argtype != RECORDOID)
1574  ereport(ERROR,
1575  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1576  errmsg("PL/Tcl functions cannot accept type %s",
1577  format_type_be(argtype))));
1578 
1579  if (type_is_rowtype(argtype))
1580  {
1581  prodesc->arg_is_rowtype[i] = true;
1582  snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
1583  }
1584  else
1585  {
1586  prodesc->arg_is_rowtype[i] = false;
1587  fmgr_info_cxt(typeStruct->typoutput,
1588  &(prodesc->arg_out_func[i]),
1589  proc_cxt);
1590  snprintf(buf, sizeof(buf), "%d", i + 1);
1591  }
1592 
1593  if (i > 0)
1594  strcat(proc_internal_args, " ");
1595  strcat(proc_internal_args, buf);
1596 
1597  ReleaseSysCache(typeTup);
1598  }
1599  }
1600  else if (is_trigger)
1601  {
1602  /* trigger procedure has fixed args */
1603  strcpy(proc_internal_args,
1604  "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");
1605  }
1606  else if (is_event_trigger)
1607  {
1608  /* event trigger procedure has fixed args */
1609  strcpy(proc_internal_args, "TG_event TG_tag");
1610  }
1611 
1612  /************************************************************
1613  * Create the tcl command to define the internal
1614  * procedure
1615  *
1616  * Leave this code as DString - performance is not critical here,
1617  * and we don't want to duplicate the knowledge of the Tcl quoting
1618  * rules that's embedded in Tcl_DStringAppendElement.
1619  ************************************************************/
1620  Tcl_DStringAppendElement(&proc_internal_def, "proc");
1621  Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
1622  Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
1623 
1624  /************************************************************
1625  * prefix procedure body with
1626  * upvar #0 <internal_proname> GD
1627  * and with appropriate setting of arguments
1628  ************************************************************/
1629  Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
1630  Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
1631  Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
1632  if (is_trigger)
1633  {
1634  Tcl_DStringAppend(&proc_internal_body,
1635  "array set NEW $__PLTcl_Tup_NEW\n", -1);
1636  Tcl_DStringAppend(&proc_internal_body,
1637  "array set OLD $__PLTcl_Tup_OLD\n", -1);
1638  Tcl_DStringAppend(&proc_internal_body,
1639  "set i 0\n"
1640  "set v 0\n"
1641  "foreach v $args {\n"
1642  " incr i\n"
1643  " set $i $v\n"
1644  "}\n"
1645  "unset i v\n\n", -1);
1646  }
1647  else if (is_event_trigger)
1648  {
1649  /* no argument support for event triggers */
1650  }
1651  else
1652  {
1653  for (i = 0; i < prodesc->nargs; i++)
1654  {
1655  if (prodesc->arg_is_rowtype[i])
1656  {
1657  snprintf(buf, sizeof(buf),
1658  "array set %d $__PLTcl_Tup_%d\n",
1659  i + 1, i + 1);
1660  Tcl_DStringAppend(&proc_internal_body, buf, -1);
1661  }
1662  }
1663  }
1664 
1665  /************************************************************
1666  * Add user's function definition to proc body
1667  ************************************************************/
1668  prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
1669  Anum_pg_proc_prosrc, &isnull);
1670  if (isnull)
1671  elog(ERROR, "null prosrc");
1672  proc_source = TextDatumGetCString(prosrcdatum);
1673  UTF_BEGIN;
1674  Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
1675  UTF_END;
1676  pfree(proc_source);
1677  Tcl_DStringAppendElement(&proc_internal_def,
1678  Tcl_DStringValue(&proc_internal_body));
1679 
1680  /************************************************************
1681  * Create the procedure in the interpreter
1682  ************************************************************/
1683  tcl_rc = Tcl_EvalEx(interp,
1684  Tcl_DStringValue(&proc_internal_def),
1685  Tcl_DStringLength(&proc_internal_def),
1686  TCL_EVAL_GLOBAL);
1687  if (tcl_rc != TCL_OK)
1688  ereport(ERROR,
1689  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1690  errmsg("could not create internal procedure \"%s\": %s",
1691  internal_proname,
1692  utf_u2e(Tcl_GetStringResult(interp)))));
1693  }
1694  PG_CATCH();
1695  {
1696  /*
1697  * If we failed anywhere above, clean up whatever got allocated. It
1698  * should all be in the proc_cxt, except for the DStrings.
1699  */
1700  if (proc_cxt)
1701  MemoryContextDelete(proc_cxt);
1702  Tcl_DStringFree(&proc_internal_def);
1703  Tcl_DStringFree(&proc_internal_body);
1704  PG_RE_THROW();
1705  }
1706  PG_END_TRY();
1707 
1708  /*
1709  * Install the new proc description block in the hashtable, incrementing
1710  * its refcount (the hashtable link counts as a reference). Then, if
1711  * there was a previous definition of the function, decrement that one's
1712  * refcount, and delete it if no longer referenced. The order of
1713  * operations here is important: if something goes wrong during the
1714  * MemoryContextDelete, leaking some memory for the old definition is OK,
1715  * but we don't want to corrupt the live hashtable entry. (Likewise,
1716  * freeing the DStrings is pretty low priority if that happens.)
1717  */
1718  old_prodesc = proc_ptr->proc_ptr;
1719 
1720  proc_ptr->proc_ptr = prodesc;
1721  prodesc->fn_refcount++;
1722 
1723  if (old_prodesc != NULL)
1724  {
1725  Assert(old_prodesc->fn_refcount > 0);
1726  if (--old_prodesc->fn_refcount == 0)
1727  MemoryContextDelete(old_prodesc->fn_cxt);
1728  }
1729 
1730  Tcl_DStringFree(&proc_internal_def);
1731  Tcl_DStringFree(&proc_internal_body);
1732 
1733  ReleaseSysCache(procTup);
1734 
1735  return prodesc;
1736 }
bool lanpltrusted
Definition: pltcl.c:144
Definition: fmgr.h:56
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
bool fn_retistuple
Definition: pltcl.c:150
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
Oid GetUserId(void)
Definition: miscinit.c:380
FmgrInfo result_in_func
Definition: pltcl.c:147
char * pstrdup(const char *in)
Definition: mcxt.c:1186
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:202
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
char * user_proname
Definition: pltcl.c:137
void * domain_info
Definition: pltcl.c:152
int errcode(int sqlerrcode)
Definition: elog.c:608
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
Oid proc_id
Definition: pltcl.c:187
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
bool fn_retisdomain
Definition: pltcl.c:151
HeapTupleHeader t_data
Definition: htup.h:68
#define FUNC_MAX_ARGS
void pfree(void *pointer)
Definition: mcxt.c:1056
#define UTF_E2U(x)
Definition: pltcl.c:98
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
FmgrInfo * arg_out_func
Definition: pltcl.c:155
ItemPointerData t_self
Definition: htup.h:65
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
static char * buf
Definition: pg_test_fsync.c:67
#define UTF_END
Definition: pltcl.c:90
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
ItemPointerData fn_tid
Definition: pltcl.c:142
unsigned long fn_refcount
Definition: pltcl.c:140
Tcl_Interp * interp
Definition: pltcl.c:115
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2433
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:134
#define ereport(elevel, rest)
Definition: elog.h:141
MemoryContext TopMemoryContext
Definition: mcxt.c:44
bool fn_readonly
Definition: pltcl.c:143
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
#define TextDatumGetCString(d)
Definition: builtins.h:84
void * palloc0(Size size)
Definition: mcxt.c:980
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
Oid result_typioparam
Definition: pltcl.c:148
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:133
#define InvalidOid
Definition: postgres_ext.h:36
#define UTF_BEGIN
Definition: pltcl.c:85
char * internal_proname
Definition: pltcl.c:138
#define PG_CATCH()
Definition: elog.h:332
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:739
pltcl_proc_desc * proc_ptr
Definition: pltcl.c:200
Oid is_trigger
Definition: pltcl.c:193
FormData_pg_type * Form_pg_type
Definition: pg_type.h:250
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:329
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:308
#define PG_RE_THROW()
Definition: elog.h:363
static pltcl_interp_desc * pltcl_fetch_interp(Oid prolang, bool pltrusted)
Definition: pltcl.c:558
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
TransactionId fn_xmin
Definition: pltcl.c:141
static HTAB * pltcl_proc_htab
Definition: pltcl.c:241
Oid result_typid
Definition: pltcl.c:146
MemoryContext fn_cxt
Definition: pltcl.c:139
bool fn_retisset
Definition: pltcl.c:149
Oid user_id
Definition: pltcl.c:194
int errmsg(const char *fmt,...)
Definition: elog.c:822
bool * arg_is_rowtype
Definition: pltcl.c:156
#define elog(elevel,...)
Definition: elog.h:228
int i
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2081
#define NameStr(name)
Definition: c.h:616
#define PG_TRY()
Definition: elog.h:322
#define snprintf
Definition: port.h:192
#define PG_END_TRY()
Definition: elog.h:347

◆ PG_FUNCTION_INFO_V1() [1/2]

PG_FUNCTION_INFO_V1 ( pltcl_call_handler  )

◆ PG_FUNCTION_INFO_V1() [2/2]

PG_FUNCTION_INFO_V1 ( pltclu_call_handler  )

◆ pltcl_AlertNotifier()

static void pltcl_AlertNotifier ( ClientData  clientData)
static

Definition at line 364 of file pltcl.c.

Referenced by _PG_init().

365 {
366 }

◆ pltcl_argisnull()

static int pltcl_argisnull ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2047 of file pltcl.c.

References pltcl_call_state::fcinfo, FunctionCallInfoBaseData::nargs, and PG_ARGISNULL.

Referenced by pltcl_init_interp().

2049 {
2050  int argno;
2052 
2053  /************************************************************
2054  * Check call syntax
2055  ************************************************************/
2056  if (objc != 2)
2057  {
2058  Tcl_WrongNumArgs(interp, 1, objv, "argno");
2059  return TCL_ERROR;
2060  }
2061 
2062  /************************************************************
2063  * Check that we're called as a normal function
2064  ************************************************************/
2065  if (fcinfo == NULL)
2066  {
2067  Tcl_SetObjResult(interp,
2068  Tcl_NewStringObj("argisnull cannot be used in triggers", -1));
2069  return TCL_ERROR;
2070  }
2071 
2072  /************************************************************
2073  * Get the argument number
2074  ************************************************************/
2075  if (Tcl_GetIntFromObj(interp, objv[1], &argno) != TCL_OK)
2076  return TCL_ERROR;
2077 
2078  /************************************************************
2079  * Check that the argno is valid
2080  ************************************************************/
2081  argno--;
2082  if (argno < 0 || argno >= fcinfo->nargs)
2083  {
2084  Tcl_SetObjResult(interp,
2085  Tcl_NewStringObj("argno out of range", -1));
2086  return TCL_ERROR;
2087  }
2088 
2089  /************************************************************
2090  * Get the requested NULL state
2091  ************************************************************/
2092  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(PG_ARGISNULL(argno)));
2093  return TCL_OK;
2094 }
FunctionCallInfo fcinfo
Definition: pltcl.c:210
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
#define PG_ARGISNULL(n)
Definition: fmgr.h:204

◆ pltcl_build_tuple_argument()

static Tcl_Obj * pltcl_build_tuple_argument ( HeapTuple  tuple,
TupleDesc  tupdesc,
bool  include_generated 
)
static

Definition at line 3090 of file pltcl.c.

References attname, getTypeOutputInfo(), heap_getattr, i, NameStr, TupleDescData::natts, OidOutputFunctionCall(), pfree(), TupleDescAttr, UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

3091 {
3092  Tcl_Obj *retobj = Tcl_NewObj();
3093  int i;
3094  char *outputstr;
3095  Datum attr;
3096  bool isnull;
3097  char *attname;
3098  Oid typoutput;
3099  bool typisvarlena;
3100 
3101  for (i = 0; i < tupdesc->natts; i++)
3102  {
3103  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3104 
3105  /* ignore dropped attributes */
3106  if (att->attisdropped)
3107  continue;
3108 
3109  if (att->attgenerated)
3110  {
3111  /* don't include unless requested */
3112  if (!include_generated)
3113  continue;
3114  }
3115 
3116  /************************************************************
3117  * Get the attribute name
3118  ************************************************************/
3119  attname = NameStr(att->attname);
3120 
3121  /************************************************************
3122  * Get the attributes value
3123  ************************************************************/
3124  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3125 
3126  /************************************************************
3127  * If there is a value, append the attribute name and the
3128  * value to the list
3129  *
3130  * Hmmm - Null attributes will cause functions to
3131  * crash if they don't expect them - need something
3132  * smarter here.
3133  ************************************************************/
3134  if (!isnull)
3135  {
3136  getTypeOutputInfo(att->atttypid,
3137  &typoutput, &typisvarlena);
3138  outputstr = OidOutputFunctionCall(typoutput, attr);
3139  UTF_BEGIN;
3140  Tcl_ListObjAppendElement(NULL, retobj,
3141  Tcl_NewStringObj(UTF_E2U(attname), -1));
3142  UTF_END;
3143  UTF_BEGIN;
3144  Tcl_ListObjAppendElement(NULL, retobj,
3145  Tcl_NewStringObj(UTF_E2U(outputstr), -1));
3146  UTF_END;
3147  pfree(outputstr);
3148  }
3149  }
3150 
3151  return retobj;
3152 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2674
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
unsigned int Oid
Definition: postgres_ext.h:31
void pfree(void *pointer)
Definition: mcxt.c:1056
#define UTF_E2U(x)
Definition: pltcl.c:98
NameData attname
Definition: pg_attribute.h:40
#define UTF_END
Definition: pltcl.c:90
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:762
uintptr_t Datum
Definition: postgres.h:367
#define UTF_BEGIN
Definition: pltcl.c:85
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1655
int i
#define NameStr(name)
Definition: c.h:616

◆ pltcl_build_tuple_result()

static HeapTuple pltcl_build_tuple_result ( Tcl_Interp *  interp,
Tcl_Obj **  kvObjv,
int  kvObjc,
pltcl_call_state call_state 
)
static

Definition at line 3166 of file pltcl.c.

References pltcl_call_state::attinmeta, BuildTupleFromCStrings(), domain_check(), pltcl_proc_desc::domain_info, elog, ereport, errcode(), errmsg(), ERROR, pltcl_proc_desc::fn_cxt, pltcl_proc_desc::fn_retisdomain, HeapTupleGetDatum, i, TupleDescData::natts, palloc0(), pltcl_call_state::prodesc, RelationGetDescr, pltcl_proc_desc::result_typid, pltcl_call_state::ret_tupdesc, SPI_ERROR_NOATTRIBUTE, SPI_fnumber(), TriggerData::tg_relation, pltcl_call_state::trigdata, TupleDescAttr, TupleDescGetAttInMetadata(), utf_u2e(), and values.

Referenced by pltcl_func_handler(), pltcl_returnnext(), and pltcl_trigger_handler().

3168 {
3169  HeapTuple tuple;
3170  TupleDesc tupdesc;
3171  AttInMetadata *attinmeta;
3172  char **values;
3173  int i;
3174 
3175  if (call_state->ret_tupdesc)
3176  {
3177  tupdesc = call_state->ret_tupdesc;
3178  attinmeta = call_state->attinmeta;
3179  }
3180  else if (call_state->trigdata)
3181  {
3182  tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
3183  attinmeta = TupleDescGetAttInMetadata(tupdesc);
3184  }
3185  else
3186  {
3187  elog(ERROR, "PL/Tcl function does not return a tuple");
3188  tupdesc = NULL; /* keep compiler quiet */
3189  attinmeta = NULL;
3190  }
3191 
3192  values = (char **) palloc0(tupdesc->natts * sizeof(char *));
3193 
3194  if (kvObjc % 2 != 0)
3195  ereport(ERROR,
3196  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3197  errmsg("column name/value list must have even number of elements")));
3198 
3199  for (i = 0; i < kvObjc; i += 2)
3200  {
3201  char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
3202  int attn = SPI_fnumber(tupdesc, fieldName);
3203 
3204  /*
3205  * We silently ignore ".tupno", if it's present but doesn't match any
3206  * actual output column. This allows direct use of a row returned by
3207  * pltcl_set_tuple_values().
3208  */
3209  if (attn == SPI_ERROR_NOATTRIBUTE)
3210  {
3211  if (strcmp(fieldName, ".tupno") == 0)
3212  continue;
3213  ereport(ERROR,
3214  (errcode(ERRCODE_UNDEFINED_COLUMN),
3215  errmsg("column name/value list contains nonexistent column name \"%s\"",
3216  fieldName)));
3217  }
3218 
3219  if (attn <= 0)
3220  ereport(ERROR,
3221  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3222  errmsg("cannot set system attribute \"%s\"",
3223  fieldName)));
3224 
3225  if (TupleDescAttr(tupdesc, attn - 1)->attgenerated)
3226  ereport(ERROR,
3227  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3228  errmsg("cannot set generated column \"%s\"",
3229  fieldName)));
3230 
3231  values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
3232  }
3233 
3234  tuple = BuildTupleFromCStrings(attinmeta, values);
3235 
3236  /* if result type is domain-over-composite, check domain constraints */
3237  if (call_state->prodesc->fn_retisdomain)
3238  domain_check(HeapTupleGetDatum(tuple), false,
3239  call_state->prodesc->result_typid,
3240  &call_state->prodesc->domain_info,
3241  call_state->prodesc->fn_cxt);
3242 
3243  return tuple;
3244 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:951
#define RelationGetDescr(relation)
Definition: rel.h:448
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
void * domain_info
Definition: pltcl.c:152
int errcode(int sqlerrcode)
Definition: elog.c:608
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:2116
bool fn_retisdomain
Definition: pltcl.c:151
AttInMetadata * attinmeta
Definition: pltcl.c:224
#define ERROR
Definition: elog.h:43
#define SPI_ERROR_NOATTRIBUTE
Definition: spi.h:47
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
#define ereport(elevel, rest)
Definition: elog.h:141
TupleDesc ret_tupdesc
Definition: pltcl.c:223
void * palloc0(Size size)
Definition: mcxt.c:980
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2067
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:327
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:220
Oid result_typid
Definition: pltcl.c:146
static Datum values[MAXATTR]
Definition: bootstrap.c:167
MemoryContext fn_cxt
Definition: pltcl.c:139
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
int i
TriggerData * trigdata
Definition: pltcl.c:213
Relation tg_relation
Definition: trigger.h:34

◆ pltcl_call_handler()

Datum pltcl_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 695 of file pltcl.c.

References PG_FUNCTION_INFO_V1(), pltcl_handler(), and pltclu_call_handler().

Referenced by start_proc_error_callback().

696 {
697  return pltcl_handler(fcinfo, true);
698 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:718

◆ pltcl_commit()

static int pltcl_commit ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2923 of file pltcl.c.

References CopyErrorData(), CurrentMemoryContext, FlushErrorState(), FreeErrorData(), MemoryContextSwitchTo(), ErrorData::message, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_construct_errorCode(), SPI_commit(), SPI_start_transaction(), UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_init_interp().

2925 {
2926  MemoryContext oldcontext = CurrentMemoryContext;
2927 
2928  PG_TRY();
2929  {
2930  SPI_commit();
2932  }
2933  PG_CATCH();
2934  {
2935  ErrorData *edata;
2936 
2937  /* Save error info */
2938  MemoryContextSwitchTo(oldcontext);
2939  edata = CopyErrorData();
2940  FlushErrorState();
2941 
2942  /* Pass the error data to Tcl */
2943  pltcl_construct_errorCode(interp, edata);
2944  UTF_BEGIN;
2945  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
2946  UTF_END;
2947  FreeErrorData(edata);
2948 
2949  return TCL_ERROR;
2950  }
2951  PG_END_TRY();
2952 
2953  return TCL_OK;
2954 }
ErrorData * CopyErrorData(void)
Definition: elog.c:1584
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void FlushErrorState(void)
Definition: elog.c:1678
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1640
#define UTF_E2U(x)
Definition: pltcl.c:98
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
Definition: pltcl.c:1830
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
void SPI_commit(void)
Definition: spi.c:278
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
void SPI_start_transaction(void)
Definition: spi.c:212
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
char * message
Definition: elog.h:392

◆ pltcl_construct_errorCode()

static void pltcl_construct_errorCode ( Tcl_Interp *  interp,
ErrorData edata 
)
static

Definition at line 1830 of file pltcl.c.

References ErrorData::column_name, ErrorData::constraint_name, ErrorData::context, ErrorData::datatype_name, ErrorData::detail, ErrorData::filename, ErrorData::funcname, ErrorData::hint, ErrorData::internalpos, ErrorData::internalquery, ErrorData::lineno, ErrorData::message, pltcl_get_condition_name(), ErrorData::schema_name, ErrorData::sqlerrcode, ErrorData::table_name, unpack_sql_state(), UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_commit(), pltcl_elog(), pltcl_rollback(), and pltcl_subtrans_abort().

1831 {
1832  Tcl_Obj *obj = Tcl_NewObj();
1833 
1834  Tcl_ListObjAppendElement(interp, obj,
1835  Tcl_NewStringObj("POSTGRES", -1));
1836  Tcl_ListObjAppendElement(interp, obj,
1837  Tcl_NewStringObj(PG_VERSION, -1));
1838  Tcl_ListObjAppendElement(interp, obj,
1839  Tcl_NewStringObj("SQLSTATE", -1));
1840  Tcl_ListObjAppendElement(interp, obj,
1841  Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode), -1));
1842  Tcl_ListObjAppendElement(interp, obj,
1843  Tcl_NewStringObj("condition", -1));
1844  Tcl_ListObjAppendElement(interp, obj,
1845  Tcl_NewStringObj(pltcl_get_condition_name(edata->sqlerrcode), -1));
1846  Tcl_ListObjAppendElement(interp, obj,
1847  Tcl_NewStringObj("message", -1));
1848  UTF_BEGIN;
1849  Tcl_ListObjAppendElement(interp, obj,
1850  Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1851  UTF_END;
1852  if (edata->detail)
1853  {
1854  Tcl_ListObjAppendElement(interp, obj,
1855  Tcl_NewStringObj("detail", -1));
1856  UTF_BEGIN;
1857  Tcl_ListObjAppendElement(interp, obj,
1858  Tcl_NewStringObj(UTF_E2U(edata->detail), -1));
1859  UTF_END;
1860  }
1861  if (edata->hint)
1862  {
1863  Tcl_ListObjAppendElement(interp, obj,
1864  Tcl_NewStringObj("hint", -1));
1865  UTF_BEGIN;
1866  Tcl_ListObjAppendElement(interp, obj,
1867  Tcl_NewStringObj(UTF_E2U(edata->hint), -1));
1868  UTF_END;
1869  }
1870  if (edata->context)
1871  {
1872  Tcl_ListObjAppendElement(interp, obj,
1873  Tcl_NewStringObj("context", -1));
1874  UTF_BEGIN;
1875  Tcl_ListObjAppendElement(interp, obj,
1876  Tcl_NewStringObj(UTF_E2U(edata->context), -1));
1877  UTF_END;
1878  }
1879  if (edata->schema_name)
1880  {
1881  Tcl_ListObjAppendElement(interp, obj,
1882  Tcl_NewStringObj("schema", -1));
1883  UTF_BEGIN;
1884  Tcl_ListObjAppendElement(interp, obj,
1885  Tcl_NewStringObj(UTF_E2U(edata->schema_name), -1));
1886  UTF_END;
1887  }
1888  if (edata->table_name)
1889  {
1890  Tcl_ListObjAppendElement(interp, obj,
1891  Tcl_NewStringObj("table", -1));
1892  UTF_BEGIN;
1893  Tcl_ListObjAppendElement(interp, obj,
1894  Tcl_NewStringObj(UTF_E2U(edata->table_name), -1));
1895  UTF_END;
1896  }
1897  if (edata->column_name)
1898  {
1899  Tcl_ListObjAppendElement(interp, obj,
1900  Tcl_NewStringObj("column", -1));
1901  UTF_BEGIN;
1902  Tcl_ListObjAppendElement(interp, obj,
1903  Tcl_NewStringObj(UTF_E2U(edata->column_name), -1));
1904  UTF_END;
1905  }
1906  if (edata->datatype_name)
1907  {
1908  Tcl_ListObjAppendElement(interp, obj,
1909  Tcl_NewStringObj("datatype", -1));
1910  UTF_BEGIN;
1911  Tcl_ListObjAppendElement(interp, obj,
1912  Tcl_NewStringObj(UTF_E2U(edata->datatype_name), -1));
1913  UTF_END;
1914  }
1915  if (edata->constraint_name)
1916  {
1917  Tcl_ListObjAppendElement(interp, obj,
1918  Tcl_NewStringObj("constraint", -1));
1919  UTF_BEGIN;
1920  Tcl_ListObjAppendElement(interp, obj,
1921  Tcl_NewStringObj(UTF_E2U(edata->constraint_name), -1));
1922  UTF_END;
1923  }
1924  /* cursorpos is never interesting here; report internal query/pos */
1925  if (edata->internalquery)
1926  {
1927  Tcl_ListObjAppendElement(interp, obj,
1928  Tcl_NewStringObj("statement", -1));
1929  UTF_BEGIN;
1930  Tcl_ListObjAppendElement(interp, obj,
1931  Tcl_NewStringObj(UTF_E2U(edata->internalquery), -1));
1932  UTF_END;
1933  }
1934  if (edata->internalpos > 0)
1935  {
1936  Tcl_ListObjAppendElement(interp, obj,
1937  Tcl_NewStringObj("cursor_position", -1));
1938  Tcl_ListObjAppendElement(interp, obj,
1939  Tcl_NewIntObj(edata->internalpos));
1940  }
1941  if (edata->filename)
1942  {
1943  Tcl_ListObjAppendElement(interp, obj,
1944  Tcl_NewStringObj("filename", -1));
1945  UTF_BEGIN;
1946  Tcl_ListObjAppendElement(interp, obj,
1947  Tcl_NewStringObj(UTF_E2U(edata->filename), -1));
1948  UTF_END;
1949  }
1950  if (edata->lineno > 0)
1951  {
1952  Tcl_ListObjAppendElement(interp, obj,
1953  Tcl_NewStringObj("lineno", -1));
1954  Tcl_ListObjAppendElement(interp, obj,
1955  Tcl_NewIntObj(edata->lineno));
1956  }
1957  if (edata->funcname)
1958  {
1959  Tcl_ListObjAppendElement(interp, obj,
1960  Tcl_NewStringObj("funcname", -1));
1961  UTF_BEGIN;
1962  Tcl_ListObjAppendElement(interp, obj,
1963  Tcl_NewStringObj(UTF_E2U(edata->funcname), -1));
1964  UTF_END;
1965  }
1966 
1967  Tcl_SetObjErrorCode(interp, obj);
1968 }
char * schema_name
Definition: elog.h:399
int sqlerrcode
Definition: elog.h:391
const char * funcname
Definition: elog.h:388
char * unpack_sql_state(int sql_state)
Definition: elog.c:2939
int lineno
Definition: elog.h:387
static const char * pltcl_get_condition_name(int sqlstate)
Definition: pltcl.c:1975
char * internalquery
Definition: elog.h:406
#define UTF_E2U(x)
Definition: pltcl.c:98
const char * filename
Definition: elog.h:386
char * table_name
Definition: elog.h:400
#define UTF_END
Definition: pltcl.c:90
int internalpos
Definition: elog.h:405
char * datatype_name
Definition: elog.h:402
char * detail
Definition: elog.h:393
char * column_name
Definition: elog.h:401
#define UTF_BEGIN
Definition: pltcl.c:85
char * hint
Definition: elog.h:395
char * context
Definition: elog.h:396
char * constraint_name
Definition: elog.h:403
char * message
Definition: elog.h:392

◆ pltcl_CreateFileHandler()

static void pltcl_CreateFileHandler ( int  fd,
int  mask,
Tcl_FileProc *  proc,
ClientData  clientData 
)
static

Definition at line 369 of file pltcl.c.

Referenced by _PG_init().

371 {
372 }

◆ pltcl_DeleteFileHandler()

static void pltcl_DeleteFileHandler ( int  fd)
static

Definition at line 375 of file pltcl.c.

Referenced by _PG_init().

376 {
377 }

◆ pltcl_elog()

static int pltcl_elog ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 1743 of file pltcl.c.

References CopyErrorData(), CurrentMemoryContext, DEBUG2, ereport, errcode(), errmsg(), ERROR, FATAL, FlushErrorState(), FreeErrorData(), INFO, LOG, MemoryContextSwitchTo(), ErrorData::message, NOTICE, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_construct_errorCode(), UTF_BEGIN, UTF_E2U, UTF_END, UTF_U2E, and WARNING.

Referenced by pltcl_init_interp().

1745 {
1746  volatile int level;
1747  MemoryContext oldcontext;
1748  int priIndex;
1749 
1750  static const char *logpriorities[] = {
1751  "DEBUG", "LOG", "INFO", "NOTICE",
1752  "WARNING", "ERROR", "FATAL", (const char *) NULL
1753  };
1754 
1755  static const int loglevels[] = {
1756  DEBUG2, LOG, INFO, NOTICE,
1757  WARNING, ERROR, FATAL
1758  };
1759 
1760  if (objc != 3)
1761  {
1762  Tcl_WrongNumArgs(interp, 1, objv, "level msg");
1763  return TCL_ERROR;
1764  }
1765 
1766  if (Tcl_GetIndexFromObj(interp, objv[1], logpriorities, "priority",
1767  TCL_EXACT, &priIndex) != TCL_OK)
1768  return TCL_ERROR;
1769 
1770  level = loglevels[priIndex];
1771 
1772  if (level == ERROR)
1773  {
1774  /*
1775  * We just pass the error back to Tcl. If it's not caught, it'll
1776  * eventually get converted to a PG error when we reach the call
1777  * handler.
1778  */
1779  Tcl_SetObjResult(interp, objv[2]);
1780  return TCL_ERROR;
1781  }
1782 
1783  /*
1784  * For non-error messages, just pass 'em to ereport(). We do not expect
1785  * that this will fail, but just on the off chance it does, report the
1786  * error back to Tcl. Note we are assuming that ereport() can't have any
1787  * internal failures that are so bad as to require a transaction abort.
1788  *
1789  * This path is also used for FATAL errors, which aren't going to come
1790  * back to us at all.
1791  */
1792  oldcontext = CurrentMemoryContext;
1793  PG_TRY();
1794  {
1795  UTF_BEGIN;
1796  ereport(level,
1797  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1798  errmsg("%s", UTF_U2E(Tcl_GetString(objv[2])))));
1799  UTF_END;
1800  }
1801  PG_CATCH();
1802  {
1803  ErrorData *edata;
1804 
1805  /* Must reset elog.c's state */
1806  MemoryContextSwitchTo(oldcontext);
1807  edata = CopyErrorData();
1808  FlushErrorState();
1809 
1810  /* Pass the error data to Tcl */
1811  pltcl_construct_errorCode(interp, edata);
1812  UTF_BEGIN;
1813  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1814  UTF_END;
1815  FreeErrorData(edata);
1816 
1817  return TCL_ERROR;
1818  }
1819  PG_END_TRY();
1820 
1821  return TCL_OK;
1822 }
ErrorData * CopyErrorData(void)
Definition: elog.c:1584
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:608
#define INFO
Definition: elog.h:33
#define LOG
Definition: elog.h:26
void FlushErrorState(void)
Definition: elog.c:1678
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1640
#define UTF_E2U(x)
Definition: pltcl.c:98
#define ERROR
Definition: elog.h:43
#define FATAL
Definition: elog.h:52
#define DEBUG2
Definition: elog.h:24
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
Definition: pltcl.c:1830
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define ereport(elevel, rest)
Definition: elog.h:141
#define WARNING
Definition: elog.h:40
#define UTF_U2E(x)
Definition: pltcl.c:95
#define NOTICE
Definition: elog.h:37
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
char * message
Definition: elog.h:392

◆ pltcl_event_trigger_handler()

static void pltcl_event_trigger_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

Definition at line 1302 of file pltcl.c.

References compile_pltcl_function(), elog, ERROR, EventTriggerData::event, pltcl_proc_desc::fn_refcount, pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, InvalidOid, pltcl_call_state::prodesc, SPI_connect(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, EventTriggerData::tag, throw_tcl_error(), pltcl_proc_desc::user_proname, and utf_e2u().

Referenced by pltcl_handler().

1304 {
1305  pltcl_proc_desc *prodesc;
1306  Tcl_Interp *volatile interp;
1307  EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
1308  Tcl_Obj *tcl_cmd;
1309  int tcl_rc;
1310 
1311  /* Connect to SPI manager */
1312  if (SPI_connect() != SPI_OK_CONNECT)
1313  elog(ERROR, "could not connect to SPI manager");
1314 
1315  /* Find or compile the function */
1316  prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1317  InvalidOid, true, pltrusted);
1318 
1319  call_state->prodesc = prodesc;
1320  prodesc->fn_refcount++;
1321 
1322  interp = prodesc->interp_desc->interp;
1323 
1324  /* Create the tcl command and call the internal proc */
1325  tcl_cmd = Tcl_NewObj();
1326  Tcl_IncrRefCount(tcl_cmd);
1327  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1328  Tcl_NewStringObj(prodesc->internal_proname, -1));
1329  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1330  Tcl_NewStringObj(utf_e2u(tdata->event), -1));
1331  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1332  Tcl_NewStringObj(utf_e2u(tdata->tag), -1));
1333 
1334  tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
1335 
1336  /* Release refcount to free tcl_cmd (and all subsidiary objects) */
1337  Tcl_DecrRefCount(tcl_cmd);
1338 
1339  /* Check for errors reported by Tcl. */
1340  if (tcl_rc != TCL_OK)
1341  throw_tcl_error(interp, prodesc->user_proname);
1342 
1343  if (SPI_finish() != SPI_OK_FINISH)
1344  elog(ERROR, "SPI_finish() failed");
1345 }
#define SPI_OK_CONNECT
Definition: spi.h:53
int SPI_connect(void)
Definition: spi.c:89
int SPI_finish(void)
Definition: spi.c:176
char * user_proname
Definition: pltcl.c:137
const char * tag
Definition: event_trigger.h:28
static void throw_tcl_error(Tcl_Interp *interp, const char *proname)
Definition: pltcl.c:1352
#define ERROR
Definition: elog.h:43
const char * event
Definition: event_trigger.h:26
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
unsigned long fn_refcount
Definition: pltcl.c:140
Tcl_Interp * interp
Definition: pltcl.c:115
static pltcl_proc_desc * compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
Definition: pltcl.c:1381
static char * utf_e2u(const char *src)
Definition: pltcl.c:80
#define InvalidOid
Definition: postgres_ext.h:36
char * internal_proname
Definition: pltcl.c:138
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
#define SPI_OK_FINISH
Definition: spi.h:54
#define elog(elevel,...)
Definition: elog.h:228

◆ pltcl_fetch_interp()

static pltcl_interp_desc * pltcl_fetch_interp ( Oid  prolang,
bool  pltrusted 
)
static

Definition at line 558 of file pltcl.c.

References GetUserId(), HASH_ENTER, hash_search(), pltcl_interp_desc::interp, InvalidOid, pltcl_init_interp(), and pltcl_interp_desc::user_id.

Referenced by compile_pltcl_function().

559 {
560  Oid user_id;
561  pltcl_interp_desc *interp_desc;
562  bool found;
563 
564  /* Find or create the interpreter hashtable entry for this userid */
565  if (pltrusted)
566  user_id = GetUserId();
567  else
568  user_id = InvalidOid;
569 
570  interp_desc = hash_search(pltcl_interp_htab, &user_id,
571  HASH_ENTER,
572  &found);
573  if (!found)
574  interp_desc->interp = NULL;
575 
576  /* If we haven't yet successfully made an interpreter, try to do that */
577  if (!interp_desc->interp)
578  pltcl_init_interp(interp_desc, prolang, pltrusted);
579 
580  return interp_desc;
581 }
static void pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
Definition: pltcl.c:485
Oid GetUserId(void)
Definition: miscinit.c:380
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
unsigned int Oid
Definition: postgres_ext.h:31
Tcl_Interp * interp
Definition: pltcl.c:115
#define InvalidOid
Definition: postgres_ext.h:36
static HTAB * pltcl_interp_htab
Definition: pltcl.c:240

◆ pltcl_FinalizeNotifier()

static void pltcl_FinalizeNotifier ( ClientData  clientData)
static

Definition at line 354 of file pltcl.c.

Referenced by _PG_init().

355 {
356 }

◆ pltcl_func_handler()

static Datum pltcl_func_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

Definition at line 789 of file pltcl.c.

References ReturnSetInfo::allowedModes, pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, Assert, pltcl_call_state::attinmeta, castNode, compile_pltcl_function(), CreateTupleDescCopy(), CurrentResourceOwner, DatumGetHeapTupleHeader, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, pltcl_proc_desc::fn_refcount, pltcl_proc_desc::fn_retisdomain, pltcl_proc_desc::fn_retisset, pltcl_proc_desc::fn_retistuple, get_call_result_type(), HeapTupleGetDatum, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, InputFunctionCall(), pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, InvalidOid, IsA, lookup_rowtype_tupdesc(), MemoryContextSwitchTo(), pltcl_proc_desc::nargs, OutputFunctionCall(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_build_tuple_argument(), pltcl_build_tuple_result(), pltcl_call_state::prodesc, ReleaseTupleDesc, pltcl_proc_desc::result_in_func, pltcl_proc_desc::result_typioparam, pltcl_call_state::ret_tupdesc, ReturnSetInfo::returnMode, pltcl_call_state::rsi, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SPI_connect_ext(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_OPT_NONATOMIC, HeapTupleData::t_data, HeapTupleData::t_len, throw_tcl_error(), pltcl_call_state::tuple_store, pltcl_call_state::tuple_store_cxt, pltcl_call_state::tuple_store_owner, TupleDescGetAttInMetadata(), TYPEFUNC_COMPOSITE, TYPEFUNC_COMPOSITE_DOMAIN, TYPEFUNC_RECORD, pltcl_proc_desc::user_proname, UTF_BEGIN, UTF_E2U, UTF_END, and utf_u2e().

Referenced by pltcl_handler().

791 {
792  bool nonatomic;
793  pltcl_proc_desc *prodesc;
794  Tcl_Interp *volatile interp;
795  Tcl_Obj *tcl_cmd;
796  int i;
797  int tcl_rc;
798  Datum retval;
799 
800  nonatomic = fcinfo->context &&
801  IsA(fcinfo->context, CallContext) &&
802  !castNode(CallContext, fcinfo->context)->atomic;
803 
804  /* Connect to SPI manager */
805  if (SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0) != SPI_OK_CONNECT)
806  elog(ERROR, "could not connect to SPI manager");
807 
808  /* Find or compile the function */
809  prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
810  false, pltrusted);
811 
812  call_state->prodesc = prodesc;
813  prodesc->fn_refcount++;
814 
815  interp = prodesc->interp_desc->interp;
816 
817  /*
818  * If we're a SRF, check caller can handle materialize mode, and save
819  * relevant info into call_state. We must ensure that the returned
820  * tuplestore is owned by the caller's context, even if we first create it
821  * inside a subtransaction.
822  */
823  if (prodesc->fn_retisset)
824  {
825  ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
826 
827  if (!rsi || !IsA(rsi, ReturnSetInfo) ||
828  (rsi->allowedModes & SFRM_Materialize) == 0)
829  ereport(ERROR,
830  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
831  errmsg("set-valued function called in context that cannot accept a set")));
832 
833  call_state->rsi = rsi;
834  call_state->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
836  }
837 
838  /************************************************************
839  * Create the tcl command to call the internal
840  * proc in the Tcl interpreter
841  ************************************************************/
842  tcl_cmd = Tcl_NewObj();
843  Tcl_ListObjAppendElement(NULL, tcl_cmd,
844  Tcl_NewStringObj(prodesc->internal_proname, -1));
845 
846  /* We hold a refcount on tcl_cmd just to be sure it stays around */
847  Tcl_IncrRefCount(tcl_cmd);
848 
849  /************************************************************
850  * Add all call arguments to the command
851  ************************************************************/
852  PG_TRY();
853  {
854  for (i = 0; i < prodesc->nargs; i++)
855  {
856  if (prodesc->arg_is_rowtype[i])
857  {
858  /**************************************************
859  * For tuple values, add a list for 'array set ...'
860  **************************************************/
861  if (fcinfo->args[i].isnull)
862  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
863  else
864  {
865  HeapTupleHeader td;
866  Oid tupType;
867  int32 tupTypmod;
868  TupleDesc tupdesc;
869  HeapTupleData tmptup;
870  Tcl_Obj *list_tmp;
871 
872  td = DatumGetHeapTupleHeader(fcinfo->args[i].value);
873  /* Extract rowtype info and find a tupdesc */
874  tupType = HeapTupleHeaderGetTypeId(td);
875  tupTypmod = HeapTupleHeaderGetTypMod(td);
876  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
877  /* Build a temporary HeapTuple control structure */
879  tmptup.t_data = td;
880 
881  list_tmp = pltcl_build_tuple_argument(&tmptup, tupdesc, true);
882  Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);
883 
884  ReleaseTupleDesc(tupdesc);
885  }
886  }
887  else
888  {
889  /**************************************************
890  * Single values are added as string element
891  * of their external representation
892  **************************************************/
893  if (fcinfo->args[i].isnull)
894  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
895  else
896  {
897  char *tmp;
898 
899  tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
900  fcinfo->args[i].value);
901  UTF_BEGIN;
902  Tcl_ListObjAppendElement(NULL, tcl_cmd,
903  Tcl_NewStringObj(UTF_E2U(tmp), -1));
904  UTF_END;
905  pfree(tmp);
906  }
907  }
908  }
909  }
910  PG_CATCH();
911  {
912  /* Release refcount to free tcl_cmd */
913  Tcl_DecrRefCount(tcl_cmd);
914  PG_RE_THROW();
915  }
916  PG_END_TRY();
917 
918  /************************************************************
919  * Call the Tcl function
920  *
921  * We assume no PG error can be thrown directly from this call.
922  ************************************************************/
923  tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
924 
925  /* Release refcount to free tcl_cmd (and all subsidiary objects) */
926  Tcl_DecrRefCount(tcl_cmd);
927 
928  /************************************************************
929  * Check for errors reported by Tcl.
930  ************************************************************/
931  if (tcl_rc != TCL_OK)
932  throw_tcl_error(interp, prodesc->user_proname);
933 
934  /************************************************************
935  * Disconnect from SPI manager and then create the return
936  * value datum (if the input function does a palloc for it
937  * this must not be allocated in the SPI memory context
938  * because SPI_finish would free it). But don't try to call
939  * the result_in_func if we've been told to return a NULL;
940  * the Tcl result may not be a valid value of the result type
941  * in that case.
942  ************************************************************/
943  if (SPI_finish() != SPI_OK_FINISH)
944  elog(ERROR, "SPI_finish() failed");
945 
946  if (prodesc->fn_retisset)
947  {
948  ReturnSetInfo *rsi = call_state->rsi;
949 
950  /* We already checked this is OK */
952 
953  /* If we produced any tuples, send back the result */
954  if (call_state->tuple_store)
955  {
956  rsi->setResult = call_state->tuple_store;
957  if (call_state->ret_tupdesc)
958  {
959  MemoryContext oldcxt;
960 
961  oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
962  rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
963  MemoryContextSwitchTo(oldcxt);
964  }
965  }
966  retval = (Datum) 0;
967  fcinfo->isnull = true;
968  }
969  else if (fcinfo->isnull)
970  {
971  retval = InputFunctionCall(&prodesc->result_in_func,
972  NULL,
973  prodesc->result_typioparam,
974  -1);
975  }
976  else if (prodesc->fn_retistuple)
977  {
978  TupleDesc td;
979  HeapTuple tup;
980  Tcl_Obj *resultObj;
981  Tcl_Obj **resultObjv;
982  int resultObjc;
983 
984  /*
985  * Set up data about result type. XXX it's tempting to consider
986  * caching this in the prodesc, in the common case where the rowtype
987  * is determined by the function not the calling query. But we'd have
988  * to be able to deal with ADD/DROP/ALTER COLUMN events when the
989  * result type is a named composite type, so it's not exactly trivial.
990  * Maybe worth improving someday.
991  */
992  switch (get_call_result_type(fcinfo, NULL, &td))
993  {
994  case TYPEFUNC_COMPOSITE:
995  /* success */
996  break;
998  Assert(prodesc->fn_retisdomain);
999  break;
1000  case TYPEFUNC_RECORD:
1001  /* failed to determine actual type of RECORD */
1002  ereport(ERROR,
1003  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1004  errmsg("function returning record called in context "
1005  "that cannot accept type record")));
1006  break;
1007  default:
1008  /* result type isn't composite? */
1009  elog(ERROR, "return type must be a row type");
1010  break;
1011  }
1012 
1013  Assert(!call_state->ret_tupdesc);
1014  Assert(!call_state->attinmeta);
1015  call_state->ret_tupdesc = td;
1016  call_state->attinmeta = TupleDescGetAttInMetadata(td);
1017 
1018  /* Convert function result to tuple */
1019  resultObj = Tcl_GetObjResult(interp);
1020  if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
1021  throw_tcl_error(interp, prodesc->user_proname);
1022 
1023  tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
1024  call_state);
1025  retval = HeapTupleGetDatum(tup);
1026  }
1027  else
1028  retval = InputFunctionCall(&prodesc->result_in_func,
1029  utf_u2e(Tcl_GetStringResult(interp)),
1030  prodesc->result_typioparam,
1031  -1);
1032 
1033  return retval;
1034 }
#define SPI_OK_CONNECT
Definition: spi.h:53
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:110
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
bool fn_retistuple
Definition: pltcl.c:150
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:196
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1652
int SPI_connect_ext(int options)
Definition: spi.c:95
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
FmgrInfo result_in_func
Definition: pltcl.c:147
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
int SPI_finish(void)
Definition: spi.c:176
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
char * user_proname
Definition: pltcl.c:137
int errcode(int sqlerrcode)
Definition: elog.c:608
unsigned int Oid
Definition: postgres_ext.h:31
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:289
static void throw_tcl_error(Tcl_Interp *interp, const char *proname)
Definition: pltcl.c:1352
ResourceOwner tuple_store_owner
Definition: pltcl.c:229
signed int int32
Definition: c.h:347
bool fn_retisdomain
Definition: pltcl.c:151
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1575
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:468
void pfree(void *pointer)
Definition: mcxt.c:1056
AttInMetadata * attinmeta
Definition: pltcl.c:224
#define UTF_E2U(x)
Definition: pltcl.c:98
#define ERROR
Definition: elog.h:43
FmgrInfo * arg_out_func
Definition: pltcl.c:155
#define SPI_OPT_NONATOMIC
Definition: spi.h:71
ReturnSetInfo * rsi
Definition: pltcl.c:226
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
uint32 t_len
Definition: htup.h:64
#define UTF_END
Definition: pltcl.c:90
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
unsigned long fn_refcount
Definition: pltcl.c:140
Tcl_Interp * interp
Definition: pltcl.c:115
MemoryContext tuple_store_cxt
Definition: pltcl.c:228
Tuplestorestate * tuple_store
Definition: pltcl.c:227
#define ereport(elevel, rest)
Definition: elog.h:141
TupleDesc ret_tupdesc
Definition: pltcl.c:223
static pltcl_proc_desc * compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
Definition: pltcl.c:1381
uintptr_t Datum
Definition: postgres.h:367
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2067
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:458
Oid result_typioparam
Definition: pltcl.c:148
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1531
#define InvalidOid
Definition: postgres_ext.h:36
int allowedModes
Definition: execnodes.h:302
static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
Definition: pltcl.c:3166
#define UTF_BEGIN
Definition: pltcl.c:85
char * internal_proname
Definition: pltcl.c:138
SetFunctionReturnMode returnMode
Definition: execnodes.h:304
#define PG_CATCH()
Definition: elog.h:332
#define Assert(condition)
Definition: c.h:739
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
#define SPI_OK_FINISH
Definition: spi.h:54
#define PG_RE_THROW()
Definition: elog.h:363
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:220
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:230
Tuplestorestate * setResult
Definition: execnodes.h:307
ExprContext * econtext
Definition: execnodes.h:300
bool fn_retisset
Definition: pltcl.c:149
TupleDesc setDesc
Definition: execnodes.h:308
int errmsg(const char *fmt,...)
Definition: elog.c:822
bool * arg_is_rowtype
Definition: pltcl.c:156
#define elog(elevel,...)
Definition: elog.h:228
int i
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
static Tcl_Obj * pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
Definition: pltcl.c:3090
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:452

◆ pltcl_get_condition_name()

static const char * pltcl_get_condition_name ( int  sqlstate)
static

Definition at line 1975 of file pltcl.c.

References i, and TclExceptionNameMap::label.

Referenced by pltcl_construct_errorCode().

1976 {
1977  int i;
1978 
1979  for (i = 0; exception_name_map[i].label != NULL; i++)
1980  {
1981  if (exception_name_map[i].sqlerrstate == sqlstate)
1982  return exception_name_map[i].label;
1983  }
1984  return "unrecognized_sqlstate";
1985 }
static const TclExceptionNameMap exception_name_map[]
Definition: pltcl.c:255
const char * label
Definition: pltcl.c:251
int i

◆ pltcl_handler()

static Datum pltcl_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
)
static

Definition at line 718 of file pltcl.c.

References Assert, CALLED_AS_EVENT_TRIGGER, CALLED_AS_TRIGGER, pltcl_call_state::fcinfo, pltcl_proc_desc::fn_cxt, pltcl_proc_desc::fn_refcount, MemoryContextDelete(), PG_END_TRY, PG_FINALLY, PG_TRY, pltcl_current_call_state, pltcl_event_trigger_handler(), pltcl_func_handler(), pltcl_trigger_handler(), PointerGetDatum, and pltcl_call_state::prodesc.

Referenced by pltcl_call_handler(), and pltclu_call_handler().

719 {
720  Datum retval = (Datum) 0;
721  pltcl_call_state current_call_state;
722  pltcl_call_state *save_call_state;
723 
724  /*
725  * Initialize current_call_state to nulls/zeroes; in particular, set its
726  * prodesc pointer to null. Anything that sets it non-null should
727  * increase the prodesc's fn_refcount at the same time. We'll decrease
728  * the refcount, and then delete the prodesc if it's no longer referenced,
729  * on the way out of this function. This ensures that prodescs live as
730  * long as needed even if somebody replaces the originating pg_proc row
731  * while they're executing.
732  */
733  memset(&current_call_state, 0, sizeof(current_call_state));
734 
735  /*
736  * Ensure that static pointer is saved/restored properly
737  */
738  save_call_state = pltcl_current_call_state;
739  pltcl_current_call_state = &current_call_state;
740 
741  PG_TRY();
742  {
743  /*
744  * Determine if called as function or trigger and call appropriate
745  * subhandler
746  */
747  if (CALLED_AS_TRIGGER(fcinfo))
748  {
749  /* invoke the trigger handler */
750  retval = PointerGetDatum(pltcl_trigger_handler(fcinfo,
751  &current_call_state,
752  pltrusted));
753  }
754  else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
755  {
756  /* invoke the event trigger handler */
757  pltcl_event_trigger_handler(fcinfo, &current_call_state, pltrusted);
758  retval = (Datum) 0;
759  }
760  else
761  {
762  /* invoke the regular function handler */
763  current_call_state.fcinfo = fcinfo;
764  retval = pltcl_func_handler(fcinfo, &current_call_state, pltrusted);
765  }
766  }
767  PG_FINALLY();
768  {
769  /* Restore static pointer, then clean up the prodesc refcount if any */
770  /* (We're being paranoid in case an error is thrown in context deletion) */
771  pltcl_current_call_state = save_call_state;
772  if (current_call_state.prodesc != NULL)
773  {
774  Assert(current_call_state.prodesc->fn_refcount > 0);
775  if (--current_call_state.prodesc->fn_refcount == 0)
776  MemoryContextDelete(current_call_state.prodesc->fn_cxt);
777  }
778  }
779  PG_END_TRY();
780 
781  return retval;
782 }
#define CALLED_AS_EVENT_TRIGGER(fcinfo)
Definition: event_trigger.h:39
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define PointerGetDatum(X)
Definition: postgres.h:556
FunctionCallInfo fcinfo
Definition: pltcl.c:210
static Datum pltcl_func_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
Definition: pltcl.c:789
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
static void pltcl_event_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
Definition: pltcl.c:1302
unsigned long fn_refcount
Definition: pltcl.c:140
static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
Definition: pltcl.c:1041
#define PG_FINALLY()
Definition: elog.h:339
uintptr_t Datum
Definition: postgres.h:367
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
#define Assert(condition)
Definition: c.h:739
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
MemoryContext fn_cxt
Definition: pltcl.c:139
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347

◆ pltcl_init_interp()

static void pltcl_init_interp ( pltcl_interp_desc interp_desc,
Oid  prolang,
bool  pltrusted 
)
static

Definition at line 485 of file pltcl.c.

References call_pltcl_start_proc(), elog, ERROR, pltcl_interp_desc::interp, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, pltcl_argisnull(), pltcl_commit(), pltcl_elog(), pltcl_hold_interp, pltcl_quote(), pltcl_returnnext(), pltcl_returnnull(), pltcl_rollback(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_prepare(), pltcl_subtransaction(), pltcl_interp_desc::query_hash, snprintf, and pltcl_interp_desc::user_id.

Referenced by pltcl_fetch_interp().

486 {
487  Tcl_Interp *interp;
488  char interpname[32];
489 
490  /************************************************************
491  * Create the Tcl interpreter as a slave of pltcl_hold_interp.
492  * Note: Tcl automatically does Tcl_Init in the untrusted case,
493  * and it's not wanted in the trusted case.
494  ************************************************************/
495  snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
496  if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
497  pltrusted ? 1 : 0)) == NULL)
498  elog(ERROR, "could not create slave Tcl interpreter");
499 
500  /************************************************************
501  * Initialize the query hash table associated with interpreter
502  ************************************************************/
503  Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
504 
505  /************************************************************
506  * Install the commands for SPI support in the interpreter
507  ************************************************************/
508  Tcl_CreateObjCommand(interp, "elog",
509  pltcl_elog, NULL, NULL);
510  Tcl_CreateObjCommand(interp, "quote",
511  pltcl_quote, NULL, NULL);
512  Tcl_CreateObjCommand(interp, "argisnull",
513  pltcl_argisnull, NULL, NULL);
514  Tcl_CreateObjCommand(interp, "return_null",
515  pltcl_returnnull, NULL, NULL);
516  Tcl_CreateObjCommand(interp, "return_next",
517  pltcl_returnnext, NULL, NULL);
518  Tcl_CreateObjCommand(interp, "spi_exec",
519  pltcl_SPI_execute, NULL, NULL);
520  Tcl_CreateObjCommand(interp, "spi_prepare",
521  pltcl_SPI_prepare, NULL, NULL);
522  Tcl_CreateObjCommand(interp, "spi_execp",
523  pltcl_SPI_execute_plan, NULL, NULL);
524  Tcl_CreateObjCommand(interp, "subtransaction",
525  pltcl_subtransaction, NULL, NULL);
526  Tcl_CreateObjCommand(interp, "commit",
527  pltcl_commit, NULL, NULL);
528  Tcl_CreateObjCommand(interp, "rollback",
529  pltcl_rollback, NULL, NULL);
530 
531  /************************************************************
532  * Call the appropriate start_proc, if there is one.
533  *
534  * We must set interp_desc->interp before the call, else the start_proc
535  * won't find the interpreter it's supposed to use. But, if the
536  * start_proc fails, we want to abandon use of the interpreter.
537  ************************************************************/
538  PG_TRY();
539  {
540  interp_desc->interp = interp;
541  call_pltcl_start_proc(prolang, pltrusted);
542  }
543  PG_CATCH();
544  {
545  interp_desc->interp = NULL;
546  Tcl_DeleteInterp(interp);
547  PG_RE_THROW();
548  }
549  PG_END_TRY();
550 }
static int pltcl_rollback(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2963
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2047
static int pltcl_commit(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2923
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:1743
static int pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2875
static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2529
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:1993
static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2656
#define ERROR
Definition: elog.h:43
static void call_pltcl_start_proc(Oid prolang, bool pltrusted)
Definition: pltcl.c:588
static Tcl_Interp * pltcl_hold_interp
Definition: pltcl.c:239
Tcl_Interp * interp
Definition: pltcl.c:115
#define PG_CATCH()
Definition: elog.h:332
static int pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2139
#define PG_RE_THROW()
Definition: elog.h:363
#define elog(elevel,...)
Definition: elog.h:228
static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2101
#define PG_TRY()
Definition: elog.h:322
#define snprintf
Definition: port.h:192
#define PG_END_TRY()
Definition: elog.h:347
Tcl_HashTable query_hash
Definition: pltcl.c:116
static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2309

◆ pltcl_init_tuple_store()

static void pltcl_init_tuple_store ( pltcl_call_state call_state)
static

Definition at line 3250 of file pltcl.c.

References ReturnSetInfo::allowedModes, Assert, pltcl_call_state::attinmeta, CurrentResourceOwner, ReturnSetInfo::expectedDesc, MemoryContextSwitchTo(), pltcl_call_state::ret_tupdesc, pltcl_call_state::rsi, SFRM_Materialize_Random, pltcl_call_state::tuple_store, pltcl_call_state::tuple_store_cxt, pltcl_call_state::tuple_store_owner, TupleDescGetAttInMetadata(), tuplestore_begin_heap(), and work_mem.

Referenced by pltcl_returnnext().

3251 {
3252  ReturnSetInfo *rsi = call_state->rsi;
3253  MemoryContext oldcxt;
3254  ResourceOwner oldowner;
3255 
3256  /* Should be in a SRF */
3257  Assert(rsi);
3258  /* Should be first time through */
3259  Assert(!call_state->tuple_store);
3260  Assert(!call_state->attinmeta);
3261 
3262  /* We expect caller to provide an appropriate result tupdesc */
3263  Assert(rsi->expectedDesc);
3264  call_state->ret_tupdesc = rsi->expectedDesc;
3265 
3266  /*
3267  * Switch to the right memory context and resource owner for storing the
3268  * tuplestore. If we're within a subtransaction opened for an exception
3269  * block, for example, we must still create the tuplestore in the resource
3270  * owner that was active when this function was entered, and not in the
3271  * subtransaction's resource owner.
3272  */
3273  oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
3274  oldowner = CurrentResourceOwner;
3276 
3277  call_state->tuple_store =
3279  false, work_mem);
3280 
3281  /* Build attinmeta in this context, too */
3282  call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
3283 
3284  CurrentResourceOwner = oldowner;
3285  MemoryContextSwitchTo(oldcxt);
3286 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ResourceOwner tuple_store_owner
Definition: pltcl.c:229
TupleDesc expectedDesc
Definition: execnodes.h:301
AttInMetadata * attinmeta
Definition: pltcl.c:224
ReturnSetInfo * rsi
Definition: pltcl.c:226
MemoryContext tuple_store_cxt
Definition: pltcl.c:228
Tuplestorestate * tuple_store
Definition: pltcl.c:227
TupleDesc ret_tupdesc
Definition: pltcl.c:223
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2067
int work_mem
Definition: globals.c:121
int allowedModes
Definition: execnodes.h:302
#define Assert(condition)
Definition: c.h:739

◆ pltcl_InitNotifier()

static ClientData pltcl_InitNotifier ( void  )
static

Definition at line 346 of file pltcl.c.

Referenced by _PG_init().

347 {
348  static int fakeThreadKey; /* To give valid address for ClientData */
349 
350  return (ClientData) &(fakeThreadKey);
351 }

◆ pltcl_process_SPI_result()

static int pltcl_process_SPI_result ( Tcl_Interp *  interp,
const char *  arrayname,
Tcl_Obj *  loop_body,
int  spi_rc,
SPITupleTable tuptable,
uint64  ntuples 
)
static

Definition at line 2418 of file pltcl.c.

References i, pltcl_set_tuple_values(), SPI_freetuptable(), SPI_OK_DELETE, SPI_OK_DELETE_RETURNING, SPI_OK_INSERT, SPI_OK_INSERT_RETURNING, SPI_OK_REWRITTEN, SPI_OK_SELECT, SPI_OK_SELINTO, SPI_OK_UPDATE, SPI_OK_UPDATE_RETURNING, SPI_OK_UTILITY, SPI_result_code_string(), SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by pltcl_SPI_execute(), and pltcl_SPI_execute_plan().

2424 {
2425  int my_rc = TCL_OK;
2426  int loop_rc;
2427  HeapTuple *tuples;
2428  TupleDesc tupdesc;
2429 
2430  switch (spi_rc)
2431  {
2432  case SPI_OK_SELINTO:
2433  case SPI_OK_INSERT:
2434  case SPI_OK_DELETE:
2435  case SPI_OK_UPDATE:
2436  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2437  break;
2438 
2439  case SPI_OK_UTILITY:
2440  case SPI_OK_REWRITTEN:
2441  if (tuptable == NULL)
2442  {
2443  Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
2444  break;
2445  }
2446  /* fall through for utility returning tuples */
2447  /* FALLTHROUGH */
2448 
2449  case SPI_OK_SELECT:
2453 
2454  /*
2455  * Process the tuples we got
2456  */
2457  tuples = tuptable->vals;
2458  tupdesc = tuptable->tupdesc;
2459 
2460  if (loop_body == NULL)
2461  {
2462  /*
2463  * If there is no loop body given, just set the variables from
2464  * the first tuple (if any)
2465  */
2466  if (ntuples > 0)
2467  pltcl_set_tuple_values(interp, arrayname, 0,
2468  tuples[0], tupdesc);
2469  }
2470  else
2471  {
2472  /*
2473  * There is a loop body - process all tuples and evaluate the
2474  * body on each
2475  */
2476  uint64 i;
2477 
2478  for (i = 0; i < ntuples; i++)
2479  {
2480  pltcl_set_tuple_values(interp, arrayname, i,
2481  tuples[i], tupdesc);
2482 
2483  loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
2484 
2485  if (loop_rc == TCL_OK)
2486  continue;
2487  if (loop_rc == TCL_CONTINUE)
2488  continue;
2489  if (loop_rc == TCL_RETURN)
2490  {
2491  my_rc = TCL_RETURN;
2492  break;
2493  }
2494  if (loop_rc == TCL_BREAK)
2495  break;
2496  my_rc = TCL_ERROR;
2497  break;
2498  }
2499  }
2500 
2501  if (my_rc == TCL_OK)
2502  {
2503  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2504  }
2505  break;
2506 
2507  default:
2508  Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
2509  SPI_result_code_string(spi_rc), NULL);
2510  my_rc = TCL_ERROR;
2511  break;
2512  }
2513 
2514  SPI_freetuptable(tuptable);
2515 
2516  return my_rc;
2517 }
#define SPI_OK_DELETE_RETURNING
Definition: spi.h:64
#define SPI_OK_DELETE
Definition: spi.h:60
HeapTuple * vals
Definition: spi.h:26
#define SPI_OK_INSERT_RETURNING
Definition: spi.h:63
const char * SPI_result_code_string(int code)
Definition: spi.c:1705
#define SPI_OK_UTILITY
Definition: spi.h:56
#define SPI_OK_UPDATE_RETURNING
Definition: spi.h:65
#define SPI_OK_REWRITTEN
Definition: spi.h:66
#define SPI_OK_SELINTO
Definition: spi.h:58
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:1162
TupleDesc tupdesc
Definition: spi.h:25
#define SPI_OK_SELECT
Definition: spi.h:57
#define SPI_OK_UPDATE
Definition: spi.h:61
int i
#define SPI_OK_INSERT
Definition: spi.h:59
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname, uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
Definition: pltcl.c:3004

◆ pltcl_quote()

static int pltcl_quote ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 1993 of file pltcl.c.

References palloc(), and pfree().

Referenced by pltcl_init_interp().

1995 {
1996  char *tmp;
1997  const char *cp1;
1998  char *cp2;
1999  int length;
2000 
2001  /************************************************************
2002  * Check call syntax
2003  ************************************************************/
2004  if (objc != 2)
2005  {
2006  Tcl_WrongNumArgs(interp, 1, objv, "string");
2007  return TCL_ERROR;
2008  }
2009 
2010  /************************************************************
2011  * Allocate space for the maximum the string can
2012  * grow to and initialize pointers
2013  ************************************************************/
2014  cp1 = Tcl_GetStringFromObj(objv[1], &length);
2015  tmp = palloc(length * 2 + 1);
2016  cp2 = tmp;
2017 
2018  /************************************************************
2019  * Walk through string and double every quote and backslash
2020  ************************************************************/
2021  while (*cp1)
2022  {
2023  if (*cp1 == '\'')
2024  *cp2++ = '\'';
2025  else
2026  {
2027  if (*cp1 == '\\')
2028  *cp2++ = '\\';
2029  }
2030  *cp2++ = *cp1++;
2031  }
2032 
2033  /************************************************************
2034  * Terminate the string and set it as result
2035  ************************************************************/
2036  *cp2 = '\0';
2037  Tcl_SetObjResult(interp, Tcl_NewStringObj(tmp, -1));
2038  pfree(tmp);
2039  return TCL_OK;
2040 }
void pfree(void *pointer)
Definition: mcxt.c:1056
void * palloc(Size size)
Definition: mcxt.c:949

◆ pltcl_returnnext()

static int pltcl_returnnext ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2139 of file pltcl.c.

References BeginInternalSubTransaction(), CurrentMemoryContext, CurrentResourceOwner, elog, ERROR, pltcl_call_state::fcinfo, pltcl_proc_desc::fn_retisset, pltcl_proc_desc::fn_retistuple, InputFunctionCall(), TupleDescData::natts, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_build_tuple_result(), pltcl_current_call_state, pltcl_init_tuple_store(), pltcl_subtrans_abort(), pltcl_subtrans_commit(), pltcl_call_state::prodesc, pltcl_proc_desc::result_in_func, pltcl_proc_desc::result_typioparam, pltcl_call_state::ret_tupdesc, pltcl_call_state::tuple_store, tuplestore_puttuple(), tuplestore_putvalues(), and utf_u2e().

Referenced by pltcl_init_interp().

2141 {
2143  FunctionCallInfo fcinfo = call_state->fcinfo;
2144  pltcl_proc_desc *prodesc = call_state->prodesc;
2145  MemoryContext oldcontext = CurrentMemoryContext;
2147  volatile int result = TCL_OK;
2148 
2149  /*
2150  * Check that we're called as a set-returning function
2151  */
2152  if (fcinfo == NULL)
2153  {
2154  Tcl_SetObjResult(interp,
2155  Tcl_NewStringObj("return_next cannot be used in triggers", -1));
2156  return TCL_ERROR;
2157  }
2158 
2159  if (!prodesc->fn_retisset)
2160  {
2161  Tcl_SetObjResult(interp,
2162  Tcl_NewStringObj("return_next cannot be used in non-set-returning functions", -1));
2163  return TCL_ERROR;
2164  }
2165 
2166  /*
2167  * Check call syntax
2168  */
2169  if (objc != 2)
2170  {
2171  Tcl_WrongNumArgs(interp, 1, objv, "result");
2172  return TCL_ERROR;
2173  }
2174 
2175  /*
2176  * The rest might throw elog(ERROR), so must run in a subtransaction.
2177  *
2178  * A small advantage of using a subtransaction is that it provides a
2179  * short-lived memory context for free, so we needn't worry about leaking
2180  * memory here. To use that context, call BeginInternalSubTransaction
2181  * directly instead of going through pltcl_subtrans_begin.
2182  */
2184  PG_TRY();
2185  {
2186  /* Set up tuple store if first output row */
2187  if (call_state->tuple_store == NULL)
2188  pltcl_init_tuple_store(call_state);
2189 
2190  if (prodesc->fn_retistuple)
2191  {
2192  Tcl_Obj **rowObjv;
2193  int rowObjc;
2194 
2195  /* result should be a list, so break it down */
2196  if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
2197  result = TCL_ERROR;
2198  else
2199  {
2200  HeapTuple tuple;
2201 
2202  tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
2203  call_state);
2204  tuplestore_puttuple(call_state->tuple_store, tuple);
2205  }
2206  }
2207  else
2208  {
2209  Datum retval;
2210  bool isNull = false;
2211 
2212  /* for paranoia's sake, check that tupdesc has exactly one column */
2213  if (call_state->ret_tupdesc->natts != 1)
2214  elog(ERROR, "wrong result type supplied in return_next");
2215 
2216  retval = InputFunctionCall(&prodesc->result_in_func,
2217  utf_u2e((char *) Tcl_GetString(objv[1])),
2218  prodesc->result_typioparam,
2219  -1);
2220  tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
2221  &retval, &isNull);
2222  }
2223 
2224  pltcl_subtrans_commit(oldcontext, oldowner);
2225  }
2226  PG_CATCH();
2227  {
2228  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2229  return TCL_ERROR;
2230  }
2231  PG_END_TRY();
2232 
2233  return result;
2234 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
bool fn_retistuple
Definition: pltcl.c:150
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2280
static void pltcl_init_tuple_store(pltcl_call_state *call_state)
Definition: pltcl.c:3250
FmgrInfo result_in_func
Definition: pltcl.c:147
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
FunctionCallInfo fcinfo
Definition: pltcl.c:210
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
#define ERROR
Definition: elog.h:43
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:730
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
Tuplestorestate * tuple_store
Definition: pltcl.c:227
TupleDesc ret_tupdesc
Definition: pltcl.c:223
uintptr_t Datum
Definition: postgres.h:367
Oid result_typioparam
Definition: pltcl.c:148
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1531
static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
Definition: pltcl.c:3166
#define PG_CATCH()
Definition: elog.h:332
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4359
bool fn_retisset
Definition: pltcl.c:149
#define elog(elevel,...)
Definition: elog.h:228
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347

◆ pltcl_returnnull()

static int pltcl_returnnull ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2101 of file pltcl.c.

References pltcl_call_state::fcinfo, and FunctionCallInfoBaseData::isnull.

Referenced by pltcl_init_interp().

2103 {
2105 
2106  /************************************************************
2107  * Check call syntax
2108  ************************************************************/
2109  if (objc != 1)
2110  {
2111  Tcl_WrongNumArgs(interp, 1, objv, "");
2112  return TCL_ERROR;
2113  }
2114 
2115  /************************************************************
2116  * Check that we're called as a normal function
2117  ************************************************************/
2118  if (fcinfo == NULL)
2119  {
2120  Tcl_SetObjResult(interp,
2121  Tcl_NewStringObj("return_null cannot be used in triggers", -1));
2122  return TCL_ERROR;
2123  }
2124 
2125  /************************************************************
2126  * Set the NULL return flag and cause Tcl to return from the
2127  * procedure.
2128  ************************************************************/
2129  fcinfo->isnull = true;
2130 
2131  return TCL_RETURN;
2132 }
FunctionCallInfo fcinfo
Definition: pltcl.c:210
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244

◆ pltcl_rollback()

static int pltcl_rollback ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2963 of file pltcl.c.

References CopyErrorData(), CurrentMemoryContext, FlushErrorState(), FreeErrorData(), MemoryContextSwitchTo(), ErrorData::message, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_construct_errorCode(), SPI_rollback(), SPI_start_transaction(), UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_init_interp().

2965 {
2966  MemoryContext oldcontext = CurrentMemoryContext;
2967 
2968  PG_TRY();
2969  {
2970  SPI_rollback();
2972  }
2973  PG_CATCH();
2974  {
2975  ErrorData *edata;
2976 
2977  /* Save error info */
2978  MemoryContextSwitchTo(oldcontext);
2979  edata = CopyErrorData();
2980  FlushErrorState();
2981 
2982  /* Pass the error data to Tcl */
2983  pltcl_construct_errorCode(interp, edata);
2984  UTF_BEGIN;
2985  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
2986  UTF_END;
2987  FreeErrorData(edata);
2988 
2989  return TCL_ERROR;
2990  }
2991  PG_END_TRY();
2992 
2993  return TCL_OK;
2994 }
ErrorData * CopyErrorData(void)
Definition: elog.c:1584
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void SPI_rollback(void)
Definition: spi.c:333
void FlushErrorState(void)
Definition: elog.c:1678
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1640
#define UTF_E2U(x)
Definition: pltcl.c:98
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
Definition: pltcl.c:1830
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
void SPI_start_transaction(void)
Definition: spi.c:212
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
char * message
Definition: elog.h:392

◆ pltcl_ServiceModeHook()

static void pltcl_ServiceModeHook ( int  mode)
static

Definition at line 380 of file pltcl.c.

Referenced by _PG_init().

381 {
382 }

◆ pltcl_set_tuple_values()

static void pltcl_set_tuple_values ( Tcl_Interp *  interp,
const char *  arrayname,
uint64  tupno,
HeapTuple  tuple,
TupleDesc  tupdesc 
)
static

Definition at line 3004 of file pltcl.c.

References attname, getTypeOutputInfo(), heap_getattr, i, NameStr, TupleDescData::natts, OidOutputFunctionCall(), pfree(), pstrdup(), TupleDescAttr, unconstify, UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_process_SPI_result().

3006 {
3007  int i;
3008  char *outputstr;
3009  Datum attr;
3010  bool isnull;
3011  const char *attname;
3012  Oid typoutput;
3013  bool typisvarlena;
3014  const char **arrptr;
3015  const char **nameptr;
3016  const char *nullname = NULL;
3017 
3018  /************************************************************
3019  * Prepare pointers for Tcl_SetVar2Ex() below
3020  ************************************************************/
3021  if (arrayname == NULL)
3022  {
3023  arrptr = &attname;
3024  nameptr = &nullname;
3025  }
3026  else
3027  {
3028  arrptr = &arrayname;
3029  nameptr = &attname;
3030 
3031  /*
3032  * When outputting to an array, fill the ".tupno" element with the
3033  * current tuple number. This will be overridden below if ".tupno" is
3034  * in use as an actual field name in the rowtype.
3035  */
3036  Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
3037  }
3038 
3039  for (i = 0; i < tupdesc->natts; i++)
3040  {
3041  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3042 
3043  /* ignore dropped attributes */
3044  if (att->attisdropped)
3045  continue;
3046 
3047  /************************************************************
3048  * Get the attribute name
3049  ************************************************************/
3050  UTF_BEGIN;
3051  attname = pstrdup(UTF_E2U(NameStr(att->attname)));
3052  UTF_END;
3053 
3054  /************************************************************
3055  * Get the attributes value
3056  ************************************************************/
3057  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3058 
3059  /************************************************************
3060  * If there is a value, set the variable
3061  * If not, unset it
3062  *
3063  * Hmmm - Null attributes will cause functions to
3064  * crash if they don't expect them - need something
3065  * smarter here.
3066  ************************************************************/
3067  if (!isnull)
3068  {
3069  getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
3070  outputstr = OidOutputFunctionCall(typoutput, attr);
3071  UTF_BEGIN;
3072  Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
3073  Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
3074  UTF_END;
3075  pfree(outputstr);
3076  }
3077  else
3078  Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
3079 
3080  pfree(unconstify(char *, attname));
3081  }
3082 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2674
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
char * pstrdup(const char *in)
Definition: mcxt.c:1186
unsigned int Oid
Definition: postgres_ext.h:31
void pfree(void *pointer)
Definition: mcxt.c:1056
#define UTF_E2U(x)
Definition: pltcl.c:98
NameData attname
Definition: pg_attribute.h:40
#define UTF_END
Definition: pltcl.c:90
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:762
#define unconstify(underlying_type, expr)
Definition: c.h:1194
uintptr_t Datum
Definition: postgres.h:367
#define UTF_BEGIN
Definition: pltcl.c:85
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1655
int i
#define NameStr(name)
Definition: c.h:616

◆ pltcl_SetTimer()

static void pltcl_SetTimer ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 359 of file pltcl.c.

Referenced by _PG_init().

360 {
361 }

◆ pltcl_SPI_execute()

static int pltcl_SPI_execute ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2309 of file pltcl.c.

References CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_process_SPI_result(), pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), pltcl_call_state::prodesc, SPI_execute(), SPI_processed, SPI_tuptable, UTF_BEGIN, UTF_END, and UTF_U2E.

Referenced by pltcl_init_interp().

2311 {
2312  int my_rc;
2313  int spi_rc;
2314  int query_idx;
2315  int i;
2316  int optIndex;
2317  int count = 0;
2318  const char *volatile arrayname = NULL;
2319  Tcl_Obj *volatile loop_body = NULL;
2320  MemoryContext oldcontext = CurrentMemoryContext;
2322 
2323  enum options
2324  {
2325  OPT_ARRAY, OPT_COUNT
2326  };
2327 
2328  static const char *options[] = {
2329  "-array", "-count", (const char *) NULL
2330  };
2331 
2332  /************************************************************
2333  * Check the call syntax and get the options
2334  ************************************************************/
2335  if (objc < 2)
2336  {
2337  Tcl_WrongNumArgs(interp, 1, objv,
2338  "?-count n? ?-array name? query ?loop body?");
2339  return TCL_ERROR;
2340  }
2341 
2342  i = 1;
2343  while (i < objc)
2344  {
2345  if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2346  TCL_EXACT, &optIndex) != TCL_OK)
2347  break;
2348 
2349  if (++i >= objc)
2350  {
2351  Tcl_SetObjResult(interp,
2352  Tcl_NewStringObj("missing argument to -count or -array", -1));
2353  return TCL_ERROR;
2354  }
2355 
2356  switch ((enum options) optIndex)
2357  {
2358  case OPT_ARRAY:
2359  arrayname = Tcl_GetString(objv[i++]);
2360  break;
2361 
2362  case OPT_COUNT:
2363  if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2364  return TCL_ERROR;
2365  break;
2366  }
2367  }
2368 
2369  query_idx = i;
2370  if (query_idx >= objc || query_idx + 2 < objc)
2371  {
2372  Tcl_WrongNumArgs(interp, query_idx - 1, objv, "query ?loop body?");
2373  return TCL_ERROR;
2374  }
2375 
2376  if (query_idx + 1 < objc)
2377  loop_body = objv[query_idx + 1];
2378 
2379  /************************************************************
2380  * Execute the query inside a sub-transaction, so we can cope with
2381  * errors sanely
2382  ************************************************************/
2383 
2384  pltcl_subtrans_begin(oldcontext, oldowner);
2385 
2386  PG_TRY();
2387  {
2388  UTF_BEGIN;
2389  spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
2391  UTF_END;
2392 
2393  my_rc = pltcl_process_SPI_result(interp,
2394  arrayname,
2395  loop_body,
2396  spi_rc,
2397  SPI_tuptable,
2398  SPI_processed);
2399 
2400  pltcl_subtrans_commit(oldcontext, oldowner);
2401  }
2402  PG_CATCH();
2403  {
2404  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2405  return TCL_ERROR;
2406  }
2407  PG_END_TRY();
2408 
2409  return my_rc;
2410 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2262
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2280
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
SPITupleTable * SPI_tuptable
Definition: spi.c:46
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
uint64 SPI_processed
Definition: spi.c:45
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
static int pltcl_process_SPI_result(Tcl_Interp *interp, const char *arrayname, Tcl_Obj *loop_body, int spi_rc, SPITupleTable *tuptable, uint64 ntuples)
Definition: pltcl.c:2418
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
bool fn_readonly
Definition: pltcl.c:143
#define UTF_U2E(x)
Definition: pltcl.c:95
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
int i
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:496

◆ pltcl_SPI_execute_plan()

static int pltcl_SPI_execute_plan ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2656 of file pltcl.c.

References pltcl_query_desc::arginfuncs, pltcl_query_desc::argtypioparams, CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, InputFunctionCall(), pltcl_proc_desc::interp_desc, pltcl_query_desc::nargs, palloc(), PG_CATCH, PG_END_TRY, PG_TRY, pltcl_query_desc::plan, pltcl_process_SPI_result(), pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), pltcl_call_state::prodesc, pltcl_interp_desc::query_hash, SPI_execute_plan(), SPI_processed, SPI_tuptable, UTF_BEGIN, UTF_END, and UTF_U2E.

Referenced by pltcl_init_interp().

2658 {
2659  int my_rc;
2660  int spi_rc;
2661  int i;
2662  int j;
2663  int optIndex;
2664  Tcl_HashEntry *hashent;
2665  pltcl_query_desc *qdesc;
2666  const char *nulls = NULL;
2667  const char *arrayname = NULL;
2668  Tcl_Obj *loop_body = NULL;
2669  int count = 0;
2670  int callObjc;
2671  Tcl_Obj **callObjv = NULL;
2672  Datum *argvalues;
2673  MemoryContext oldcontext = CurrentMemoryContext;
2675  Tcl_HashTable *query_hash;
2676 
2677  enum options
2678  {
2679  OPT_ARRAY, OPT_COUNT, OPT_NULLS
2680  };
2681 
2682  static const char *options[] = {
2683  "-array", "-count", "-nulls", (const char *) NULL
2684  };
2685 
2686  /************************************************************
2687  * Get the options and check syntax
2688  ************************************************************/
2689  i = 1;
2690  while (i < objc)
2691  {
2692  if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2693  TCL_EXACT, &optIndex) != TCL_OK)
2694  break;
2695 
2696  if (++i >= objc)
2697  {
2698  Tcl_SetObjResult(interp,
2699  Tcl_NewStringObj("missing argument to -array, -count or -nulls", -1));
2700  return TCL_ERROR;
2701  }
2702 
2703  switch ((enum options) optIndex)
2704  {
2705  case OPT_ARRAY:
2706  arrayname = Tcl_GetString(objv[i++]);
2707  break;
2708 
2709  case OPT_COUNT:
2710  if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2711  return TCL_ERROR;
2712  break;
2713 
2714  case OPT_NULLS:
2715  nulls = Tcl_GetString(objv[i++]);
2716  break;
2717  }
2718  }
2719 
2720  /************************************************************
2721  * Get the prepared plan descriptor by its key
2722  ************************************************************/
2723  if (i >= objc)
2724  {
2725  Tcl_SetObjResult(interp,
2726  Tcl_NewStringObj("missing argument to -count or -array", -1));
2727  return TCL_ERROR;
2728  }
2729 
2731 
2732  hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
2733  if (hashent == NULL)
2734  {
2735  Tcl_AppendResult(interp, "invalid queryid '", Tcl_GetString(objv[i]), "'", NULL);
2736  return TCL_ERROR;
2737  }
2738  qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
2739  i++;
2740 
2741  /************************************************************
2742  * If a nulls string is given, check for correct length
2743  ************************************************************/
2744  if (nulls != NULL)
2745  {
2746  if (strlen(nulls) != qdesc->nargs)
2747  {
2748  Tcl_SetObjResult(interp,
2749  Tcl_NewStringObj(
2750  "length of nulls string doesn't match number of arguments",
2751  -1));
2752  return TCL_ERROR;
2753  }
2754  }
2755 
2756  /************************************************************
2757  * If there was an argtype list on preparation, we need
2758  * an argument value list now
2759  ************************************************************/
2760  if (qdesc->nargs > 0)
2761  {
2762  if (i >= objc)
2763  {
2764  Tcl_SetObjResult(interp,
2765  Tcl_NewStringObj(
2766  "argument list length doesn't match number of arguments for query"
2767  ,-1));
2768  return TCL_ERROR;
2769  }
2770 
2771  /************************************************************
2772  * Split the argument values
2773  ************************************************************/
2774  if (Tcl_ListObjGetElements(interp, objv[i++], &callObjc, &callObjv) != TCL_OK)
2775  return TCL_ERROR;
2776 
2777  /************************************************************
2778  * Check that the number of arguments matches
2779  ************************************************************/
2780  if (callObjc != qdesc->nargs)
2781  {
2782  Tcl_SetObjResult(interp,
2783  Tcl_NewStringObj(
2784  "argument list length doesn't match number of arguments for query"
2785  ,-1));
2786  return TCL_ERROR;
2787  }
2788  }
2789  else
2790  callObjc = 0;
2791 
2792  /************************************************************
2793  * Get loop body if present
2794  ************************************************************/
2795  if (i < objc)
2796  loop_body = objv[i++];
2797 
2798  if (i != objc)
2799  {
2800  Tcl_WrongNumArgs(interp, 1, objv,
2801  "?-count n? ?-array name? ?-nulls string? "
2802  "query ?args? ?loop body?");
2803  return TCL_ERROR;
2804  }
2805 
2806  /************************************************************
2807  * Execute the plan inside a sub-transaction, so we can cope with
2808  * errors sanely
2809  ************************************************************/
2810 
2811  pltcl_subtrans_begin(oldcontext, oldowner);
2812 
2813  PG_TRY();
2814  {
2815  /************************************************************
2816  * Setup the value array for SPI_execute_plan() using
2817  * the type specific input functions
2818  ************************************************************/
2819  argvalues = (Datum *) palloc(callObjc * sizeof(Datum));
2820 
2821  for (j = 0; j < callObjc; j++)
2822  {
2823  if (nulls && nulls[j] == 'n')
2824  {
2825  argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2826  NULL,
2827  qdesc->argtypioparams[j],
2828  -1);
2829  }
2830  else
2831  {
2832  UTF_BEGIN;
2833  argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2834  UTF_U2E(Tcl_GetString(callObjv[j])),
2835  qdesc->argtypioparams[j],
2836  -1);
2837  UTF_END;
2838  }
2839  }
2840 
2841  /************************************************************
2842  * Execute the plan
2843  ************************************************************/
2844  spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
2846  count);
2847 
2848  my_rc = pltcl_process_SPI_result(interp,
2849  arrayname,
2850  loop_body,
2851  spi_rc,
2852  SPI_tuptable,
2853  SPI_processed);
2854 
2855  pltcl_subtrans_commit(oldcontext, oldowner);
2856  }
2857  PG_CATCH();
2858  {
2859  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2860  return TCL_ERROR;
2861  }
2862  PG_END_TRY();
2863 
2864  return my_rc;
2865 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2262
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2280
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
SPITupleTable * SPI_tuptable
Definition: spi.c:46
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
FmgrInfo * arginfuncs
Definition: pltcl.c:169
uint64 SPI_processed
Definition: spi.c:45
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition: spi.c:531
static int pltcl_process_SPI_result(Tcl_Interp *interp, const char *arrayname, Tcl_Obj *loop_body, int spi_rc, SPITupleTable *tuptable, uint64 ntuples)
Definition: pltcl.c:2418
SPIPlanPtr plan
Definition: pltcl.c:166
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
bool fn_readonly
Definition: pltcl.c:143
uintptr_t Datum
Definition: postgres.h:367
#define UTF_U2E(x)
Definition: pltcl.c:95
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1531
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
Oid * argtypioparams
Definition: pltcl.c:170
void * palloc(Size size)
Definition: mcxt.c:949
int i
#define PG_TRY()
Definition: elog.h:322
#define PG_END_TRY()
Definition: elog.h:347
Tcl_HashTable query_hash
Definition: pltcl.c:116

◆ pltcl_SPI_prepare()

static int pltcl_SPI_prepare ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2529 of file pltcl.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, pltcl_query_desc::arginfuncs, pltcl_query_desc::argtypes, pltcl_query_desc::argtypioparams, CurrentMemoryContext, CurrentResourceOwner, elog, ERROR, fmgr_info_cxt(), getTypeInputInfo(), i, pltcl_proc_desc::interp_desc, MemoryContextDelete(), MemoryContextSwitchTo(), pltcl_query_desc::nargs, palloc(), palloc0(), parseTypeString(), PG_CATCH, PG_END_TRY, PG_TRY, pltcl_query_desc::plan, pltcl_subtrans_abort(), pltcl_subtrans_begin(), pltcl_subtrans_commit(), pltcl_call_state::prodesc, pltcl_query_desc::qname, pltcl_interp_desc::query_hash, snprintf, SPI_keepplan(), SPI_prepare(), TopMemoryContext, UTF_BEGIN, UTF_END, and UTF_U2E.

Referenced by pltcl_init_interp().

2531 {
2532  volatile MemoryContext plan_cxt = NULL;
2533  int nargs;
2534  Tcl_Obj **argsObj;
2535  pltcl_query_desc *qdesc;
2536  int i;
2537  Tcl_HashEntry *hashent;
2538  int hashnew;
2539  Tcl_HashTable *query_hash;
2540  MemoryContext oldcontext = CurrentMemoryContext;
2542 
2543  /************************************************************
2544  * Check the call syntax
2545  ************************************************************/
2546  if (objc != 3)
2547  {
2548  Tcl_WrongNumArgs(interp, 1, objv, "query argtypes");
2549  return TCL_ERROR;
2550  }
2551 
2552  /************************************************************
2553  * Split the argument type list
2554  ************************************************************/
2555  if (Tcl_ListObjGetElements(interp, objv[2], &nargs, &argsObj) != TCL_OK)
2556  return TCL_ERROR;
2557 
2558  /************************************************************
2559  * Allocate the new querydesc structure
2560  *
2561  * struct qdesc and subsidiary data all live in plan_cxt. Note that if the
2562  * function is recompiled for whatever reason, permanent memory leaks
2563  * occur. FIXME someday.
2564  ************************************************************/
2566  "PL/Tcl spi_prepare query",
2568  MemoryContextSwitchTo(plan_cxt);
2569  qdesc = (pltcl_query_desc *) palloc0(sizeof(pltcl_query_desc));
2570  snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
2571  qdesc->nargs = nargs;
2572  qdesc->argtypes = (Oid *) palloc(nargs * sizeof(Oid));
2573  qdesc->arginfuncs = (FmgrInfo *) palloc(nargs * sizeof(FmgrInfo));
2574  qdesc->argtypioparams = (Oid *) palloc(nargs * sizeof(Oid));
2575  MemoryContextSwitchTo(oldcontext);
2576 
2577  /************************************************************
2578  * Execute the prepare inside a sub-transaction, so we can cope with
2579  * errors sanely
2580  ************************************************************/
2581 
2582  pltcl_subtrans_begin(oldcontext, oldowner);
2583 
2584  PG_TRY();
2585  {
2586  /************************************************************
2587  * Resolve argument type names and then look them up by oid
2588  * in the system cache, and remember the required information
2589  * for input conversion.
2590  ************************************************************/
2591  for (i = 0; i < nargs; i++)
2592  {
2593  Oid typId,
2594  typInput,
2595  typIOParam;
2596  int32 typmod;
2597 
2598  parseTypeString(Tcl_GetString(argsObj[i]), &typId, &typmod, false);
2599 
2600  getTypeInputInfo(typId, &typInput, &typIOParam);
2601 
2602  qdesc->argtypes[i] = typId;
2603  fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
2604  qdesc->argtypioparams[i] = typIOParam;
2605  }
2606 
2607  /************************************************************
2608  * Prepare the plan and check for errors
2609  ************************************************************/
2610  UTF_BEGIN;
2611  qdesc->plan = SPI_prepare(UTF_U2E(Tcl_GetString(objv[1])),
2612  nargs, qdesc->argtypes);
2613  UTF_END;
2614 
2615  if (qdesc->plan == NULL)
2616  elog(ERROR, "SPI_prepare() failed");
2617 
2618  /************************************************************
2619  * Save the plan into permanent memory (right now it's in the
2620  * SPI procCxt, which will go away at function end).
2621  ************************************************************/
2622  if (SPI_keepplan(qdesc->plan))
2623  elog(ERROR, "SPI_keepplan() failed");
2624 
2625  pltcl_subtrans_commit(oldcontext, oldowner);
2626  }
2627  PG_CATCH();
2628  {
2629  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2630 
2631  MemoryContextDelete(plan_cxt);
2632 
2633  return TCL_ERROR;
2634  }
2635  PG_END_TRY();
2636 
2637  /************************************************************
2638  * Insert a hashtable entry for the plan and return
2639  * the key to the caller
2640  ************************************************************/
2642 
2643  hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
2644  Tcl_SetHashValue(hashent, (ClientData) qdesc);
2645 
2646  /* qname is ASCII, so no need for encoding conversion */
2647  Tcl_SetObjResult(interp, Tcl_NewStringObj(qdesc->qname, -1));
2648  return TCL_OK;
2649 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2262
Definition: fmgr.h:56
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2280
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:674
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:202
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Oid * argtypes
Definition: pltcl.c:168
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
FmgrInfo * arginfuncs
Definition: pltcl.c:169
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:347
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:244
char qname[20]
Definition: pltcl.c:165
#define ERROR
Definition: elog.h:43
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
SPIPlanPtr plan
Definition: pltcl.c:166
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:752
#define UTF_END
Definition: pltcl.c:90
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:134
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2641
MemoryContext TopMemoryContext
Definition: mcxt.c:44
void * palloc0(Size size)
Definition: mcxt.c:980
#define UTF_U2E(x)
Definition: pltcl.c:95
void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok)
Definition: parse_type.c:832
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:332
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
Oid * argtypioparams
Definition: pltcl.c:170
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i
#define PG_TRY()
Definition: elog.h:322
#define snprintf
Definition: port.h:192
#define PG_END_TRY()
Definition: elog.h:347
Tcl_HashTable query_hash
Definition: pltcl.c:116

◆ pltcl_subtrans_abort()

static void pltcl_subtrans_abort ( Tcl_Interp *  interp,
MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2280 of file pltcl.c.

References CopyErrorData(), CurrentResourceOwner, FlushErrorState(), FreeErrorData(), MemoryContextSwitchTo(), ErrorData::message, pltcl_construct_errorCode(), RollbackAndReleaseCurrentSubTransaction(), UTF_BEGIN, UTF_E2U, and UTF_END.

Referenced by pltcl_returnnext(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

2282 {
2283  ErrorData *edata;
2284 
2285  /* Save error info */
2286  MemoryContextSwitchTo(oldcontext);
2287  edata = CopyErrorData();
2288  FlushErrorState();
2289 
2290  /* Abort the inner transaction */
2292  MemoryContextSwitchTo(oldcontext);
2293  CurrentResourceOwner = oldowner;
2294 
2295  /* Pass the error data to Tcl */
2296  pltcl_construct_errorCode(interp, edata);
2297  UTF_BEGIN;
2298  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
2299  UTF_END;
2300  FreeErrorData(edata);
2301 }
ErrorData * CopyErrorData(void)
Definition: elog.c:1584
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void FlushErrorState(void)
Definition: elog.c:1678
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1640
#define UTF_E2U(x)
Definition: pltcl.c:98
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
Definition: pltcl.c:1830
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4464
#define UTF_END
Definition: pltcl.c:90
#define UTF_BEGIN
Definition: pltcl.c:85
char * message
Definition: elog.h:392

◆ pltcl_subtrans_begin()

static void pltcl_subtrans_begin ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2262 of file pltcl.c.

References BeginInternalSubTransaction(), and MemoryContextSwitchTo().

Referenced by pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

2263 {
2265 
2266  /* Want to run inside function's memory context */
2267  MemoryContextSwitchTo(oldcontext);
2268 }
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4359

◆ pltcl_subtrans_commit()

static void pltcl_subtrans_commit ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2271 of file pltcl.c.

References CurrentResourceOwner, MemoryContextSwitchTo(), and ReleaseCurrentSubTransaction().

Referenced by pltcl_returnnext(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), and pltcl_SPI_prepare().

2272 {
2273  /* Commit the inner transaction, return to outer xact context */
2275  MemoryContextSwitchTo(oldcontext);
2276  CurrentResourceOwner = oldowner;
2277 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4430
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109

◆ pltcl_subtransaction()

static int pltcl_subtransaction ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2875 of file pltcl.c.

References BeginInternalSubTransaction(), CurrentMemoryContext, CurrentResourceOwner, MemoryContextSwitchTo(), ReleaseCurrentSubTransaction(), and RollbackAndReleaseCurrentSubTransaction().

Referenced by pltcl_init_interp().

2877 {
2878  MemoryContext oldcontext = CurrentMemoryContext;
2880  int retcode;
2881 
2882  if (objc != 2)
2883  {
2884  Tcl_WrongNumArgs(interp, 1, objv, "command");
2885  return TCL_ERROR;
2886  }
2887 
2888  /*
2889  * Note: we don't use pltcl_subtrans_begin and friends here because we
2890  * don't want the error handling in pltcl_subtrans_abort. But otherwise
2891  * the processing should be about the same as in those functions.
2892  */
2894  MemoryContextSwitchTo(oldcontext);
2895 
2896  retcode = Tcl_EvalObjEx(interp, objv[1], 0);
2897 
2898  if (retcode == TCL_ERROR)
2899  {
2900  /* Rollback the subtransaction */
2902  }
2903  else
2904  {
2905  /* Commit the subtransaction */
2907  }
2908 
2909  /* In either case, restore previous memory context and resource owner */
2910  MemoryContextSwitchTo(oldcontext);
2911  CurrentResourceOwner = oldowner;
2912 
2913  return retcode;
2914 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:142
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4430
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4464
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4359

◆ pltcl_trigger_handler()

static HeapTuple pltcl_trigger_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

Definition at line 1041 of file pltcl.c.

References Assert, compile_pltcl_function(), DatumGetCString, DirectFunctionCall1, elog, ereport, errcode(), errmsg(), ERROR, pltcl_proc_desc::fn_refcount, i, pltcl_proc_desc::internal_proname, pltcl_interp_desc::interp, pltcl_proc_desc::interp_desc, NameStr, TupleDescData::natts, ObjectIdGetDatum, oidout(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PG_USED_FOR_ASSERTS_ONLY, pltcl_build_tuple_argument(), pltcl_build_tuple_result(), pltcl_call_state::prodesc, RelationData::rd_id, RelationGetDescr, RelationGetRelid, SPI_connect(), SPI_finish(), SPI_getnspname(), SPI_getrelname(), SPI_OK_CONNECT, SPI_OK_FINISH, SPI_register_trigger_data(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgname, Trigger::tgnargs, throw_tcl_error(), pltcl_call_state::trigdata, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_TRUNCATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TRIGGER_FIRED_FOR_STATEMENT, TRIGGER_FIRED_INSTEAD, TupleDescAttr, pltcl_proc_desc::user_proname, utf_e2u(), and utf_u2e().

Referenced by pltcl_handler().

1043 {
1044  pltcl_proc_desc *prodesc;
1045  Tcl_Interp *volatile interp;
1046  TriggerData *trigdata = (TriggerData *) fcinfo->context;
1047  char *stroid;
1048  TupleDesc tupdesc;
1049  volatile HeapTuple rettup;
1050  Tcl_Obj *tcl_cmd;
1051  Tcl_Obj *tcl_trigtup;
1052  int tcl_rc;
1053  int i;
1054  const char *result;
1055  int result_Objc;
1056  Tcl_Obj **result_Objv;
1057  int rc PG_USED_FOR_ASSERTS_ONLY;
1058 
1059  call_state->trigdata = trigdata;
1060 
1061  /* Connect to SPI manager */
1062  if (SPI_connect() != SPI_OK_CONNECT)
1063  elog(ERROR, "could not connect to SPI manager");
1064 
1065  /* Make transition tables visible to this SPI connection */
1066  rc = SPI_register_trigger_data(trigdata);
1067  Assert(rc >= 0);
1068 
1069  /* Find or compile the function */
1070  prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1071  RelationGetRelid(trigdata->tg_relation),
1072  false, /* not an event trigger */
1073  pltrusted);
1074 
1075  call_state->prodesc = prodesc;
1076  prodesc->fn_refcount++;
1077 
1078  interp = prodesc->interp_desc->interp;
1079 
1080  tupdesc = RelationGetDescr(trigdata->tg_relation);
1081 
1082  /************************************************************
1083  * Create the tcl command to call the internal
1084  * proc in the interpreter
1085  ************************************************************/
1086  tcl_cmd = Tcl_NewObj();
1087  Tcl_IncrRefCount(tcl_cmd);
1088 
1089  PG_TRY();
1090  {
1091  /* The procedure name (note this is all ASCII, so no utf_e2u) */
1092  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1093  Tcl_NewStringObj(prodesc->internal_proname, -1));
1094 
1095  /* The trigger name for argument TG_name */
1096  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1097  Tcl_NewStringObj(utf_e2u(trigdata->tg_trigger->tgname), -1));
1098 
1099  /* The oid of the trigger relation for argument TG_relid */
1100  /* Consider not converting to a string for more performance? */
1102  ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
1103  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1104  Tcl_NewStringObj(stroid, -1));
1105  pfree(stroid);
1106 
1107  /* The name of the table the trigger is acting on: TG_table_name */
1108  stroid = SPI_getrelname(trigdata->tg_relation);
1109  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1110  Tcl_NewStringObj(utf_e2u(stroid), -1));
1111  pfree(stroid);
1112 
1113  /* The schema of the table the trigger is acting on: TG_table_schema */
1114  stroid = SPI_getnspname(trigdata->tg_relation);
1115  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1116  Tcl_NewStringObj(utf_e2u(stroid), -1));
1117  pfree(stroid);
1118 
1119  /* A list of attribute names for argument TG_relatts */
1120  tcl_trigtup = Tcl_NewObj();
1121  Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
1122  for (i = 0; i < tupdesc->natts; i++)
1123  {
1124  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1125 
1126  if (att->attisdropped)
1127  Tcl_ListObjAppendElement(NULL, tcl_trigtup, Tcl_NewObj());
1128  else
1129  Tcl_ListObjAppendElement(NULL, tcl_trigtup,
1130  Tcl_NewStringObj(utf_e2u(NameStr(att->attname)), -1));
1131  }
1132  Tcl_ListObjAppendElement(NULL, tcl_cmd, tcl_trigtup);
1133 
1134  /* The when part of the event for TG_when */
1135  if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
1136  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1137  Tcl_NewStringObj("BEFORE", -1));
1138  else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
1139  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1140  Tcl_NewStringObj("AFTER", -1));
1141  else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
1142  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1143  Tcl_NewStringObj("INSTEAD OF", -1));
1144  else
1145  elog(ERROR, "unrecognized WHEN tg_event: %u", trigdata->tg_event);
1146 
1147  /* The level part of the event for TG_level */
1148  if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
1149  {
1150  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1151  Tcl_NewStringObj("ROW", -1));
1152 
1153  /*
1154  * Now the command part of the event for TG_op and data for NEW
1155  * and OLD
1156  *
1157  * Note: In BEFORE trigger, stored generated columns are not
1158  * computed yet, so don't make them accessible in NEW row.
1159  */
1160  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
1161  {
1162  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1163  Tcl_NewStringObj("INSERT", -1));
1164 
1165  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1167  tupdesc,
1168  !TRIGGER_FIRED_BEFORE(trigdata->tg_event)));
1169  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
1170 
1171  rettup = trigdata->tg_trigtuple;
1172  }
1173  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
1174  {
1175  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1176  Tcl_NewStringObj("DELETE", -1));
1177 
1178  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
1179  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1181  tupdesc,
1182  true));
1183 
1184  rettup = trigdata->tg_trigtuple;
1185  }
1186  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
1187  {
1188  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1189  Tcl_NewStringObj("UPDATE", -1));
1190 
1191  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1193  tupdesc,
1194  !TRIGGER_FIRED_BEFORE(trigdata->tg_event)));
1195  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1197  tupdesc,
1198  true));
1199 
1200  rettup = trigdata->tg_newtuple;
1201  }
1202  else
1203  elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
1204  }
1205  else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
1206  {
1207  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1208  Tcl_NewStringObj("STATEMENT", -1));
1209 
1210  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
1211  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1212  Tcl_NewStringObj("INSERT", -1));
1213  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
1214  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1215  Tcl_NewStringObj("DELETE", -1));
1216  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
1217  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1218  Tcl_NewStringObj("UPDATE", -1));
1219  else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
1220  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1221  Tcl_NewStringObj("TRUNCATE", -1));
1222  else
1223  elog(ERROR, "unrecognized OP tg_event: %u", trigdata->tg_event);
1224 
1225  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
1226  Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
1227 
1228  rettup = (HeapTuple) NULL;
1229  }
1230  else
1231  elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
1232 
1233  /* Finally append the arguments from CREATE TRIGGER */
1234  for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
1235  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1236  Tcl_NewStringObj(utf_e2u(trigdata->tg_trigger->tgargs[i]), -1));
1237 
1238  }
1239  PG_CATCH();
1240  {
1241  Tcl_DecrRefCount(tcl_cmd);
1242  PG_RE_THROW();
1243  }
1244  PG_END_TRY();
1245 
1246  /************************************************************
1247  * Call the Tcl function
1248  *
1249  * We assume no PG error can be thrown directly from this call.
1250  ************************************************************/
1251  tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
1252 
1253  /* Release refcount to free tcl_cmd (and all subsidiary objects) */
1254  Tcl_DecrRefCount(tcl_cmd);
1255 
1256  /************************************************************
1257  * Check for errors reported by Tcl.
1258  ************************************************************/
1259  if (tcl_rc != TCL_OK)
1260  throw_tcl_error(interp, prodesc->user_proname);
1261 
1262  /************************************************************
1263  * Exit SPI environment.
1264  ************************************************************/
1265  if (SPI_finish() != SPI_OK_FINISH)
1266  elog(ERROR, "SPI_finish() failed");
1267 
1268  /************************************************************
1269  * The return value from the procedure might be one of
1270  * the magic strings OK or SKIP, or a list from array get.
1271  * We can check for OK or SKIP without worrying about encoding.
1272  ************************************************************/
1273  result = Tcl_GetStringResult(interp);
1274 
1275  if (strcmp(result, "OK") == 0)
1276  return rettup;
1277  if (strcmp(result, "SKIP") == 0)
1278  return (HeapTuple) NULL;
1279 
1280  /************************************************************
1281  * Otherwise, the return value should be a column name/value list
1282  * specifying the modified tuple to return.
1283  ************************************************************/
1284  if (Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp),
1285  &result_Objc, &result_Objv) != TCL_OK)
1286  ereport(ERROR,
1287  (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
1288  errmsg("could not split return value from trigger: %s",
1289  utf_u2e(Tcl_GetStringResult(interp)))));
1290 
1291  /* Convert function result to tuple */
1292  rettup = pltcl_build_tuple_result(interp, result_Objv, result_Objc,
1293  call_state);
1294 
1295  return rettup;
1296 }
#define SPI_OK_CONNECT
Definition: spi.h:53
HeapTupleData * HeapTuple
Definition: htup.h:71
#define RelationGetDescr(relation)
Definition: rel.h:448
int SPI_connect(void)
Definition: spi.c:89
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int SPI_finish(void)
Definition: spi.c:176
char * user_proname
Definition: pltcl.c:137
int errcode(int sqlerrcode)
Definition: elog.c:608
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:615
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:137
Datum oidout(PG_FUNCTION_ARGS)
Definition: oid.c:127
static void throw_tcl_error(Tcl_Interp *interp, const char *proname)
Definition: pltcl.c:1352
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:131
HeapTuple tg_trigtuple
Definition: trigger.h:35
void pfree(void *pointer)
Definition: mcxt.c:1056
#define TRIGGER_FIRED_BY_TRUNCATE(event)
Definition: trigger.h:125
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
#define DatumGetCString(X)
Definition: postgres.h:566
char * tgname
Definition: reltrigger.h:27
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
unsigned long fn_refcount
Definition: pltcl.c:140
Tcl_Interp * interp
Definition: pltcl.c:115
#define ereport(elevel, rest)
Definition: elog.h:141
int SPI_register_trigger_data(TriggerData *tdata)
Definition: spi.c:2934
Oid rd_id
Definition: rel.h:85
char ** tgargs
Definition: reltrigger.h:40
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:119
static pltcl_proc_desc * compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
Definition: pltcl.c:1381
static char * utf_e2u(const char *src)
Definition: pltcl.c:80
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
char * SPI_getrelname(Relation rel)
Definition: spi.c:1102
static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
Definition: pltcl.c:3166
char * internal_proname
Definition: pltcl.c:138
#define PG_CATCH()
Definition: elog.h:332
#define Assert(condition)
Definition: c.h:739
pltcl_proc_desc * prodesc
Definition: pltcl.c:216
TriggerEvent tg_event
Definition: trigger.h:33
char * SPI_getnspname(Relation rel)
Definition: spi.c:1108
#define SPI_OK_FINISH
Definition: spi.h:54
#define PG_RE_THROW()
Definition: elog.h:363
#define TRIGGER_FIRED_BEFORE(event)
Definition: trigger.h:134
#define TRIGGER_FIRED_INSTEAD(event)
Definition: trigger.h:140
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:116
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
int i
int16 tgnargs
Definition: reltrigger.h:37
#define NameStr(name)
Definition: c.h:616
TriggerData * trigdata
Definition: pltcl.c:213
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:123
#define PG_TRY()
Definition: elog.h:322
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:128
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
#define RelationGetRelid(relation)
Definition: rel.h:422
#define PG_END_TRY()
Definition: elog.h:347
static Tcl_Obj * pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
Definition: pltcl.c:3090
Relation tg_relation
Definition: trigger.h:34

◆ pltcl_WaitForEvent()

static int pltcl_WaitForEvent ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 385 of file pltcl.c.

Referenced by _PG_init().

386 {
387  return 0;
388 }

◆ pltclu_call_handler()

Datum pltclu_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 707 of file pltcl.c.

References pltcl_handler().

Referenced by pltcl_call_handler().

708 {
709  return pltcl_handler(fcinfo, false);
710 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:718

◆ start_proc_error_callback()

static void start_proc_error_callback ( void *  arg)
static

Definition at line 675 of file pltcl.c.

References errcontext, PG_FUNCTION_INFO_V1(), and pltcl_call_handler().

Referenced by call_pltcl_start_proc().

676 {
677  const char *gucname = (const char *) arg;
678 
679  /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
680  errcontext("processing %s parameter", gucname);
681 }
#define errcontext
Definition: elog.h:183
void * arg

◆ throw_tcl_error()

static void throw_tcl_error ( Tcl_Interp *  interp,
const char *  proname 
)
static

Definition at line 1352 of file pltcl.c.

References ereport, errcode(), errcontext, errmsg(), ERROR, pstrdup(), and utf_u2e().

Referenced by pltcl_event_trigger_handler(), pltcl_func_handler(), and pltcl_trigger_handler().

1353 {
1354  /*
1355  * Caution is needed here because Tcl_GetVar could overwrite the
1356  * interpreter result (even though it's not really supposed to), and we
1357  * can't control the order of evaluation of ereport arguments. Hence, make
1358  * real sure we have our own copy of the result string before invoking
1359  * Tcl_GetVar.
1360  */
1361  char *emsg;
1362  char *econtext;
1363 
1364  emsg = pstrdup(utf_u2e(Tcl_GetStringResult(interp)));
1365  econtext = utf_u2e(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
1366  ereport(ERROR,
1367  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1368  errmsg("%s", emsg),
1369  errcontext("%s\nin PL/Tcl function \"%s\"",
1370  econtext, proname)));
1371 }
NameData proname
Definition: pg_proc.h:35
char * pstrdup(const char *in)
Definition: mcxt.c:1186
int errcode(int sqlerrcode)
Definition: elog.c:608
#define ERROR
Definition: elog.h:43
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
#define ereport(elevel, rest)
Definition: elog.h:141
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define errcontext
Definition: elog.h:183

◆ utf_e2u()

static char* utf_e2u ( const char *  src)
inlinestatic

Definition at line 80 of file pltcl.c.

References pg_server_to_any(), and PG_UTF8.

Referenced by pltcl_event_trigger_handler(), and pltcl_trigger_handler().

81 {
82  return pg_server_to_any(src, strlen(src), PG_UTF8);
83 }
char * pg_server_to_any(const char *s, int len, int encoding)
Definition: mbutils.c:654

◆ utf_u2e()

static char* utf_u2e ( const char *  src)
inlinestatic

Definition at line 74 of file pltcl.c.

References pg_any_to_server(), and PG_UTF8.

Referenced by compile_pltcl_function(), pltcl_build_tuple_result(), pltcl_func_handler(), pltcl_returnnext(), pltcl_trigger_handler(), and throw_tcl_error().

75 {
76  return pg_any_to_server(src, strlen(src), PG_UTF8);
77 }
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:581

Variable Documentation

◆ exception_name_map

const TclExceptionNameMap exception_name_map[]
static
Initial value:
= {
{NULL, 0}
}

Definition at line 255 of file pltcl.c.

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 42 of file pltcl.c.

◆ pltcl_current_call_state

pltcl_call_state* pltcl_current_call_state = NULL
static

Definition at line 244 of file pltcl.c.

Referenced by pltcl_handler(), and pltcl_returnnext().

◆ pltcl_hold_interp

Tcl_Interp* pltcl_hold_interp = NULL
static

Definition at line 239 of file pltcl.c.

Referenced by _PG_init(), and pltcl_init_interp().

◆ pltcl_interp_htab

HTAB* pltcl_interp_htab = NULL
static

Definition at line 240 of file pltcl.c.

◆ pltcl_pm_init_done

bool pltcl_pm_init_done = false
static

Definition at line 238 of file pltcl.c.

Referenced by _PG_init().

◆ pltcl_proc_htab

HTAB* pltcl_proc_htab = NULL
static

Definition at line 241 of file pltcl.c.

◆ pltcl_start_proc

char* pltcl_start_proc = NULL
static

Definition at line 236 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().

◆ pltclu_start_proc

char* pltclu_start_proc = NULL
static

Definition at line 237 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().