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_SPI_lastoid (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 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)
 
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

#define UTF_BEGIN

◆ UTF_E2U

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

◆ UTF_END

#define UTF_END
Value:
if (_pltcl_utf_src != (const char *) _pltcl_utf_dst) \
pfree(_pltcl_utf_dst); \
} while (0)

Definition at line 90 of file pltcl.c.

Referenced by compile_pltcl_function(), pltcl_build_tuple_argument(), pltcl_construct_errorCode(), pltcl_elog(), pltcl_func_handler(), pltcl_set_tuple_values(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_prepare(), and pltcl_subtrans_abort().

◆ 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 399 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.

400 {
401  Tcl_NotifierProcs notifier;
402  HASHCTL hash_ctl;
403 
404  /* Be sure we do initialization only once (should be redundant now) */
405  if (pltcl_pm_init_done)
406  return;
407 
409 
410 #ifdef WIN32
411  /* Required on win32 to prevent error loading init.tcl */
412  Tcl_FindExecutable("");
413 #endif
414 
415  /*
416  * Override the functions in the Notifier subsystem. See comments above.
417  */
418  notifier.setTimerProc = pltcl_SetTimer;
419  notifier.waitForEventProc = pltcl_WaitForEvent;
420  notifier.createFileHandlerProc = pltcl_CreateFileHandler;
421  notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
422  notifier.initNotifierProc = pltcl_InitNotifier;
423  notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
424  notifier.alertNotifierProc = pltcl_AlertNotifier;
425  notifier.serviceModeHookProc = pltcl_ServiceModeHook;
426  Tcl_SetNotifier(&notifier);
427 
428  /************************************************************
429  * Create the dummy hold interpreter to prevent close of
430  * stdout and stderr on DeleteInterp
431  ************************************************************/
432  if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
433  elog(ERROR, "could not create master Tcl interpreter");
434  if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
435  elog(ERROR, "could not initialize master Tcl interpreter");
436 
437  /************************************************************
438  * Create the hash table for working interpreters
439  ************************************************************/
440  memset(&hash_ctl, 0, sizeof(hash_ctl));
441  hash_ctl.keysize = sizeof(Oid);
442  hash_ctl.entrysize = sizeof(pltcl_interp_desc);
443  pltcl_interp_htab = hash_create("PL/Tcl interpreters",
444  8,
445  &hash_ctl,
447 
448  /************************************************************
449  * Create the hash table for function lookup
450  ************************************************************/
451  memset(&hash_ctl, 0, sizeof(hash_ctl));
452  hash_ctl.keysize = sizeof(pltcl_proc_key);
453  hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
454  pltcl_proc_htab = hash_create("PL/Tcl functions",
455  100,
456  &hash_ctl,
458 
459  /************************************************************
460  * Define PL/Tcl's custom GUCs
461  ************************************************************/
462  DefineCustomStringVariable("pltcl.start_proc",
463  gettext_noop("PL/Tcl function to call once when pltcl is first used."),
464  NULL,
466  NULL,
467  PGC_SUSET, 0,
468  NULL, NULL, NULL);
469  DefineCustomStringVariable("pltclu.start_proc",
470  gettext_noop("PL/TclU function to call once when pltclu is first used."),
471  NULL,
473  NULL,
474  PGC_SUSET, 0,
475  NULL, NULL, NULL);
476 
477  pltcl_pm_init_done = true;
478 }
#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:374
Size entrysize
Definition: hsearch.h:73
#define gettext_noop(x)
Definition: c.h:981
static void pltcl_ServiceModeHook(int mode)
Definition: pltcl.c:379
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:368
static void pltcl_AlertNotifier(ClientData clientData)
Definition: pltcl.c:363
static bool pltcl_pm_init_done
Definition: pltcl.c:239
static char * pltcl_start_proc
Definition: pltcl.c:237
static int pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:384
#define ERROR
Definition: elog.h:43
static ClientData pltcl_InitNotifier(void)
Definition: pltcl.c:345
Definition: guc.h:75
static void pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:358
static Tcl_Interp * pltcl_hold_interp
Definition: pltcl.c:240
#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:7842
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:242
static void pltcl_FinalizeNotifier(ClientData clientData)
Definition: pltcl.c:353
void pg_bindtextdomain(const char *domain)
Definition: miscinit.c:1511
static HTAB * pltcl_interp_htab
Definition: pltcl.c:241
static char * pltclu_start_proc
Definition: pltcl.c:238
#define elog
Definition: elog.h:219

◆ call_pltcl_start_proc()

static void call_pltcl_start_proc ( Oid  prolang,
bool  pltrusted 
)
static

Definition at line 585 of file pltcl.c.

References ACL_EXECUTE, ACL_KIND_PROC, 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, LookupFuncName(), 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(), and stringToQualifiedNameList().

Referenced by pltcl_init_interp().

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

◆ 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 1379 of file pltcl.c.

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate(), Anum_pg_proc_prosrc, pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, Assert, buf, pltcl_proc_desc::domain_info, elog, ereport, errcode(), errmsg(), ERROR, EVTTRIGGEROID, fmgr_info_cxt(), pltcl_proc_desc::fn_cxt, pltcl_proc_desc::fn_is_procedure, 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(), 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, PROVOLATILE_VOLATILE, pstrdup(), RECORDOID, 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, TRIGGEROID, type_is_rowtype(), TYPEOID, TYPTYPE_DOMAIN, TYPTYPE_PSEUDO, pltcl_proc_key::user_id, pltcl_proc_desc::user_proname, UTF_BEGIN, UTF_E2U, UTF_END, utf_u2e(), and VOIDOID.

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

1381 {
1382  HeapTuple procTup;
1383  Form_pg_proc procStruct;
1384  pltcl_proc_key proc_key;
1385  pltcl_proc_ptr *proc_ptr;
1386  bool found;
1387  pltcl_proc_desc *prodesc;
1388  pltcl_proc_desc *old_prodesc;
1389  volatile MemoryContext proc_cxt = NULL;
1390  Tcl_DString proc_internal_def;
1391  Tcl_DString proc_internal_body;
1392 
1393  /* We'll need the pg_proc tuple in any case... */
1394  procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
1395  if (!HeapTupleIsValid(procTup))
1396  elog(ERROR, "cache lookup failed for function %u", fn_oid);
1397  procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1398 
1399  /*
1400  * Look up function in pltcl_proc_htab; if it's not there, create an entry
1401  * and set the entry's proc_ptr to NULL.
1402  */
1403  proc_key.proc_id = fn_oid;
1404  proc_key.is_trigger = OidIsValid(tgreloid);
1405  proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
1406 
1407  proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
1408  HASH_ENTER,
1409  &found);
1410  if (!found)
1411  proc_ptr->proc_ptr = NULL;
1412 
1413  prodesc = proc_ptr->proc_ptr;
1414 
1415  /************************************************************
1416  * If it's present, must check whether it's still up to date.
1417  * This is needed because CREATE OR REPLACE FUNCTION can modify the
1418  * function's pg_proc entry without changing its OID.
1419  ************************************************************/
1420  if (prodesc != NULL &&
1421  prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
1422  ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
1423  {
1424  /* It's still up-to-date, so we can use it */
1425  ReleaseSysCache(procTup);
1426  return prodesc;
1427  }
1428 
1429  /************************************************************
1430  * If we haven't found it in the hashtable, we analyze
1431  * the functions arguments and returntype and store
1432  * the in-/out-functions in the prodesc block and create
1433  * a new hashtable entry for it.
1434  *
1435  * Then we load the procedure into the Tcl interpreter.
1436  ************************************************************/
1437  Tcl_DStringInit(&proc_internal_def);
1438  Tcl_DStringInit(&proc_internal_body);
1439  PG_TRY();
1440  {
1441  bool is_trigger = OidIsValid(tgreloid);
1442  char internal_proname[128];
1443  HeapTuple typeTup;
1444  Form_pg_type typeStruct;
1445  char proc_internal_args[33 * FUNC_MAX_ARGS];
1446  Datum prosrcdatum;
1447  bool isnull;
1448  char *proc_source;
1449  char buf[32];
1450  Tcl_Interp *interp;
1451  int i;
1452  int tcl_rc;
1453  MemoryContext oldcontext;
1454 
1455  /************************************************************
1456  * Build our internal proc name from the function's Oid. Append
1457  * "_trigger" when appropriate to ensure the normal and trigger
1458  * cases are kept separate. Note name must be all-ASCII.
1459  ************************************************************/
1460  if (is_event_trigger)
1461  snprintf(internal_proname, sizeof(internal_proname),
1462  "__PLTcl_proc_%u_evttrigger", fn_oid);
1463  else if (is_trigger)
1464  snprintf(internal_proname, sizeof(internal_proname),
1465  "__PLTcl_proc_%u_trigger", fn_oid);
1466  else
1467  snprintf(internal_proname, sizeof(internal_proname),
1468  "__PLTcl_proc_%u", fn_oid);
1469 
1470  /************************************************************
1471  * Allocate a context that will hold all PG data for the procedure.
1472  * We use the internal proc name as the context name.
1473  ************************************************************/
1475  internal_proname,
1477 
1478  /************************************************************
1479  * Allocate and fill a new procedure description block.
1480  * struct prodesc and subsidiary data must all live in proc_cxt.
1481  ************************************************************/
1482  oldcontext = MemoryContextSwitchTo(proc_cxt);
1483  prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
1484  prodesc->user_proname = pstrdup(NameStr(procStruct->proname));
1485  prodesc->internal_proname = pstrdup(internal_proname);
1486  prodesc->fn_cxt = proc_cxt;
1487  prodesc->fn_refcount = 0;
1488  prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
1489  prodesc->fn_tid = procTup->t_self;
1490  prodesc->nargs = procStruct->pronargs;
1491  prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
1492  prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
1493  MemoryContextSwitchTo(oldcontext);
1494 
1495  /* Remember if function is STABLE/IMMUTABLE */
1496  prodesc->fn_readonly =
1497  (procStruct->provolatile != PROVOLATILE_VOLATILE);
1498  /* And whether it is trusted */
1499  prodesc->lanpltrusted = pltrusted;
1500 
1501  /************************************************************
1502  * Identify the interpreter to use for the function
1503  ************************************************************/
1504  prodesc->interp_desc = pltcl_fetch_interp(procStruct->prolang,
1505  prodesc->lanpltrusted);
1506  interp = prodesc->interp_desc->interp;
1507 
1508  /************************************************************
1509  * Get the required information for input conversion of the
1510  * return value.
1511  ************************************************************/
1512  prodesc->fn_is_procedure = (procStruct->prorettype == InvalidOid);
1513 
1514  if (!is_trigger && !is_event_trigger && procStruct->prorettype)
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_procname> 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
#define TYPTYPE_DOMAIN
Definition: pg_type.h:722
Definition: fmgr.h:56
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
bool fn_retistuple
Definition: pltcl.c:151
#define GETSTRUCT(TUP)
Definition: htup_details.h:661
Oid GetUserId(void)
Definition: miscinit.c:284
FmgrInfo result_in_func
Definition: pltcl.c:147
char * pstrdup(const char *in)
Definition: mcxt.c:1076
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:180
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
char * user_proname
Definition: pltcl.c:137
void * domain_info
Definition: pltcl.c:153
#define Anum_pg_proc_prosrc
Definition: pg_proc.h:115
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
Oid proc_id
Definition: pltcl.c:188
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:902
FormData_pg_type * Form_pg_type
Definition: pg_type.h:233
unsigned int Oid
Definition: postgres_ext.h:31
bool fn_is_procedure
Definition: pltcl.c:149
#define OidIsValid(objectId)
Definition: c.h:576
bool fn_retisdomain
Definition: pltcl.c:152
HeapTupleHeader t_data
Definition: htup.h:67
#define FUNC_MAX_ARGS
void pfree(void *pointer)
Definition: mcxt.c:949
#define VOIDOID
Definition: pg_type.h:690
#define UTF_E2U(x)
Definition: pltcl.c:98
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
FmgrInfo * arg_out_func
Definition: pltcl.c:156
#define TRIGGEROID
Definition: pg_type.h:692
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
#define RECORDOID
Definition: pg_type.h:680
unsigned long fn_refcount
Definition: pltcl.c:140
Tcl_Interp * interp
Definition: pltcl.c:115
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2424
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:132
#define ereport(elevel, rest)
Definition: elog.h:122
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define PROVOLATILE_VOLATILE
Definition: pg_proc.h:5539
bool fn_readonly
Definition: pltcl.c:143
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
#define TextDatumGetCString(d)
Definition: builtins.h:92
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:342
void * palloc0(Size size)
Definition: mcxt.c:877
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
Oid result_typioparam
Definition: pltcl.c:148
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:83
#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:293
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define EVTTRIGGEROID
Definition: pg_type.h:694
#define Assert(condition)
Definition: c.h:670
pltcl_proc_desc * proc_ptr
Definition: pltcl.c:201
Oid is_trigger
Definition: pltcl.c:194
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:307
#define PG_RE_THROW()
Definition: elog.h:314
static pltcl_interp_desc * pltcl_fetch_interp(Oid prolang, bool pltrusted)
Definition: pltcl.c:555
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define TYPTYPE_PSEUDO
Definition: pg_type.h:724
TransactionId fn_xmin
Definition: pltcl.c:141
static HTAB * pltcl_proc_htab
Definition: pltcl.c:242
Oid result_typid
Definition: pltcl.c:146
MemoryContext fn_cxt
Definition: pltcl.c:139
bool fn_retisset
Definition: pltcl.c:150
Oid user_id
Definition: pltcl.c:195
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool * arg_is_rowtype
Definition: pltcl.c:157
int i
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2072
#define NameStr(name)
Definition: c.h:547
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300

◆ 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 363 of file pltcl.c.

Referenced by _PG_init().

364 {
365 }

◆ 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, FunctionCallInfoData::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:211
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:245
#define PG_ARGISNULL(n)
Definition: fmgr.h:174

◆ pltcl_build_tuple_argument()

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

Definition at line 3031 of file pltcl.c.

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

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

3032 {
3033  Tcl_Obj *retobj = Tcl_NewObj();
3034  int i;
3035  char *outputstr;
3036  Datum attr;
3037  bool isnull;
3038  char *attname;
3039  Oid typoutput;
3040  bool typisvarlena;
3041 
3042  for (i = 0; i < tupdesc->natts; i++)
3043  {
3044  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3045 
3046  /* ignore dropped attributes */
3047  if (att->attisdropped)
3048  continue;
3049 
3050  /************************************************************
3051  * Get the attribute name
3052  ************************************************************/
3053  attname = NameStr(att->attname);
3054 
3055  /************************************************************
3056  * Get the attributes value
3057  ************************************************************/
3058  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3059 
3060  /************************************************************
3061  * If there is a value, append the attribute name and the
3062  * value to the list
3063  *
3064  * Hmmm - Null attributes will cause functions to
3065  * crash if they don't expect them - need something
3066  * smarter here.
3067  ************************************************************/
3068  if (!isnull)
3069  {
3070  getTypeOutputInfo(att->atttypid,
3071  &typoutput, &typisvarlena);
3072  outputstr = OidOutputFunctionCall(typoutput, attr);
3073  UTF_BEGIN;
3074  Tcl_ListObjAppendElement(NULL, retobj,
3075  Tcl_NewStringObj(UTF_E2U(attname), -1));
3076  UTF_END;
3077  UTF_BEGIN;
3078  Tcl_ListObjAppendElement(NULL, retobj,
3079  Tcl_NewStringObj(UTF_E2U(outputstr), -1));
3080  UTF_END;
3081  pfree(outputstr);
3082  }
3083  }
3084 
3085  return retobj;
3086 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2665
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:79
void pfree(void *pointer)
Definition: mcxt.c:949
#define UTF_E2U(x)
Definition: pltcl.c:98
#define UTF_END
Definition: pltcl.c:90
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:774
uintptr_t Datum
Definition: postgres.h:372
#define UTF_BEGIN
Definition: pltcl.c:85
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1742
int i
#define NameStr(name)
Definition: c.h:547

◆ 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 3100 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, tupleDesc::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, TupleDescGetAttInMetadata(), utf_u2e(), and values.

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

3102 {
3103  HeapTuple tuple;
3104  TupleDesc tupdesc;
3105  AttInMetadata *attinmeta;
3106  char **values;
3107  int i;
3108 
3109  if (call_state->ret_tupdesc)
3110  {
3111  tupdesc = call_state->ret_tupdesc;
3112  attinmeta = call_state->attinmeta;
3113  }
3114  else if (call_state->trigdata)
3115  {
3116  tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
3117  attinmeta = TupleDescGetAttInMetadata(tupdesc);
3118  }
3119  else
3120  {
3121  elog(ERROR, "PL/Tcl function does not return a tuple");
3122  tupdesc = NULL; /* keep compiler quiet */
3123  attinmeta = NULL;
3124  }
3125 
3126  values = (char **) palloc0(tupdesc->natts * sizeof(char *));
3127 
3128  if (kvObjc % 2 != 0)
3129  ereport(ERROR,
3130  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3131  errmsg("column name/value list must have even number of elements")));
3132 
3133  for (i = 0; i < kvObjc; i += 2)
3134  {
3135  char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
3136  int attn = SPI_fnumber(tupdesc, fieldName);
3137 
3138  /*
3139  * We silently ignore ".tupno", if it's present but doesn't match any
3140  * actual output column. This allows direct use of a row returned by
3141  * pltcl_set_tuple_values().
3142  */
3143  if (attn == SPI_ERROR_NOATTRIBUTE)
3144  {
3145  if (strcmp(fieldName, ".tupno") == 0)
3146  continue;
3147  ereport(ERROR,
3148  (errcode(ERRCODE_UNDEFINED_COLUMN),
3149  errmsg("column name/value list contains nonexistent column name \"%s\"",
3150  fieldName)));
3151  }
3152 
3153  if (attn <= 0)
3154  ereport(ERROR,
3155  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3156  errmsg("cannot set system attribute \"%s\"",
3157  fieldName)));
3158 
3159  values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
3160  }
3161 
3162  tuple = BuildTupleFromCStrings(attinmeta, values);
3163 
3164  /* if result type is domain-over-composite, check domain constraints */
3165  if (call_state->prodesc->fn_retisdomain)
3166  domain_check(HeapTupleGetDatum(tuple), false,
3167  call_state->prodesc->result_typid,
3168  &call_state->prodesc->domain_info,
3169  call_state->prodesc->fn_cxt);
3170 
3171  return tuple;
3172 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:767
#define RelationGetDescr(relation)
Definition: rel.h:437
void * domain_info
Definition: pltcl.c:153
int errcode(int sqlerrcode)
Definition: elog.c:575
int natts
Definition: tupdesc.h:79
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:1118
bool fn_retisdomain
Definition: pltcl.c:152
AttInMetadata * attinmeta
Definition: pltcl.c:225
#define ERROR
Definition: elog.h:43
#define SPI_ERROR_NOATTRIBUTE
Definition: spi.h:44
static char * utf_u2e(const char *src)
Definition: pltcl.c:74
#define ereport(elevel, rest)
Definition: elog.h:122
TupleDesc ret_tupdesc
Definition: pltcl.c:224
void * palloc0(Size size)
Definition: mcxt.c:877
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:327
pltcl_proc_desc * prodesc
Definition: pltcl.c:217
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:230
Oid result_typid
Definition: pltcl.c:146
static Datum values[MAXATTR]
Definition: bootstrap.c:164
MemoryContext fn_cxt
Definition: pltcl.c:139
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define elog
Definition: elog.h:219
TriggerData * trigdata
Definition: pltcl.c:214
Relation tg_relation
Definition: trigger.h:34

◆ pltcl_call_handler()

Datum pltcl_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 693 of file pltcl.c.

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

Referenced by start_proc_error_callback().

694 {
695  return pltcl_handler(fcinfo, true);
696 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:716

◆ 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_elog(), 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:349
int sqlerrcode
Definition: elog.h:342
const char * funcname
Definition: elog.h:339
char * unpack_sql_state(int sql_state)
Definition: elog.c:2849
int lineno
Definition: elog.h:338
static const char * pltcl_get_condition_name(int sqlstate)
Definition: pltcl.c:1975
char * internalquery
Definition: elog.h:356
#define UTF_E2U(x)
Definition: pltcl.c:98
const char * filename
Definition: elog.h:337
char * table_name
Definition: elog.h:350
#define UTF_END
Definition: pltcl.c:90
int internalpos
Definition: elog.h:355
char * datatype_name
Definition: elog.h:352
char * detail
Definition: elog.h:344
char * column_name
Definition: elog.h:351
#define UTF_BEGIN
Definition: pltcl.c:85
char * hint
Definition: elog.h:346
char * context
Definition: elog.h:347
char * constraint_name
Definition: elog.h:353
char * message
Definition: elog.h:343

◆ pltcl_CreateFileHandler()

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

Definition at line 368 of file pltcl.c.

Referenced by _PG_init().

370 {
371 }

◆ pltcl_DeleteFileHandler()

static void pltcl_DeleteFileHandler ( int  fd)
static

Definition at line 374 of file pltcl.c.

Referenced by _PG_init().

375 {
376 }

◆ 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:1497
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:575
#define INFO
Definition: elog.h:33
#define LOG
Definition: elog.h:26
void FlushErrorState(void)
Definition: elog.c:1587
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1551
#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:37
#define ereport(elevel, rest)
Definition: elog.h:122
#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:293
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300
char * message
Definition: elog.h:343

◆ pltcl_event_trigger_handler()

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

Definition at line 1300 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().

1302 {
1303  pltcl_proc_desc *prodesc;
1304  Tcl_Interp *volatile interp;
1305  EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
1306  Tcl_Obj *tcl_cmd;
1307  int tcl_rc;
1308 
1309  /* Connect to SPI manager */
1310  if (SPI_connect() != SPI_OK_CONNECT)
1311  elog(ERROR, "could not connect to SPI manager");
1312 
1313  /* Find or compile the function */
1314  prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1315  InvalidOid, true, pltrusted);
1316 
1317  call_state->prodesc = prodesc;
1318  prodesc->fn_refcount++;
1319 
1320  interp = prodesc->interp_desc->interp;
1321 
1322  /* Create the tcl command and call the internal proc */
1323  tcl_cmd = Tcl_NewObj();
1324  Tcl_IncrRefCount(tcl_cmd);
1325  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1326  Tcl_NewStringObj(prodesc->internal_proname, -1));
1327  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1328  Tcl_NewStringObj(utf_e2u(tdata->event), -1));
1329  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1330  Tcl_NewStringObj(utf_e2u(tdata->tag), -1));
1331 
1332  tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
1333 
1334  /* Release refcount to free tcl_cmd (and all subsidiary objects) */
1335  Tcl_DecrRefCount(tcl_cmd);
1336 
1337  /* Check for errors reported by Tcl. */
1338  if (tcl_rc != TCL_OK)
1339  throw_tcl_error(interp, prodesc->user_proname);
1340 
1341  if (SPI_finish() != SPI_OK_FINISH)
1342  elog(ERROR, "SPI_finish() failed");
1343 }
#define SPI_OK_CONNECT
Definition: spi.h:50
int SPI_connect(void)
Definition: spi.c:84
int SPI_finish(void)
Definition: spi.c:149
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:1350
#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:1379
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:217
#define SPI_OK_FINISH
Definition: spi.h:51
#define elog
Definition: elog.h:219

◆ pltcl_fetch_interp()

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

Definition at line 555 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().

556 {
557  Oid user_id;
558  pltcl_interp_desc *interp_desc;
559  bool found;
560 
561  /* Find or create the interpreter hashtable entry for this userid */
562  if (pltrusted)
563  user_id = GetUserId();
564  else
565  user_id = InvalidOid;
566 
567  interp_desc = hash_search(pltcl_interp_htab, &user_id,
568  HASH_ENTER,
569  &found);
570  if (!found)
571  interp_desc->interp = NULL;
572 
573  /* If we haven't yet successfully made an interpreter, try to do that */
574  if (!interp_desc->interp)
575  pltcl_init_interp(interp_desc, prolang, pltrusted);
576 
577  return interp_desc;
578 }
static void pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
Definition: pltcl.c:484
Oid GetUserId(void)
Definition: miscinit.c:284
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:902
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:241

◆ pltcl_FinalizeNotifier()

static void pltcl_FinalizeNotifier ( ClientData  clientData)
static

Definition at line 353 of file pltcl.c.

Referenced by _PG_init().

354 {
355 }

◆ pltcl_func_handler()

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

Definition at line 797 of file pltcl.c.

References ReturnSetInfo::allowedModes, pltcl_proc_desc::arg_is_rowtype, pltcl_proc_desc::arg_out_func, Assert, pltcl_call_state::attinmeta, compile_pltcl_function(), CreateTupleDescCopy(), CurrentResourceOwner, DatumGetHeapTupleHeader, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, elog, ereport, errcode(), errmsg(), ERROR, pltcl_proc_desc::fn_is_procedure, 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(), SPI_finish(), SPI_OK_CONNECT, SPI_OK_FINISH, 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().

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

◆ 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:256
const char * label
Definition: pltcl.c:252
int i

◆ pltcl_handler()

static Datum pltcl_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
)
static

Definition at line 716 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_CATCH, PG_END_TRY, PG_RE_THROW, 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().

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

◆ pltcl_init_interp()

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

Definition at line 484 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_elog(), pltcl_hold_interp, pltcl_quote(), pltcl_returnnext(), pltcl_returnnull(), pltcl_SPI_execute(), pltcl_SPI_execute_plan(), pltcl_SPI_lastoid(), pltcl_SPI_prepare(), pltcl_subtransaction(), pltcl_interp_desc::query_hash, snprintf(), and pltcl_interp_desc::user_id.

Referenced by pltcl_fetch_interp().

485 {
486  Tcl_Interp *interp;
487  char interpname[32];
488 
489  /************************************************************
490  * Create the Tcl interpreter as a slave of pltcl_hold_interp.
491  * Note: Tcl automatically does Tcl_Init in the untrusted case,
492  * and it's not wanted in the trusted case.
493  ************************************************************/
494  snprintf(interpname, sizeof(interpname), "slave_%u", interp_desc->user_id);
495  if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
496  pltrusted ? 1 : 0)) == NULL)
497  elog(ERROR, "could not create slave Tcl interpreter");
498 
499  /************************************************************
500  * Initialize the query hash table associated with interpreter
501  ************************************************************/
502  Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
503 
504  /************************************************************
505  * Install the commands for SPI support in the interpreter
506  ************************************************************/
507  Tcl_CreateObjCommand(interp, "elog",
508  pltcl_elog, NULL, NULL);
509  Tcl_CreateObjCommand(interp, "quote",
510  pltcl_quote, NULL, NULL);
511  Tcl_CreateObjCommand(interp, "argisnull",
512  pltcl_argisnull, NULL, NULL);
513  Tcl_CreateObjCommand(interp, "return_null",
514  pltcl_returnnull, NULL, NULL);
515  Tcl_CreateObjCommand(interp, "return_next",
516  pltcl_returnnext, NULL, NULL);
517  Tcl_CreateObjCommand(interp, "spi_exec",
518  pltcl_SPI_execute, NULL, NULL);
519  Tcl_CreateObjCommand(interp, "spi_prepare",
520  pltcl_SPI_prepare, NULL, NULL);
521  Tcl_CreateObjCommand(interp, "spi_execp",
522  pltcl_SPI_execute_plan, NULL, NULL);
523  Tcl_CreateObjCommand(interp, "spi_lastoid",
524  pltcl_SPI_lastoid, NULL, NULL);
525  Tcl_CreateObjCommand(interp, "subtransaction",
526  pltcl_subtransaction, NULL, NULL);
527 
528  /************************************************************
529  * Call the appropriate start_proc, if there is one.
530  *
531  * We must set interp_desc->interp before the call, else the start_proc
532  * won't find the interpreter it's supposed to use. But, if the
533  * start_proc fails, we want to abandon use of the interpreter.
534  ************************************************************/
535  PG_TRY();
536  {
537  interp_desc->interp = interp;
538  call_pltcl_start_proc(prolang, pltrusted);
539  }
540  PG_CATCH();
541  {
542  interp_desc->interp = NULL;
543  Tcl_DeleteInterp(interp);
544  PG_RE_THROW();
545  }
546  PG_END_TRY();
547 }
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2047
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
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:2896
static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2528
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:2655
#define ERROR
Definition: elog.h:43
static void call_pltcl_start_proc(Oid prolang, bool pltrusted)
Definition: pltcl.c:585
static Tcl_Interp * pltcl_hold_interp
Definition: pltcl.c:240
Tcl_Interp * interp
Definition: pltcl.c:115
static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2872
#define PG_CATCH()
Definition: elog.h:293
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:314
#define elog
Definition: elog.h:219
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:284
#define PG_END_TRY()
Definition: elog.h:300
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 3178 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().

3179 {
3180  ReturnSetInfo *rsi = call_state->rsi;
3181  MemoryContext oldcxt;
3182  ResourceOwner oldowner;
3183 
3184  /* Should be in a SRF */
3185  Assert(rsi);
3186  /* Should be first time through */
3187  Assert(!call_state->tuple_store);
3188  Assert(!call_state->attinmeta);
3189 
3190  /* We expect caller to provide an appropriate result tupdesc */
3191  Assert(rsi->expectedDesc);
3192  call_state->ret_tupdesc = rsi->expectedDesc;
3193 
3194  /*
3195  * Switch to the right memory context and resource owner for storing the
3196  * tuplestore. If we're within a subtransaction opened for an exception
3197  * block, for example, we must still create the tuplestore in the resource
3198  * owner that was active when this function was entered, and not in the
3199  * subtransaction's resource owner.
3200  */
3201  oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
3202  oldowner = CurrentResourceOwner;
3204 
3205  call_state->tuple_store =
3207  false, work_mem);
3208 
3209  /* Build attinmeta in this context, too */
3210  call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
3211 
3212  CurrentResourceOwner = oldowner;
3213  MemoryContextSwitchTo(oldcxt);
3214 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ResourceOwner tuple_store_owner
Definition: pltcl.c:230
TupleDesc expectedDesc
Definition: execnodes.h:268
AttInMetadata * attinmeta
Definition: pltcl.c:225
ReturnSetInfo * rsi
Definition: pltcl.c:227
MemoryContext tuple_store_cxt
Definition: pltcl.c:229
Tuplestorestate * tuple_store
Definition: pltcl.c:228
TupleDesc ret_tupdesc
Definition: pltcl.c:224
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
int work_mem
Definition: globals.c:113
int allowedModes
Definition: execnodes.h:269
#define Assert(condition)
Definition: c.h:670

◆ pltcl_InitNotifier()

static ClientData pltcl_InitNotifier ( void  )
static

Definition at line 345 of file pltcl.c.

Referenced by _PG_init().

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

◆ 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 THRU for utility returning tuples */
2447 
2448  case SPI_OK_SELECT:
2452 
2453  /*
2454  * Process the tuples we got
2455  */
2456  tuples = tuptable->vals;
2457  tupdesc = tuptable->tupdesc;
2458 
2459  if (loop_body == NULL)
2460  {
2461  /*
2462  * If there is no loop body given, just set the variables from
2463  * the first tuple (if any)
2464  */
2465  if (ntuples > 0)
2466  pltcl_set_tuple_values(interp, arrayname, 0,
2467  tuples[0], tupdesc);
2468  }
2469  else
2470  {
2471  /*
2472  * There is a loop body - process all tuples and evaluate the
2473  * body on each
2474  */
2475  uint64 i;
2476 
2477  for (i = 0; i < ntuples; i++)
2478  {
2479  pltcl_set_tuple_values(interp, arrayname, i,
2480  tuples[i], tupdesc);
2481 
2482  loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
2483 
2484  if (loop_rc == TCL_OK)
2485  continue;
2486  if (loop_rc == TCL_CONTINUE)
2487  continue;
2488  if (loop_rc == TCL_RETURN)
2489  {
2490  my_rc = TCL_RETURN;
2491  break;
2492  }
2493  if (loop_rc == TCL_BREAK)
2494  break;
2495  my_rc = TCL_ERROR;
2496  break;
2497  }
2498  }
2499 
2500  if (my_rc == TCL_OK)
2501  {
2502  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2503  }
2504  break;
2505 
2506  default:
2507  Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
2508  SPI_result_code_string(spi_rc), NULL);
2509  my_rc = TCL_ERROR;
2510  break;
2511  }
2512 
2513  SPI_freetuptable(tuptable);
2514 
2515  return my_rc;
2516 }
#define SPI_OK_DELETE_RETURNING
Definition: spi.h:61
#define SPI_OK_DELETE
Definition: spi.h:57
HeapTuple * vals
Definition: spi.h:28
#define SPI_OK_INSERT_RETURNING
Definition: spi.h:60
const char * SPI_result_code_string(int code)
Definition: spi.c:1521
#define SPI_OK_UTILITY
Definition: spi.h:53
#define SPI_OK_UPDATE_RETURNING
Definition: spi.h:62
#define SPI_OK_REWRITTEN
Definition: spi.h:63
#define SPI_OK_SELINTO
Definition: spi.h:55
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:978
TupleDesc tupdesc
Definition: spi.h:27
#define SPI_OK_SELECT
Definition: spi.h:54
#define SPI_OK_UPDATE
Definition: spi.h:58
int i
#define SPI_OK_INSERT
Definition: spi.h:56
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname, uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
Definition: pltcl.c:2945

◆ 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 length(), 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 }
int length(const List *list)
Definition: list.c:1309
void pfree(void *pointer)
Definition: mcxt.c:949
void * palloc(Size size)
Definition: mcxt.c:848

◆ 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_is_procedure, pltcl_proc_desc::fn_retisset, pltcl_proc_desc::fn_retistuple, InputFunctionCall(), tupleDesc::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 if (!prodesc->fn_is_procedure)
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:151
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:3178
FmgrInfo result_in_func
Definition: pltcl.c:147
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
FunctionCallInfo fcinfo
Definition: pltcl.c:211
bool fn_is_procedure
Definition: pltcl.c:149
int natts
Definition: tupdesc.h:79
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:245
#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:37
Tuplestorestate * tuple_store
Definition: pltcl.c:228
TupleDesc ret_tupdesc
Definition: pltcl.c:224
uintptr_t Datum
Definition: postgres.h:372
Oid result_typioparam
Definition: pltcl.c:148
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1618
static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
Definition: pltcl.c:3100
#define PG_CATCH()
Definition: elog.h:293
pltcl_proc_desc * prodesc
Definition: pltcl.c:217
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4171
bool fn_retisset
Definition: pltcl.c:150
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300

◆ 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 FunctionCallInfoData::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:211
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:245

◆ pltcl_ServiceModeHook()

static void pltcl_ServiceModeHook ( int  mode)
static

Definition at line 379 of file pltcl.c.

Referenced by _PG_init().

380 {
381 }

◆ 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 2945 of file pltcl.c.

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

Referenced by pltcl_process_SPI_result().

2947 {
2948  int i;
2949  char *outputstr;
2950  Datum attr;
2951  bool isnull;
2952  const char *attname;
2953  Oid typoutput;
2954  bool typisvarlena;
2955  const char **arrptr;
2956  const char **nameptr;
2957  const char *nullname = NULL;
2958 
2959  /************************************************************
2960  * Prepare pointers for Tcl_SetVar2() below
2961  ************************************************************/
2962  if (arrayname == NULL)
2963  {
2964  arrptr = &attname;
2965  nameptr = &nullname;
2966  }
2967  else
2968  {
2969  arrptr = &arrayname;
2970  nameptr = &attname;
2971 
2972  /*
2973  * When outputting to an array, fill the ".tupno" element with the
2974  * current tuple number. This will be overridden below if ".tupno" is
2975  * in use as an actual field name in the rowtype.
2976  */
2977  Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
2978  }
2979 
2980  for (i = 0; i < tupdesc->natts; i++)
2981  {
2982  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2983 
2984  /* ignore dropped attributes */
2985  if (att->attisdropped)
2986  continue;
2987 
2988  /************************************************************
2989  * Get the attribute name
2990  ************************************************************/
2991  UTF_BEGIN;
2992  attname = pstrdup(UTF_E2U(NameStr(att->attname)));
2993  UTF_END;
2994 
2995  /************************************************************
2996  * Get the attributes value
2997  ************************************************************/
2998  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
2999 
3000  /************************************************************
3001  * If there is a value, set the variable
3002  * If not, unset it
3003  *
3004  * Hmmm - Null attributes will cause functions to
3005  * crash if they don't expect them - need something
3006  * smarter here.
3007  ************************************************************/
3008  if (!isnull)
3009  {
3010  getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
3011  outputstr = OidOutputFunctionCall(typoutput, attr);
3012  UTF_BEGIN;
3013  Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
3014  Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
3015  UTF_END;
3016  pfree(outputstr);
3017  }
3018  else
3019  Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
3020 
3021  pfree((char *) attname);
3022  }
3023 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2665
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
char * pstrdup(const char *in)
Definition: mcxt.c:1076
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:79
void pfree(void *pointer)
Definition: mcxt.c:949
#define UTF_E2U(x)
Definition: pltcl.c:98
#define UTF_END
Definition: pltcl.c:90
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define heap_getattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:774
uintptr_t Datum
Definition: postgres.h:372
#define UTF_BEGIN
Definition: pltcl.c:85
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1742
int i
#define NameStr(name)
Definition: c.h:547

◆ pltcl_SetTimer()

static void pltcl_SetTimer ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 358 of file pltcl.c.

Referenced by _PG_init().

359 {
360 }

◆ 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:138
SPITupleTable * SPI_tuptable
Definition: spi.c:41
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2271
uint64 SPI_processed
Definition: spi.c:39
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:245
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:37
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:293
pltcl_proc_desc * prodesc
Definition: pltcl.c:217
int i
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:310

◆ 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 2655 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().

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

◆ pltcl_SPI_lastoid()

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

Definition at line 2872 of file pltcl.c.

References SPI_lastoid.

Referenced by pltcl_init_interp().

2874 {
2875  /*
2876  * Check call syntax
2877  */
2878  if (objc != 1)
2879  {
2880  Tcl_WrongNumArgs(interp, 1, objv, "");
2881  return TCL_ERROR;
2882  }
2883 
2884  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(SPI_lastoid));
2885  return TCL_OK;
2886 }
Oid SPI_lastoid
Definition: spi.c:40

◆ pltcl_SPI_prepare()

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

Definition at line 2528 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().

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

◆ 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:4171

◆ 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:138
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4242
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 2896 of file pltcl.c.

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

Referenced by pltcl_init_interp().

2898 {
2899  MemoryContext oldcontext = CurrentMemoryContext;
2901  int retcode;
2902 
2903  if (objc != 2)
2904  {
2905  Tcl_WrongNumArgs(interp, 1, objv, "command");
2906  return TCL_ERROR;
2907  }
2908 
2909  /*
2910  * Note: we don't use pltcl_subtrans_begin and friends here because we
2911  * don't want the error handling in pltcl_subtrans_abort. But otherwise
2912  * the processing should be about the same as in those functions.
2913  */
2915  MemoryContextSwitchTo(oldcontext);
2916 
2917  retcode = Tcl_EvalObjEx(interp, objv[1], 0);
2918 
2919  if (retcode == TCL_ERROR)
2920  {
2921  /* Rollback the subtransaction */
2923  }
2924  else
2925  {
2926  /* Commit the subtransaction */
2928  }
2929 
2930  /* In either case, restore previous memory context and resource owner */
2931  MemoryContextSwitchTo(oldcontext);
2932  CurrentResourceOwner = oldowner;
2933 
2934  return retcode;
2935 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4242
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4276
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4171

◆ pltcl_trigger_handler()

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

Definition at line 1046 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, tupleDesc::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().

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

◆ pltcl_WaitForEvent()

static int pltcl_WaitForEvent ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 384 of file pltcl.c.

Referenced by _PG_init().

385 {
386  return 0;
387 }

◆ pltclu_call_handler()

Datum pltclu_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 705 of file pltcl.c.

References pltcl_handler().

Referenced by pltcl_call_handler().

706 {
707  return pltcl_handler(fcinfo, false);
708 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:716

◆ start_proc_error_callback()

static void start_proc_error_callback ( void *  arg)
static

Definition at line 673 of file pltcl.c.

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

Referenced by call_pltcl_start_proc().

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

◆ throw_tcl_error()

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

Definition at line 1350 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().

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

◆ 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:634

◆ 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:561

Variable Documentation

◆ exception_name_map

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

Definition at line 256 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 245 of file pltcl.c.

Referenced by pltcl_handler(), and pltcl_returnnext().

◆ pltcl_hold_interp

Tcl_Interp* pltcl_hold_interp = NULL
static

Definition at line 240 of file pltcl.c.

Referenced by _PG_init(), and pltcl_init_interp().

◆ pltcl_interp_htab

HTAB* pltcl_interp_htab = NULL
static

Definition at line 241 of file pltcl.c.

◆ pltcl_pm_init_done

bool pltcl_pm_init_done = false
static

Definition at line 239 of file pltcl.c.

Referenced by _PG_init().

◆ pltcl_proc_htab

HTAB* pltcl_proc_htab = NULL
static

Definition at line 242 of file pltcl.c.

◆ pltcl_start_proc

char* pltcl_start_proc = NULL
static

Definition at line 237 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 238 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().