PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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

#define CONST86

Definition at line 55 of file pltcl.c.

#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.

#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")

Definition at line 60 of file pltcl.c.

Referenced by _PG_init().

#define UTF_BEGIN
#define UTF_E2U (   x)    (_pltcl_utf_dst = utf_e2u(_pltcl_utf_src = (x)))
#define UTF_END
Value:
if (_pltcl_utf_src != (const char *) _pltcl_utf_dst) \
pfree(_pltcl_utf_dst); \
} while (0)
void pfree(void *pointer)
Definition: mcxt.c:949

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

#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

Function Documentation

void _PG_init ( void  )

Definition at line 395 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.

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

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

582 {
583  char *start_proc;
584  const char *gucname;
585  ErrorContextCallback errcallback;
586  List *namelist;
587  Oid fargtypes[1]; /* dummy */
588  Oid procOid;
589  HeapTuple procTup;
590  Form_pg_proc procStruct;
591  AclResult aclresult;
592  FmgrInfo finfo;
593  FunctionCallInfoData fcinfo;
594  PgStat_FunctionCallUsage fcusage;
595 
596  /* select appropriate GUC */
597  start_proc = pltrusted ? pltcl_start_proc : pltclu_start_proc;
598  gucname = pltrusted ? "pltcl.start_proc" : "pltclu.start_proc";
599 
600  /* Nothing to do if it's empty or unset */
601  if (start_proc == NULL || start_proc[0] == '\0')
602  return;
603 
604  /* Set up errcontext callback to make errors more helpful */
605  errcallback.callback = start_proc_error_callback;
606  errcallback.arg = (void *) gucname;
607  errcallback.previous = error_context_stack;
608  error_context_stack = &errcallback;
609 
610  /* Parse possibly-qualified identifier and look up the function */
611  namelist = stringToQualifiedNameList(start_proc);
612  procOid = LookupFuncName(namelist, 0, fargtypes, false);
613 
614  /* Current user must have permission to call function */
615  aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
616  if (aclresult != ACLCHECK_OK)
617  aclcheck_error(aclresult, ACL_KIND_PROC, start_proc);
618 
619  /* Get the function's pg_proc entry */
620  procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
621  if (!HeapTupleIsValid(procTup))
622  elog(ERROR, "cache lookup failed for function %u", procOid);
623  procStruct = (Form_pg_proc) GETSTRUCT(procTup);
624 
625  /* It must be same language as the function we're currently calling */
626  if (procStruct->prolang != prolang)
627  ereport(ERROR,
628  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
629  errmsg("function \"%s\" is in the wrong language",
630  start_proc)));
631 
632  /*
633  * It must not be SECURITY DEFINER, either. This together with the
634  * language match check ensures that the function will execute in the same
635  * Tcl interpreter we just finished initializing.
636  */
637  if (procStruct->prosecdef)
638  ereport(ERROR,
639  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
640  errmsg("function \"%s\" must not be SECURITY DEFINER",
641  start_proc)));
642 
643  /* A-OK */
644  ReleaseSysCache(procTup);
645 
646  /*
647  * Call the function using the normal SQL function call mechanism. We
648  * could perhaps cheat and jump directly to pltcl_handler(), but it seems
649  * better to do it this way so that the call is exposed to, eg, call
650  * statistics collection.
651  */
652  InvokeFunctionExecuteHook(procOid);
653  fmgr_info(procOid, &finfo);
654  InitFunctionCallInfoData(fcinfo, &finfo,
655  0,
656  InvalidOid, NULL, NULL);
657  pgstat_init_function_usage(&fcinfo, &fcusage);
658  (void) FunctionCallInvoke(&fcinfo);
659  pgstat_end_function_usage(&fcusage, true);
660 
661  /* Pop the error context stack */
662  error_context_stack = errcallback.previous;
663 }
Definition: fmgr.h:56
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
Oid GetUserId(void)
Definition: miscinit.c:284
int errcode(int sqlerrcode)
Definition: elog.c:575
unsigned int Oid
Definition: postgres_ext.h:31
struct ErrorContextCallback * previous
Definition: elog.h:238
static char * pltcl_start_proc
Definition: pltcl.c:233
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:3399
#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:669
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:1929
void pgstat_init_function_usage(FunctionCallInfoData *fcinfo, PgStat_FunctionCallUsage *fcu)
Definition: pgstat.c:1581
List * stringToQualifiedNameList(const char *string)
Definition: regproc.c:1687
void(* callback)(void *arg)
Definition: elog.h:239
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:4446
static char * pltclu_start_proc
Definition: pltcl.c:234
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
static pltcl_proc_desc * compile_pltcl_function ( Oid  fn_oid,
Oid  tgreloid,
bool  is_event_trigger,
bool  pltrusted 
)
static

Definition at line 1358 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, elog, ereport, errcode(), errmsg(), ERROR, EVTTRIGGEROID, fmgr_info_cxt(), pltcl_proc_desc::fn_cxt, pltcl_proc_desc::fn_readonly, pltcl_proc_desc::fn_refcount, 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_typioparam, SearchSysCache1(), snprintf(), SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_self, TextDatumGetCString, TopMemoryContext, TRIGGEROID, TYPEOID, TYPTYPE_COMPOSITE, 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().

1360 {
1361  HeapTuple procTup;
1362  Form_pg_proc procStruct;
1363  pltcl_proc_key proc_key;
1364  pltcl_proc_ptr *proc_ptr;
1365  bool found;
1366  pltcl_proc_desc *prodesc;
1367  pltcl_proc_desc *old_prodesc;
1368  volatile MemoryContext proc_cxt = NULL;
1369  Tcl_DString proc_internal_def;
1370  Tcl_DString proc_internal_body;
1371 
1372  /* We'll need the pg_proc tuple in any case... */
1373  procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
1374  if (!HeapTupleIsValid(procTup))
1375  elog(ERROR, "cache lookup failed for function %u", fn_oid);
1376  procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1377 
1378  /*
1379  * Look up function in pltcl_proc_htab; if it's not there, create an entry
1380  * and set the entry's proc_ptr to NULL.
1381  */
1382  proc_key.proc_id = fn_oid;
1383  proc_key.is_trigger = OidIsValid(tgreloid);
1384  proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
1385 
1386  proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
1387  HASH_ENTER,
1388  &found);
1389  if (!found)
1390  proc_ptr->proc_ptr = NULL;
1391 
1392  prodesc = proc_ptr->proc_ptr;
1393 
1394  /************************************************************
1395  * If it's present, must check whether it's still up to date.
1396  * This is needed because CREATE OR REPLACE FUNCTION can modify the
1397  * function's pg_proc entry without changing its OID.
1398  ************************************************************/
1399  if (prodesc != NULL &&
1400  prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
1401  ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
1402  {
1403  /* It's still up-to-date, so we can use it */
1404  ReleaseSysCache(procTup);
1405  return prodesc;
1406  }
1407 
1408  /************************************************************
1409  * If we haven't found it in the hashtable, we analyze
1410  * the functions arguments and returntype and store
1411  * the in-/out-functions in the prodesc block and create
1412  * a new hashtable entry for it.
1413  *
1414  * Then we load the procedure into the Tcl interpreter.
1415  ************************************************************/
1416  Tcl_DStringInit(&proc_internal_def);
1417  Tcl_DStringInit(&proc_internal_body);
1418  PG_TRY();
1419  {
1420  bool is_trigger = OidIsValid(tgreloid);
1421  char internal_proname[128];
1422  HeapTuple typeTup;
1423  Form_pg_type typeStruct;
1424  char proc_internal_args[33 * FUNC_MAX_ARGS];
1425  Datum prosrcdatum;
1426  bool isnull;
1427  char *proc_source;
1428  char buf[32];
1429  Tcl_Interp *interp;
1430  int i;
1431  int tcl_rc;
1432  MemoryContext oldcontext;
1433 
1434  /************************************************************
1435  * Build our internal proc name from the function's Oid. Append
1436  * "_trigger" when appropriate to ensure the normal and trigger
1437  * cases are kept separate. Note name must be all-ASCII.
1438  ************************************************************/
1439  if (is_event_trigger)
1440  snprintf(internal_proname, sizeof(internal_proname),
1441  "__PLTcl_proc_%u_evttrigger", fn_oid);
1442  else if (is_trigger)
1443  snprintf(internal_proname, sizeof(internal_proname),
1444  "__PLTcl_proc_%u_trigger", fn_oid);
1445  else
1446  snprintf(internal_proname, sizeof(internal_proname),
1447  "__PLTcl_proc_%u", fn_oid);
1448 
1449  /************************************************************
1450  * Allocate a context that will hold all PG data for the procedure.
1451  * We use the internal proc name as the context name.
1452  ************************************************************/
1454  internal_proname,
1456 
1457  /************************************************************
1458  * Allocate and fill a new procedure description block.
1459  * struct prodesc and subsidiary data must all live in proc_cxt.
1460  ************************************************************/
1461  oldcontext = MemoryContextSwitchTo(proc_cxt);
1462  prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
1463  prodesc->user_proname = pstrdup(NameStr(procStruct->proname));
1464  prodesc->internal_proname = pstrdup(internal_proname);
1465  prodesc->fn_cxt = proc_cxt;
1466  prodesc->fn_refcount = 0;
1467  prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
1468  prodesc->fn_tid = procTup->t_self;
1469  prodesc->nargs = procStruct->pronargs;
1470  prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
1471  prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
1472  MemoryContextSwitchTo(oldcontext);
1473 
1474  /* Remember if function is STABLE/IMMUTABLE */
1475  prodesc->fn_readonly =
1476  (procStruct->provolatile != PROVOLATILE_VOLATILE);
1477  /* And whether it is trusted */
1478  prodesc->lanpltrusted = pltrusted;
1479 
1480  /************************************************************
1481  * Identify the interpreter to use for the function
1482  ************************************************************/
1483  prodesc->interp_desc = pltcl_fetch_interp(procStruct->prolang,
1484  prodesc->lanpltrusted);
1485  interp = prodesc->interp_desc->interp;
1486 
1487  /************************************************************
1488  * Get the required information for input conversion of the
1489  * return value.
1490  ************************************************************/
1491  if (!is_trigger && !is_event_trigger)
1492  {
1493  typeTup =
1495  ObjectIdGetDatum(procStruct->prorettype));
1496  if (!HeapTupleIsValid(typeTup))
1497  elog(ERROR, "cache lookup failed for type %u",
1498  procStruct->prorettype);
1499  typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1500 
1501  /* Disallow pseudotype result, except VOID and RECORD */
1502  if (typeStruct->typtype == TYPTYPE_PSEUDO)
1503  {
1504  if (procStruct->prorettype == VOIDOID ||
1505  procStruct->prorettype == RECORDOID)
1506  /* okay */ ;
1507  else if (procStruct->prorettype == TRIGGEROID ||
1508  procStruct->prorettype == EVTTRIGGEROID)
1509  ereport(ERROR,
1510  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1511  errmsg("trigger functions can only be called as triggers")));
1512  else
1513  ereport(ERROR,
1514  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1515  errmsg("PL/Tcl functions cannot return type %s",
1516  format_type_be(procStruct->prorettype))));
1517  }
1518 
1519  fmgr_info_cxt(typeStruct->typinput,
1520  &(prodesc->result_in_func),
1521  proc_cxt);
1522  prodesc->result_typioparam = getTypeIOParam(typeTup);
1523 
1524  prodesc->fn_retisset = procStruct->proretset;
1525  prodesc->fn_retistuple = (procStruct->prorettype == RECORDOID ||
1526  typeStruct->typtype == TYPTYPE_COMPOSITE);
1527 
1528  ReleaseSysCache(typeTup);
1529  }
1530 
1531  /************************************************************
1532  * Get the required information for output conversion
1533  * of all procedure arguments, and set up argument naming info.
1534  ************************************************************/
1535  if (!is_trigger && !is_event_trigger)
1536  {
1537  proc_internal_args[0] = '\0';
1538  for (i = 0; i < prodesc->nargs; i++)
1539  {
1540  typeTup = SearchSysCache1(TYPEOID,
1541  ObjectIdGetDatum(procStruct->proargtypes.values[i]));
1542  if (!HeapTupleIsValid(typeTup))
1543  elog(ERROR, "cache lookup failed for type %u",
1544  procStruct->proargtypes.values[i]);
1545  typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1546 
1547  /* Disallow pseudotype argument */
1548  if (typeStruct->typtype == TYPTYPE_PSEUDO)
1549  ereport(ERROR,
1550  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1551  errmsg("PL/Tcl functions cannot accept type %s",
1552  format_type_be(procStruct->proargtypes.values[i]))));
1553 
1554  if (typeStruct->typtype == TYPTYPE_COMPOSITE)
1555  {
1556  prodesc->arg_is_rowtype[i] = true;
1557  snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
1558  }
1559  else
1560  {
1561  prodesc->arg_is_rowtype[i] = false;
1562  fmgr_info_cxt(typeStruct->typoutput,
1563  &(prodesc->arg_out_func[i]),
1564  proc_cxt);
1565  snprintf(buf, sizeof(buf), "%d", i + 1);
1566  }
1567 
1568  if (i > 0)
1569  strcat(proc_internal_args, " ");
1570  strcat(proc_internal_args, buf);
1571 
1572  ReleaseSysCache(typeTup);
1573  }
1574  }
1575  else if (is_trigger)
1576  {
1577  /* trigger procedure has fixed args */
1578  strcpy(proc_internal_args,
1579  "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");
1580  }
1581  else if (is_event_trigger)
1582  {
1583  /* event trigger procedure has fixed args */
1584  strcpy(proc_internal_args, "TG_event TG_tag");
1585  }
1586 
1587  /************************************************************
1588  * Create the tcl command to define the internal
1589  * procedure
1590  *
1591  * Leave this code as DString - performance is not critical here,
1592  * and we don't want to duplicate the knowledge of the Tcl quoting
1593  * rules that's embedded in Tcl_DStringAppendElement.
1594  ************************************************************/
1595  Tcl_DStringAppendElement(&proc_internal_def, "proc");
1596  Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
1597  Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
1598 
1599  /************************************************************
1600  * prefix procedure body with
1601  * upvar #0 <internal_procname> GD
1602  * and with appropriate setting of arguments
1603  ************************************************************/
1604  Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
1605  Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
1606  Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
1607  if (is_trigger)
1608  {
1609  Tcl_DStringAppend(&proc_internal_body,
1610  "array set NEW $__PLTcl_Tup_NEW\n", -1);
1611  Tcl_DStringAppend(&proc_internal_body,
1612  "array set OLD $__PLTcl_Tup_OLD\n", -1);
1613  Tcl_DStringAppend(&proc_internal_body,
1614  "set i 0\n"
1615  "set v 0\n"
1616  "foreach v $args {\n"
1617  " incr i\n"
1618  " set $i $v\n"
1619  "}\n"
1620  "unset i v\n\n", -1);
1621  }
1622  else if (is_event_trigger)
1623  {
1624  /* no argument support for event triggers */
1625  }
1626  else
1627  {
1628  for (i = 0; i < prodesc->nargs; i++)
1629  {
1630  if (prodesc->arg_is_rowtype[i])
1631  {
1632  snprintf(buf, sizeof(buf),
1633  "array set %d $__PLTcl_Tup_%d\n",
1634  i + 1, i + 1);
1635  Tcl_DStringAppend(&proc_internal_body, buf, -1);
1636  }
1637  }
1638  }
1639 
1640  /************************************************************
1641  * Add user's function definition to proc body
1642  ************************************************************/
1643  prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
1644  Anum_pg_proc_prosrc, &isnull);
1645  if (isnull)
1646  elog(ERROR, "null prosrc");
1647  proc_source = TextDatumGetCString(prosrcdatum);
1648  UTF_BEGIN;
1649  Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
1650  UTF_END;
1651  pfree(proc_source);
1652  Tcl_DStringAppendElement(&proc_internal_def,
1653  Tcl_DStringValue(&proc_internal_body));
1654 
1655  /************************************************************
1656  * Create the procedure in the interpreter
1657  ************************************************************/
1658  tcl_rc = Tcl_EvalEx(interp,
1659  Tcl_DStringValue(&proc_internal_def),
1660  Tcl_DStringLength(&proc_internal_def),
1661  TCL_EVAL_GLOBAL);
1662  if (tcl_rc != TCL_OK)
1663  ereport(ERROR,
1664  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1665  errmsg("could not create internal procedure \"%s\": %s",
1666  internal_proname,
1667  utf_u2e(Tcl_GetStringResult(interp)))));
1668  }
1669  PG_CATCH();
1670  {
1671  /*
1672  * If we failed anywhere above, clean up whatever got allocated. It
1673  * should all be in the proc_cxt, except for the DStrings.
1674  */
1675  if (proc_cxt)
1676  MemoryContextDelete(proc_cxt);
1677  Tcl_DStringFree(&proc_internal_def);
1678  Tcl_DStringFree(&proc_internal_body);
1679  PG_RE_THROW();
1680  }
1681  PG_END_TRY();
1682 
1683  /*
1684  * Install the new proc description block in the hashtable, incrementing
1685  * its refcount (the hashtable link counts as a reference). Then, if
1686  * there was a previous definition of the function, decrement that one's
1687  * refcount, and delete it if no longer referenced. The order of
1688  * operations here is important: if something goes wrong during the
1689  * MemoryContextDelete, leaking some memory for the old definition is OK,
1690  * but we don't want to corrupt the live hashtable entry. (Likewise,
1691  * freeing the DStrings is pretty low priority if that happens.)
1692  */
1693  old_prodesc = proc_ptr->proc_ptr;
1694 
1695  proc_ptr->proc_ptr = prodesc;
1696  prodesc->fn_refcount++;
1697 
1698  if (old_prodesc != NULL)
1699  {
1700  Assert(old_prodesc->fn_refcount > 0);
1701  if (--old_prodesc->fn_refcount == 0)
1702  MemoryContextDelete(old_prodesc->fn_cxt);
1703  }
1704 
1705  Tcl_DStringFree(&proc_internal_def);
1706  Tcl_DStringFree(&proc_internal_body);
1707 
1708  ReleaseSysCache(procTup);
1709 
1710  return prodesc;
1711 }
bool lanpltrusted
Definition: pltcl.c:144
Definition: fmgr.h:56
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
bool fn_retistuple
Definition: pltcl.c:149
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
Oid GetUserId(void)
Definition: miscinit.c:284
#define TYPTYPE_COMPOSITE
Definition: pg_type.h:721
FmgrInfo result_in_func
Definition: pltcl.c:146
char * pstrdup(const char *in)
Definition: mcxt.c:1076
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:175
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
char * user_proname
Definition: pltcl.c:137
#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:184
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
#define OidIsValid(objectId)
Definition: c.h:532
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:152
#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
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:5535
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:322
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:147
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:681
pltcl_proc_desc * proc_ptr
Definition: pltcl.c:197
Oid is_trigger
Definition: pltcl.c:190
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:302
#define PG_RE_THROW()
Definition: elog.h:314
static pltcl_interp_desc * pltcl_fetch_interp(Oid prolang, bool pltrusted)
Definition: pltcl.c:551
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:238
MemoryContext fn_cxt
Definition: pltcl.c:139
bool fn_retisset
Definition: pltcl.c:148
Oid user_id
Definition: pltcl.c:191
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool * arg_is_rowtype
Definition: pltcl.c:153
int i
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2053
#define NameStr(name)
Definition: c.h:493
#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 ( pltcl_call_handler  )
PG_FUNCTION_INFO_V1 ( pltclu_call_handler  )
static void pltcl_AlertNotifier ( ClientData  clientData)
static

Definition at line 359 of file pltcl.c.

Referenced by _PG_init().

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

Definition at line 2022 of file pltcl.c.

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

Referenced by pltcl_init_interp().

2024 {
2025  int argno;
2027 
2028  /************************************************************
2029  * Check call syntax
2030  ************************************************************/
2031  if (objc != 2)
2032  {
2033  Tcl_WrongNumArgs(interp, 1, objv, "argno");
2034  return TCL_ERROR;
2035  }
2036 
2037  /************************************************************
2038  * Check that we're called as a normal function
2039  ************************************************************/
2040  if (fcinfo == NULL)
2041  {
2042  Tcl_SetObjResult(interp,
2043  Tcl_NewStringObj("argisnull cannot be used in triggers", -1));
2044  return TCL_ERROR;
2045  }
2046 
2047  /************************************************************
2048  * Get the argument number
2049  ************************************************************/
2050  if (Tcl_GetIntFromObj(interp, objv[1], &argno) != TCL_OK)
2051  return TCL_ERROR;
2052 
2053  /************************************************************
2054  * Check that the argno is valid
2055  ************************************************************/
2056  argno--;
2057  if (argno < 0 || argno >= fcinfo->nargs)
2058  {
2059  Tcl_SetObjResult(interp,
2060  Tcl_NewStringObj("argno out of range", -1));
2061  return TCL_ERROR;
2062  }
2063 
2064  /************************************************************
2065  * Get the requested NULL state
2066  ************************************************************/
2067  Tcl_SetObjResult(interp, Tcl_NewBooleanObj(PG_ARGISNULL(argno)));
2068  return TCL_OK;
2069 }
FunctionCallInfo fcinfo
Definition: pltcl.c:207
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
#define PG_ARGISNULL(n)
Definition: fmgr.h:174
static Tcl_Obj * pltcl_build_tuple_argument ( HeapTuple  tuple,
TupleDesc  tupdesc 
)
static

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

3007 {
3008  Tcl_Obj *retobj = Tcl_NewObj();
3009  int i;
3010  char *outputstr;
3011  Datum attr;
3012  bool isnull;
3013  char *attname;
3014  Oid typoutput;
3015  bool typisvarlena;
3016 
3017  for (i = 0; i < tupdesc->natts; i++)
3018  {
3019  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3020 
3021  /* ignore dropped attributes */
3022  if (att->attisdropped)
3023  continue;
3024 
3025  /************************************************************
3026  * Get the attribute name
3027  ************************************************************/
3028  attname = NameStr(att->attname);
3029 
3030  /************************************************************
3031  * Get the attributes value
3032  ************************************************************/
3033  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3034 
3035  /************************************************************
3036  * If there is a value, append the attribute name and the
3037  * value to the list
3038  *
3039  * Hmmm - Null attributes will cause functions to
3040  * crash if they don't expect them - need something
3041  * smarter here.
3042  ************************************************************/
3043  if (!isnull)
3044  {
3045  getTypeOutputInfo(att->atttypid,
3046  &typoutput, &typisvarlena);
3047  outputstr = OidOutputFunctionCall(typoutput, attr);
3048  UTF_BEGIN;
3049  Tcl_ListObjAppendElement(NULL, retobj,
3050  Tcl_NewStringObj(UTF_E2U(attname), -1));
3051  UTF_END;
3052  UTF_BEGIN;
3053  Tcl_ListObjAppendElement(NULL, retobj,
3054  Tcl_NewStringObj(UTF_E2U(outputstr), -1));
3055  UTF_END;
3056  pfree(outputstr);
3057  }
3058  }
3059 
3060  return retobj;
3061 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2632
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:84
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:73
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:769
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:493
static HeapTuple pltcl_build_tuple_result ( Tcl_Interp *  interp,
Tcl_Obj **  kvObjv,
int  kvObjc,
pltcl_call_state call_state 
)
static

Definition at line 3075 of file pltcl.c.

References pltcl_call_state::attinmeta, BuildTupleFromCStrings(), elog, ereport, errcode(), errmsg(), ERROR, i, tupleDesc::natts, palloc0(), RelationGetDescr, 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().

3077 {
3078  TupleDesc tupdesc;
3079  AttInMetadata *attinmeta;
3080  char **values;
3081  int i;
3082 
3083  if (call_state->ret_tupdesc)
3084  {
3085  tupdesc = call_state->ret_tupdesc;
3086  attinmeta = call_state->attinmeta;
3087  }
3088  else if (call_state->trigdata)
3089  {
3090  tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
3091  attinmeta = TupleDescGetAttInMetadata(tupdesc);
3092  }
3093  else
3094  {
3095  elog(ERROR, "PL/Tcl function does not return a tuple");
3096  tupdesc = NULL; /* keep compiler quiet */
3097  attinmeta = NULL;
3098  }
3099 
3100  values = (char **) palloc0(tupdesc->natts * sizeof(char *));
3101 
3102  if (kvObjc % 2 != 0)
3103  ereport(ERROR,
3104  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3105  errmsg("column name/value list must have even number of elements")));
3106 
3107  for (i = 0; i < kvObjc; i += 2)
3108  {
3109  char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
3110  int attn = SPI_fnumber(tupdesc, fieldName);
3111 
3112  /*
3113  * We silently ignore ".tupno", if it's present but doesn't match any
3114  * actual output column. This allows direct use of a row returned by
3115  * pltcl_set_tuple_values().
3116  */
3117  if (attn == SPI_ERROR_NOATTRIBUTE)
3118  {
3119  if (strcmp(fieldName, ".tupno") == 0)
3120  continue;
3121  ereport(ERROR,
3122  (errcode(ERRCODE_UNDEFINED_COLUMN),
3123  errmsg("column name/value list contains nonexistent column name \"%s\"",
3124  fieldName)));
3125  }
3126 
3127  if (attn <= 0)
3128  ereport(ERROR,
3129  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3130  errmsg("cannot set system attribute \"%s\"",
3131  fieldName)));
3132 
3133  values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
3134  }
3135 
3136  return BuildTupleFromCStrings(attinmeta, values);
3137 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:767
#define RelationGetDescr(relation)
Definition: rel.h:428
int errcode(int sqlerrcode)
Definition: elog.c:575
int natts
Definition: tupdesc.h:73
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:1118
AttInMetadata * attinmeta
Definition: pltcl.c:221
#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:220
void * palloc0(Size size)
Definition: mcxt.c:877
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:1069
static Datum values[MAXATTR]
Definition: bootstrap.c:164
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
#define elog
Definition: elog.h:219
TriggerData * trigdata
Definition: pltcl.c:210
Relation tg_relation
Definition: trigger.h:34
Datum pltcl_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 689 of file pltcl.c.

References pltcl_handler().

690 {
691  return pltcl_handler(fcinfo, true);
692 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:712
static void pltcl_construct_errorCode ( Tcl_Interp *  interp,
ErrorData edata 
)
static

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

1806 {
1807  Tcl_Obj *obj = Tcl_NewObj();
1808 
1809  Tcl_ListObjAppendElement(interp, obj,
1810  Tcl_NewStringObj("POSTGRES", -1));
1811  Tcl_ListObjAppendElement(interp, obj,
1812  Tcl_NewStringObj(PG_VERSION, -1));
1813  Tcl_ListObjAppendElement(interp, obj,
1814  Tcl_NewStringObj("SQLSTATE", -1));
1815  Tcl_ListObjAppendElement(interp, obj,
1816  Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode), -1));
1817  Tcl_ListObjAppendElement(interp, obj,
1818  Tcl_NewStringObj("condition", -1));
1819  Tcl_ListObjAppendElement(interp, obj,
1820  Tcl_NewStringObj(pltcl_get_condition_name(edata->sqlerrcode), -1));
1821  Tcl_ListObjAppendElement(interp, obj,
1822  Tcl_NewStringObj("message", -1));
1823  UTF_BEGIN;
1824  Tcl_ListObjAppendElement(interp, obj,
1825  Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1826  UTF_END;
1827  if (edata->detail)
1828  {
1829  Tcl_ListObjAppendElement(interp, obj,
1830  Tcl_NewStringObj("detail", -1));
1831  UTF_BEGIN;
1832  Tcl_ListObjAppendElement(interp, obj,
1833  Tcl_NewStringObj(UTF_E2U(edata->detail), -1));
1834  UTF_END;
1835  }
1836  if (edata->hint)
1837  {
1838  Tcl_ListObjAppendElement(interp, obj,
1839  Tcl_NewStringObj("hint", -1));
1840  UTF_BEGIN;
1841  Tcl_ListObjAppendElement(interp, obj,
1842  Tcl_NewStringObj(UTF_E2U(edata->hint), -1));
1843  UTF_END;
1844  }
1845  if (edata->context)
1846  {
1847  Tcl_ListObjAppendElement(interp, obj,
1848  Tcl_NewStringObj("context", -1));
1849  UTF_BEGIN;
1850  Tcl_ListObjAppendElement(interp, obj,
1851  Tcl_NewStringObj(UTF_E2U(edata->context), -1));
1852  UTF_END;
1853  }
1854  if (edata->schema_name)
1855  {
1856  Tcl_ListObjAppendElement(interp, obj,
1857  Tcl_NewStringObj("schema", -1));
1858  UTF_BEGIN;
1859  Tcl_ListObjAppendElement(interp, obj,
1860  Tcl_NewStringObj(UTF_E2U(edata->schema_name), -1));
1861  UTF_END;
1862  }
1863  if (edata->table_name)
1864  {
1865  Tcl_ListObjAppendElement(interp, obj,
1866  Tcl_NewStringObj("table", -1));
1867  UTF_BEGIN;
1868  Tcl_ListObjAppendElement(interp, obj,
1869  Tcl_NewStringObj(UTF_E2U(edata->table_name), -1));
1870  UTF_END;
1871  }
1872  if (edata->column_name)
1873  {
1874  Tcl_ListObjAppendElement(interp, obj,
1875  Tcl_NewStringObj("column", -1));
1876  UTF_BEGIN;
1877  Tcl_ListObjAppendElement(interp, obj,
1878  Tcl_NewStringObj(UTF_E2U(edata->column_name), -1));
1879  UTF_END;
1880  }
1881  if (edata->datatype_name)
1882  {
1883  Tcl_ListObjAppendElement(interp, obj,
1884  Tcl_NewStringObj("datatype", -1));
1885  UTF_BEGIN;
1886  Tcl_ListObjAppendElement(interp, obj,
1887  Tcl_NewStringObj(UTF_E2U(edata->datatype_name), -1));
1888  UTF_END;
1889  }
1890  if (edata->constraint_name)
1891  {
1892  Tcl_ListObjAppendElement(interp, obj,
1893  Tcl_NewStringObj("constraint", -1));
1894  UTF_BEGIN;
1895  Tcl_ListObjAppendElement(interp, obj,
1896  Tcl_NewStringObj(UTF_E2U(edata->constraint_name), -1));
1897  UTF_END;
1898  }
1899  /* cursorpos is never interesting here; report internal query/pos */
1900  if (edata->internalquery)
1901  {
1902  Tcl_ListObjAppendElement(interp, obj,
1903  Tcl_NewStringObj("statement", -1));
1904  UTF_BEGIN;
1905  Tcl_ListObjAppendElement(interp, obj,
1906  Tcl_NewStringObj(UTF_E2U(edata->internalquery), -1));
1907  UTF_END;
1908  }
1909  if (edata->internalpos > 0)
1910  {
1911  Tcl_ListObjAppendElement(interp, obj,
1912  Tcl_NewStringObj("cursor_position", -1));
1913  Tcl_ListObjAppendElement(interp, obj,
1914  Tcl_NewIntObj(edata->internalpos));
1915  }
1916  if (edata->filename)
1917  {
1918  Tcl_ListObjAppendElement(interp, obj,
1919  Tcl_NewStringObj("filename", -1));
1920  UTF_BEGIN;
1921  Tcl_ListObjAppendElement(interp, obj,
1922  Tcl_NewStringObj(UTF_E2U(edata->filename), -1));
1923  UTF_END;
1924  }
1925  if (edata->lineno > 0)
1926  {
1927  Tcl_ListObjAppendElement(interp, obj,
1928  Tcl_NewStringObj("lineno", -1));
1929  Tcl_ListObjAppendElement(interp, obj,
1930  Tcl_NewIntObj(edata->lineno));
1931  }
1932  if (edata->funcname)
1933  {
1934  Tcl_ListObjAppendElement(interp, obj,
1935  Tcl_NewStringObj("funcname", -1));
1936  UTF_BEGIN;
1937  Tcl_ListObjAppendElement(interp, obj,
1938  Tcl_NewStringObj(UTF_E2U(edata->funcname), -1));
1939  UTF_END;
1940  }
1941 
1942  Tcl_SetObjErrorCode(interp, obj);
1943 }
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:2844
int lineno
Definition: elog.h:338
static const char * pltcl_get_condition_name(int sqlstate)
Definition: pltcl.c:1950
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
static void pltcl_CreateFileHandler ( int  fd,
int  mask,
Tcl_FileProc *  proc,
ClientData  clientData 
)
static

Definition at line 364 of file pltcl.c.

Referenced by _PG_init().

366 {
367 }
static void pltcl_DeleteFileHandler ( int  fd)
static

Definition at line 370 of file pltcl.c.

Referenced by _PG_init().

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

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

1720 {
1721  volatile int level;
1722  MemoryContext oldcontext;
1723  int priIndex;
1724 
1725  static const char *logpriorities[] = {
1726  "DEBUG", "LOG", "INFO", "NOTICE",
1727  "WARNING", "ERROR", "FATAL", (const char *) NULL
1728  };
1729 
1730  static const int loglevels[] = {
1731  DEBUG2, LOG, INFO, NOTICE,
1732  WARNING, ERROR, FATAL
1733  };
1734 
1735  if (objc != 3)
1736  {
1737  Tcl_WrongNumArgs(interp, 1, objv, "level msg");
1738  return TCL_ERROR;
1739  }
1740 
1741  if (Tcl_GetIndexFromObj(interp, objv[1], logpriorities, "priority",
1742  TCL_EXACT, &priIndex) != TCL_OK)
1743  return TCL_ERROR;
1744 
1745  level = loglevels[priIndex];
1746 
1747  if (level == ERROR)
1748  {
1749  /*
1750  * We just pass the error back to Tcl. If it's not caught, it'll
1751  * eventually get converted to a PG error when we reach the call
1752  * handler.
1753  */
1754  Tcl_SetObjResult(interp, objv[2]);
1755  return TCL_ERROR;
1756  }
1757 
1758  /*
1759  * For non-error messages, just pass 'em to ereport(). We do not expect
1760  * that this will fail, but just on the off chance it does, report the
1761  * error back to Tcl. Note we are assuming that ereport() can't have any
1762  * internal failures that are so bad as to require a transaction abort.
1763  *
1764  * This path is also used for FATAL errors, which aren't going to come
1765  * back to us at all.
1766  */
1767  oldcontext = CurrentMemoryContext;
1768  PG_TRY();
1769  {
1770  UTF_BEGIN;
1771  ereport(level,
1772  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1773  errmsg("%s", UTF_U2E(Tcl_GetString(objv[2])))));
1774  UTF_END;
1775  }
1776  PG_CATCH();
1777  {
1778  ErrorData *edata;
1779 
1780  /* Must reset elog.c's state */
1781  MemoryContextSwitchTo(oldcontext);
1782  edata = CopyErrorData();
1783  FlushErrorState();
1784 
1785  /* Pass the error data to Tcl */
1786  pltcl_construct_errorCode(interp, edata);
1787  UTF_BEGIN;
1788  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1789  UTF_END;
1790  FreeErrorData(edata);
1791 
1792  return TCL_ERROR;
1793  }
1794  PG_END_TRY();
1795 
1796  return TCL_OK;
1797 }
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:1805
#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
static void pltcl_event_trigger_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

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

1281 {
1282  pltcl_proc_desc *prodesc;
1283  Tcl_Interp *volatile interp;
1284  EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
1285  Tcl_Obj *tcl_cmd;
1286  int tcl_rc;
1287 
1288  /* Connect to SPI manager */
1289  if (SPI_connect() != SPI_OK_CONNECT)
1290  elog(ERROR, "could not connect to SPI manager");
1291 
1292  /* Find or compile the function */
1293  prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1294  InvalidOid, true, pltrusted);
1295 
1296  call_state->prodesc = prodesc;
1297  prodesc->fn_refcount++;
1298 
1299  interp = prodesc->interp_desc->interp;
1300 
1301  /* Create the tcl command and call the internal proc */
1302  tcl_cmd = Tcl_NewObj();
1303  Tcl_IncrRefCount(tcl_cmd);
1304  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1305  Tcl_NewStringObj(prodesc->internal_proname, -1));
1306  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1307  Tcl_NewStringObj(utf_e2u(tdata->event), -1));
1308  Tcl_ListObjAppendElement(NULL, tcl_cmd,
1309  Tcl_NewStringObj(utf_e2u(tdata->tag), -1));
1310 
1311  tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
1312 
1313  /* Release refcount to free tcl_cmd (and all subsidiary objects) */
1314  Tcl_DecrRefCount(tcl_cmd);
1315 
1316  /* Check for errors reported by Tcl. */
1317  if (tcl_rc != TCL_OK)
1318  throw_tcl_error(interp, prodesc->user_proname);
1319 
1320  if (SPI_finish() != SPI_OK_FINISH)
1321  elog(ERROR, "SPI_finish() failed");
1322 }
#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:1329
#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:1358
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:213
#define SPI_OK_FINISH
Definition: spi.h:51
#define elog
Definition: elog.h:219
static pltcl_interp_desc * pltcl_fetch_interp ( Oid  prolang,
bool  pltrusted 
)
static

Definition at line 551 of file pltcl.c.

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

Referenced by compile_pltcl_function().

552 {
553  Oid user_id;
554  pltcl_interp_desc *interp_desc;
555  bool found;
556 
557  /* Find or create the interpreter hashtable entry for this userid */
558  if (pltrusted)
559  user_id = GetUserId();
560  else
561  user_id = InvalidOid;
562 
563  interp_desc = hash_search(pltcl_interp_htab, &user_id,
564  HASH_ENTER,
565  &found);
566  if (!found)
567  interp_desc->interp = NULL;
568 
569  /* If we haven't yet successfully made an interpreter, try to do that */
570  if (!interp_desc->interp)
571  pltcl_init_interp(interp_desc, prolang, pltrusted);
572 
573  return interp_desc;
574 }
static void pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
Definition: pltcl.c:480
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:237
static void pltcl_FinalizeNotifier ( ClientData  clientData)
static

Definition at line 349 of file pltcl.c.

Referenced by _PG_init().

350 {
351 }
static Datum pltcl_func_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

Definition at line 793 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_refcount, 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, pltcl_proc_desc::user_proname, UTF_BEGIN, UTF_E2U, UTF_END, and utf_u2e().

Referenced by pltcl_handler().

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

Definition at line 1950 of file pltcl.c.

References i, and TclExceptionNameMap::label.

Referenced by pltcl_construct_errorCode().

1951 {
1952  int i;
1953 
1954  for (i = 0; exception_name_map[i].label != NULL; i++)
1955  {
1956  if (exception_name_map[i].sqlerrstate == sqlstate)
1957  return exception_name_map[i].label;
1958  }
1959  return "unrecognized_sqlstate";
1960 }
static const TclExceptionNameMap exception_name_map[]
Definition: pltcl.c:252
const char * label
Definition: pltcl.c:248
int i
static Datum pltcl_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
)
static

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

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

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

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

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

3144 {
3145  ReturnSetInfo *rsi = call_state->rsi;
3146  MemoryContext oldcxt;
3147  ResourceOwner oldowner;
3148 
3149  /* Should be in a SRF */
3150  Assert(rsi);
3151  /* Should be first time through */
3152  Assert(!call_state->tuple_store);
3153  Assert(!call_state->attinmeta);
3154 
3155  /* We expect caller to provide an appropriate result tupdesc */
3156  Assert(rsi->expectedDesc);
3157  call_state->ret_tupdesc = rsi->expectedDesc;
3158 
3159  /*
3160  * Switch to the right memory context and resource owner for storing the
3161  * tuplestore. If we're within a subtransaction opened for an exception
3162  * block, for example, we must still create the tuplestore in the resource
3163  * owner that was active when this function was entered, and not in the
3164  * subtransaction's resource owner.
3165  */
3166  oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
3167  oldowner = CurrentResourceOwner;
3169 
3170  call_state->tuple_store =
3172  false, work_mem);
3173 
3174  /* Build attinmeta in this context, too */
3175  call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
3176 
3177  CurrentResourceOwner = oldowner;
3178  MemoryContextSwitchTo(oldcxt);
3179 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ResourceOwner tuple_store_owner
Definition: pltcl.c:226
TupleDesc expectedDesc
Definition: execnodes.h:267
AttInMetadata * attinmeta
Definition: pltcl.c:221
ReturnSetInfo * rsi
Definition: pltcl.c:223
MemoryContext tuple_store_cxt
Definition: pltcl.c:225
Tuplestorestate * tuple_store
Definition: pltcl.c:224
TupleDesc ret_tupdesc
Definition: pltcl.c:220
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:268
#define Assert(condition)
Definition: c.h:681
static ClientData pltcl_InitNotifier ( void  )
static

Definition at line 341 of file pltcl.c.

Referenced by _PG_init().

342 {
343  static int fakeThreadKey; /* To give valid address for ClientData */
344 
345  return (ClientData) &(fakeThreadKey);
346 }
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 2393 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().

2399 {
2400  int my_rc = TCL_OK;
2401  int loop_rc;
2402  HeapTuple *tuples;
2403  TupleDesc tupdesc;
2404 
2405  switch (spi_rc)
2406  {
2407  case SPI_OK_SELINTO:
2408  case SPI_OK_INSERT:
2409  case SPI_OK_DELETE:
2410  case SPI_OK_UPDATE:
2411  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2412  break;
2413 
2414  case SPI_OK_UTILITY:
2415  case SPI_OK_REWRITTEN:
2416  if (tuptable == NULL)
2417  {
2418  Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
2419  break;
2420  }
2421  /* FALL THRU for utility returning tuples */
2422 
2423  case SPI_OK_SELECT:
2427 
2428  /*
2429  * Process the tuples we got
2430  */
2431  tuples = tuptable->vals;
2432  tupdesc = tuptable->tupdesc;
2433 
2434  if (loop_body == NULL)
2435  {
2436  /*
2437  * If there is no loop body given, just set the variables from
2438  * the first tuple (if any)
2439  */
2440  if (ntuples > 0)
2441  pltcl_set_tuple_values(interp, arrayname, 0,
2442  tuples[0], tupdesc);
2443  }
2444  else
2445  {
2446  /*
2447  * There is a loop body - process all tuples and evaluate the
2448  * body on each
2449  */
2450  uint64 i;
2451 
2452  for (i = 0; i < ntuples; i++)
2453  {
2454  pltcl_set_tuple_values(interp, arrayname, i,
2455  tuples[i], tupdesc);
2456 
2457  loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
2458 
2459  if (loop_rc == TCL_OK)
2460  continue;
2461  if (loop_rc == TCL_CONTINUE)
2462  continue;
2463  if (loop_rc == TCL_RETURN)
2464  {
2465  my_rc = TCL_RETURN;
2466  break;
2467  }
2468  if (loop_rc == TCL_BREAK)
2469  break;
2470  my_rc = TCL_ERROR;
2471  break;
2472  }
2473  }
2474 
2475  if (my_rc == TCL_OK)
2476  {
2477  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2478  }
2479  break;
2480 
2481  default:
2482  Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
2483  SPI_result_code_string(spi_rc), NULL);
2484  my_rc = TCL_ERROR;
2485  break;
2486  }
2487 
2488  SPI_freetuptable(tuptable);
2489 
2490  return my_rc;
2491 }
#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:2920
static int pltcl_quote ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 1968 of file pltcl.c.

References length(), palloc(), and pfree().

Referenced by pltcl_init_interp().

1970 {
1971  char *tmp;
1972  const char *cp1;
1973  char *cp2;
1974  int length;
1975 
1976  /************************************************************
1977  * Check call syntax
1978  ************************************************************/
1979  if (objc != 2)
1980  {
1981  Tcl_WrongNumArgs(interp, 1, objv, "string");
1982  return TCL_ERROR;
1983  }
1984 
1985  /************************************************************
1986  * Allocate space for the maximum the string can
1987  * grow to and initialize pointers
1988  ************************************************************/
1989  cp1 = Tcl_GetStringFromObj(objv[1], &length);
1990  tmp = palloc(length * 2 + 1);
1991  cp2 = tmp;
1992 
1993  /************************************************************
1994  * Walk through string and double every quote and backslash
1995  ************************************************************/
1996  while (*cp1)
1997  {
1998  if (*cp1 == '\'')
1999  *cp2++ = '\'';
2000  else
2001  {
2002  if (*cp1 == '\\')
2003  *cp2++ = '\\';
2004  }
2005  *cp2++ = *cp1++;
2006  }
2007 
2008  /************************************************************
2009  * Terminate the string and set it as result
2010  ************************************************************/
2011  *cp2 = '\0';
2012  Tcl_SetObjResult(interp, Tcl_NewStringObj(tmp, -1));
2013  pfree(tmp);
2014  return TCL_OK;
2015 }
int length(const List *list)
Definition: list.c:1271
void pfree(void *pointer)
Definition: mcxt.c:949
void * palloc(Size size)
Definition: mcxt.c:848
static int pltcl_returnnext ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2114 of file pltcl.c.

References BeginInternalSubTransaction(), CurrentMemoryContext, CurrentResourceOwner, elog, ERROR, pltcl_call_state::fcinfo, pltcl_proc_desc::fn_retisset, pltcl_proc_desc::fn_retistuple, InputFunctionCall(), 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().

2116 {
2118  FunctionCallInfo fcinfo = call_state->fcinfo;
2119  pltcl_proc_desc *prodesc = call_state->prodesc;
2120  MemoryContext oldcontext = CurrentMemoryContext;
2122  volatile int result = TCL_OK;
2123 
2124  /*
2125  * Check that we're called as a set-returning function
2126  */
2127  if (fcinfo == NULL)
2128  {
2129  Tcl_SetObjResult(interp,
2130  Tcl_NewStringObj("return_next cannot be used in triggers", -1));
2131  return TCL_ERROR;
2132  }
2133 
2134  if (!prodesc->fn_retisset)
2135  {
2136  Tcl_SetObjResult(interp,
2137  Tcl_NewStringObj("return_next cannot be used in non-set-returning functions", -1));
2138  return TCL_ERROR;
2139  }
2140 
2141  /*
2142  * Check call syntax
2143  */
2144  if (objc != 2)
2145  {
2146  Tcl_WrongNumArgs(interp, 1, objv, "result");
2147  return TCL_ERROR;
2148  }
2149 
2150  /*
2151  * The rest might throw elog(ERROR), so must run in a subtransaction.
2152  *
2153  * A small advantage of using a subtransaction is that it provides a
2154  * short-lived memory context for free, so we needn't worry about leaking
2155  * memory here. To use that context, call BeginInternalSubTransaction
2156  * directly instead of going through pltcl_subtrans_begin.
2157  */
2159  PG_TRY();
2160  {
2161  /* Set up tuple store if first output row */
2162  if (call_state->tuple_store == NULL)
2163  pltcl_init_tuple_store(call_state);
2164 
2165  if (prodesc->fn_retistuple)
2166  {
2167  Tcl_Obj **rowObjv;
2168  int rowObjc;
2169 
2170  /* result should be a list, so break it down */
2171  if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
2172  result = TCL_ERROR;
2173  else
2174  {
2175  HeapTuple tuple;
2176 
2177  tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
2178  call_state);
2179  tuplestore_puttuple(call_state->tuple_store, tuple);
2180  }
2181  }
2182  else
2183  {
2184  Datum retval;
2185  bool isNull = false;
2186 
2187  /* for paranoia's sake, check that tupdesc has exactly one column */
2188  if (call_state->ret_tupdesc->natts != 1)
2189  elog(ERROR, "wrong result type supplied in return_next");
2190 
2191  retval = InputFunctionCall(&prodesc->result_in_func,
2192  utf_u2e((char *) Tcl_GetString(objv[1])),
2193  prodesc->result_typioparam,
2194  -1);
2195  tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
2196  &retval, &isNull);
2197  }
2198 
2199  pltcl_subtrans_commit(oldcontext, oldowner);
2200  }
2201  PG_CATCH();
2202  {
2203  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2204  return TCL_ERROR;
2205  }
2206  PG_END_TRY();
2207 
2208  return result;
2209 }
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
bool fn_retistuple
Definition: pltcl.c:149
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2255
static void pltcl_init_tuple_store(pltcl_call_state *call_state)
Definition: pltcl.c:3143
FmgrInfo result_in_func
Definition: pltcl.c:146
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2246
FunctionCallInfo fcinfo
Definition: pltcl.c:207
int natts
Definition: tupdesc.h:73
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
#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:224
TupleDesc ret_tupdesc
Definition: pltcl.c:220
uintptr_t Datum
Definition: postgres.h:372
Oid result_typioparam
Definition: pltcl.c:147
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:3075
#define PG_CATCH()
Definition: elog.h:293
pltcl_proc_desc * prodesc
Definition: pltcl.c:213
void BeginInternalSubTransaction(char *name)
Definition: xact.c:4171
bool fn_retisset
Definition: pltcl.c:148
#define elog
Definition: elog.h:219
#define PG_TRY()
Definition: elog.h:284
#define PG_END_TRY()
Definition: elog.h:300
static int pltcl_returnnull ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2076 of file pltcl.c.

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

Referenced by pltcl_init_interp().

2078 {
2080 
2081  /************************************************************
2082  * Check call syntax
2083  ************************************************************/
2084  if (objc != 1)
2085  {
2086  Tcl_WrongNumArgs(interp, 1, objv, "");
2087  return TCL_ERROR;
2088  }
2089 
2090  /************************************************************
2091  * Check that we're called as a normal function
2092  ************************************************************/
2093  if (fcinfo == NULL)
2094  {
2095  Tcl_SetObjResult(interp,
2096  Tcl_NewStringObj("return_null cannot be used in triggers", -1));
2097  return TCL_ERROR;
2098  }
2099 
2100  /************************************************************
2101  * Set the NULL return flag and cause Tcl to return from the
2102  * procedure.
2103  ************************************************************/
2104  fcinfo->isnull = true;
2105 
2106  return TCL_RETURN;
2107 }
FunctionCallInfo fcinfo
Definition: pltcl.c:207
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
static void pltcl_ServiceModeHook ( int  mode)
static

Definition at line 375 of file pltcl.c.

Referenced by _PG_init().

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

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

2922 {
2923  int i;
2924  char *outputstr;
2925  Datum attr;
2926  bool isnull;
2927  const char *attname;
2928  Oid typoutput;
2929  bool typisvarlena;
2930  const char **arrptr;
2931  const char **nameptr;
2932  const char *nullname = NULL;
2933 
2934  /************************************************************
2935  * Prepare pointers for Tcl_SetVar2() below
2936  ************************************************************/
2937  if (arrayname == NULL)
2938  {
2939  arrptr = &attname;
2940  nameptr = &nullname;
2941  }
2942  else
2943  {
2944  arrptr = &arrayname;
2945  nameptr = &attname;
2946 
2947  /*
2948  * When outputting to an array, fill the ".tupno" element with the
2949  * current tuple number. This will be overridden below if ".tupno" is
2950  * in use as an actual field name in the rowtype.
2951  */
2952  Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
2953  }
2954 
2955  for (i = 0; i < tupdesc->natts; i++)
2956  {
2957  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2958 
2959  /* ignore dropped attributes */
2960  if (att->attisdropped)
2961  continue;
2962 
2963  /************************************************************
2964  * Get the attribute name
2965  ************************************************************/
2966  UTF_BEGIN;
2967  attname = pstrdup(UTF_E2U(NameStr(att->attname)));
2968  UTF_END;
2969 
2970  /************************************************************
2971  * Get the attributes value
2972  ************************************************************/
2973  attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
2974 
2975  /************************************************************
2976  * If there is a value, set the variable
2977  * If not, unset it
2978  *
2979  * Hmmm - Null attributes will cause functions to
2980  * crash if they don't expect them - need something
2981  * smarter here.
2982  ************************************************************/
2983  if (!isnull)
2984  {
2985  getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
2986  outputstr = OidOutputFunctionCall(typoutput, attr);
2987  UTF_BEGIN;
2988  Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
2989  Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
2990  UTF_END;
2991  pfree(outputstr);
2992  }
2993  else
2994  Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
2995 
2996  pfree((char *) attname);
2997  }
2998 }
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2632
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:84
char * pstrdup(const char *in)
Definition: mcxt.c:1076
unsigned int Oid
Definition: postgres_ext.h:31
int natts
Definition: tupdesc.h:73
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:769
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:493
static void pltcl_SetTimer ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 354 of file pltcl.c.

Referenced by _PG_init().

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

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

2286 {
2287  int my_rc;
2288  int spi_rc;
2289  int query_idx;
2290  int i;
2291  int optIndex;
2292  int count = 0;
2293  const char *volatile arrayname = NULL;
2294  Tcl_Obj *volatile loop_body = NULL;
2295  MemoryContext oldcontext = CurrentMemoryContext;
2297 
2298  enum options
2299  {
2300  OPT_ARRAY, OPT_COUNT
2301  };
2302 
2303  static const char *options[] = {
2304  "-array", "-count", (const char *) NULL
2305  };
2306 
2307  /************************************************************
2308  * Check the call syntax and get the options
2309  ************************************************************/
2310  if (objc < 2)
2311  {
2312  Tcl_WrongNumArgs(interp, 1, objv,
2313  "?-count n? ?-array name? query ?loop body?");
2314  return TCL_ERROR;
2315  }
2316 
2317  i = 1;
2318  while (i < objc)
2319  {
2320  if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2321  TCL_EXACT, &optIndex) != TCL_OK)
2322  break;
2323 
2324  if (++i >= objc)
2325  {
2326  Tcl_SetObjResult(interp,
2327  Tcl_NewStringObj("missing argument to -count or -array", -1));
2328  return TCL_ERROR;
2329  }
2330 
2331  switch ((enum options) optIndex)
2332  {
2333  case OPT_ARRAY:
2334  arrayname = Tcl_GetString(objv[i++]);
2335  break;
2336 
2337  case OPT_COUNT:
2338  if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2339  return TCL_ERROR;
2340  break;
2341  }
2342  }
2343 
2344  query_idx = i;
2345  if (query_idx >= objc || query_idx + 2 < objc)
2346  {
2347  Tcl_WrongNumArgs(interp, query_idx - 1, objv, "query ?loop body?");
2348  return TCL_ERROR;
2349  }
2350 
2351  if (query_idx + 1 < objc)
2352  loop_body = objv[query_idx + 1];
2353 
2354  /************************************************************
2355  * Execute the query inside a sub-transaction, so we can cope with
2356  * errors sanely
2357  ************************************************************/
2358 
2359  pltcl_subtrans_begin(oldcontext, oldowner);
2360 
2361  PG_TRY();
2362  {
2363  UTF_BEGIN;
2364  spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
2366  UTF_END;
2367 
2368  my_rc = pltcl_process_SPI_result(interp,
2369  arrayname,
2370  loop_body,
2371  spi_rc,
2372  SPI_tuptable,
2373  SPI_processed);
2374 
2375  pltcl_subtrans_commit(oldcontext, oldowner);
2376  }
2377  PG_CATCH();
2378  {
2379  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2380  return TCL_ERROR;
2381  }
2382  PG_END_TRY();
2383 
2384  return my_rc;
2385 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2237
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2255
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:2246
uint64 SPI_processed
Definition: spi.c:39
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
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:2393
#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:213
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
static int pltcl_SPI_execute_plan ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

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

2632 {
2633  int my_rc;
2634  int spi_rc;
2635  int i;
2636  int j;
2637  int optIndex;
2638  Tcl_HashEntry *hashent;
2639  pltcl_query_desc *qdesc;
2640  const char *nulls = NULL;
2641  const char *arrayname = NULL;
2642  Tcl_Obj *loop_body = NULL;
2643  int count = 0;
2644  int callObjc;
2645  Tcl_Obj **callObjv = NULL;
2646  Datum *argvalues;
2647  MemoryContext oldcontext = CurrentMemoryContext;
2649  Tcl_HashTable *query_hash;
2650 
2651  enum options
2652  {
2653  OPT_ARRAY, OPT_COUNT, OPT_NULLS
2654  };
2655 
2656  static const char *options[] = {
2657  "-array", "-count", "-nulls", (const char *) NULL
2658  };
2659 
2660  /************************************************************
2661  * Get the options and check syntax
2662  ************************************************************/
2663  i = 1;
2664  while (i < objc)
2665  {
2666  if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2667  TCL_EXACT, &optIndex) != TCL_OK)
2668  break;
2669 
2670  if (++i >= objc)
2671  {
2672  Tcl_SetObjResult(interp,
2673  Tcl_NewStringObj("missing argument to -array, -count or -nulls", -1));
2674  return TCL_ERROR;
2675  }
2676 
2677  switch ((enum options) optIndex)
2678  {
2679  case OPT_ARRAY:
2680  arrayname = Tcl_GetString(objv[i++]);
2681  break;
2682 
2683  case OPT_COUNT:
2684  if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2685  return TCL_ERROR;
2686  break;
2687 
2688  case OPT_NULLS:
2689  nulls = Tcl_GetString(objv[i++]);
2690  break;
2691  }
2692  }
2693 
2694  /************************************************************
2695  * Get the prepared plan descriptor by its key
2696  ************************************************************/
2697  if (i >= objc)
2698  {
2699  Tcl_SetObjResult(interp,
2700  Tcl_NewStringObj("missing argument to -count or -array", -1));
2701  return TCL_ERROR;
2702  }
2703 
2705 
2706  hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
2707  if (hashent == NULL)
2708  {
2709  Tcl_AppendResult(interp, "invalid queryid '", Tcl_GetString(objv[i]), "'", NULL);
2710  return TCL_ERROR;
2711  }
2712  qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
2713  i++;
2714 
2715  /************************************************************
2716  * If a nulls string is given, check for correct length
2717  ************************************************************/
2718  if (nulls != NULL)
2719  {
2720  if (strlen(nulls) != qdesc->nargs)
2721  {
2722  Tcl_SetObjResult(interp,
2723  Tcl_NewStringObj(
2724  "length of nulls string doesn't match number of arguments",
2725  -1));
2726  return TCL_ERROR;
2727  }
2728  }
2729 
2730  /************************************************************
2731  * If there was a argtype list on preparation, we need
2732  * an argument value list now
2733  ************************************************************/
2734  if (qdesc->nargs > 0)
2735  {
2736  if (i >= objc)
2737  {
2738  Tcl_SetObjResult(interp,
2739  Tcl_NewStringObj(
2740  "argument list length doesn't match number of arguments for query"
2741  ,-1));
2742  return TCL_ERROR;
2743  }
2744 
2745  /************************************************************
2746  * Split the argument values
2747  ************************************************************/
2748  if (Tcl_ListObjGetElements(interp, objv[i++], &callObjc, &callObjv) != TCL_OK)
2749  return TCL_ERROR;
2750 
2751  /************************************************************
2752  * Check that the number of arguments matches
2753  ************************************************************/
2754  if (callObjc != qdesc->nargs)
2755  {
2756  Tcl_SetObjResult(interp,
2757  Tcl_NewStringObj(
2758  "argument list length doesn't match number of arguments for query"
2759  ,-1));
2760  return TCL_ERROR;
2761  }
2762  }
2763  else
2764  callObjc = 0;
2765 
2766  /************************************************************
2767  * Get loop body if present
2768  ************************************************************/
2769  if (i < objc)
2770  loop_body = objv[i++];
2771 
2772  if (i != objc)
2773  {
2774  Tcl_WrongNumArgs(interp, 1, objv,
2775  "?-count n? ?-array name? ?-nulls string? "
2776  "query ?args? ?loop body?");
2777  return TCL_ERROR;
2778  }
2779 
2780  /************************************************************
2781  * Execute the plan inside a sub-transaction, so we can cope with
2782  * errors sanely
2783  ************************************************************/
2784 
2785  pltcl_subtrans_begin(oldcontext, oldowner);
2786 
2787  PG_TRY();
2788  {
2789  /************************************************************
2790  * Setup the value array for SPI_execute_plan() using
2791  * the type specific input functions
2792  ************************************************************/
2793  argvalues = (Datum *) palloc(callObjc * sizeof(Datum));
2794 
2795  for (j = 0; j < callObjc; j++)
2796  {
2797  if (nulls && nulls[j] == 'n')
2798  {
2799  argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2800  NULL,
2801  qdesc->argtypioparams[j],
2802  -1);
2803  }
2804  else
2805  {
2806  UTF_BEGIN;
2807  argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2808  UTF_U2E(Tcl_GetString(callObjv[j])),
2809  qdesc->argtypioparams[j],
2810  -1);
2811  UTF_END;
2812  }
2813  }
2814 
2815  /************************************************************
2816  * Execute the plan
2817  ************************************************************/
2818  spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
2820  count);
2821 
2822  my_rc = pltcl_process_SPI_result(interp,
2823  arrayname,
2824  loop_body,
2825  spi_rc,
2826  SPI_tuptable,
2827  SPI_processed);
2828 
2829  pltcl_subtrans_commit(oldcontext, oldowner);
2830  }
2831  PG_CATCH();
2832  {
2833  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2834  return TCL_ERROR;
2835  }
2836  PG_END_TRY();
2837 
2838  return my_rc;
2839 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2237
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2255
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:2246
FmgrInfo * arginfuncs
Definition: pltcl.c:166
uint64 SPI_processed
Definition: spi.c:39
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
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:2393
SPIPlanPtr plan
Definition: pltcl.c:163
#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:213
Oid * argtypioparams
Definition: pltcl.c:167
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
static int pltcl_SPI_lastoid ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2847 of file pltcl.c.

References SPI_lastoid.

Referenced by pltcl_init_interp().

2849 {
2850  /*
2851  * Check call syntax
2852  */
2853  if (objc != 1)
2854  {
2855  Tcl_WrongNumArgs(interp, 1, objv, "");
2856  return TCL_ERROR;
2857  }
2858 
2859  Tcl_SetObjResult(interp, Tcl_NewWideIntObj(SPI_lastoid));
2860  return TCL_OK;
2861 }
Oid SPI_lastoid
Definition: spi.c:40
static int pltcl_SPI_prepare ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

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

2505 {
2506  volatile MemoryContext plan_cxt = NULL;
2507  int nargs;
2508  Tcl_Obj **argsObj;
2509  pltcl_query_desc *qdesc;
2510  int i;
2511  Tcl_HashEntry *hashent;
2512  int hashnew;
2513  Tcl_HashTable *query_hash;
2514  MemoryContext oldcontext = CurrentMemoryContext;
2516 
2517  /************************************************************
2518  * Check the call syntax
2519  ************************************************************/
2520  if (objc != 3)
2521  {
2522  Tcl_WrongNumArgs(interp, 1, objv, "query argtypes");
2523  return TCL_ERROR;
2524  }
2525 
2526  /************************************************************
2527  * Split the argument type list
2528  ************************************************************/
2529  if (Tcl_ListObjGetElements(interp, objv[2], &nargs, &argsObj) != TCL_OK)
2530  return TCL_ERROR;
2531 
2532  /************************************************************
2533  * Allocate the new querydesc structure
2534  *
2535  * struct qdesc and subsidiary data all live in plan_cxt. Note that if the
2536  * function is recompiled for whatever reason, permanent memory leaks
2537  * occur. FIXME someday.
2538  ************************************************************/
2540  "PL/Tcl spi_prepare query",
2542  MemoryContextSwitchTo(plan_cxt);
2543  qdesc = (pltcl_query_desc *) palloc0(sizeof(pltcl_query_desc));
2544  snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
2545  qdesc->nargs = nargs;
2546  qdesc->argtypes = (Oid *) palloc(nargs * sizeof(Oid));
2547  qdesc->arginfuncs = (FmgrInfo *) palloc(nargs * sizeof(FmgrInfo));
2548  qdesc->argtypioparams = (Oid *) palloc(nargs * sizeof(Oid));
2549  MemoryContextSwitchTo(oldcontext);
2550 
2551  /************************************************************
2552  * Execute the prepare inside a sub-transaction, so we can cope with
2553  * errors sanely
2554  ************************************************************/
2555 
2556  pltcl_subtrans_begin(oldcontext, oldowner);
2557 
2558  PG_TRY();
2559  {
2560  /************************************************************
2561  * Resolve argument type names and then look them up by oid
2562  * in the system cache, and remember the required information
2563  * for input conversion.
2564  ************************************************************/
2565  for (i = 0; i < nargs; i++)
2566  {
2567  Oid typId,
2568  typInput,
2569  typIOParam;
2570  int32 typmod;
2571 
2572  parseTypeString(Tcl_GetString(argsObj[i]), &typId, &typmod, false);
2573 
2574  getTypeInputInfo(typId, &typInput, &typIOParam);
2575 
2576  qdesc->argtypes[i] = typId;
2577  fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
2578  qdesc->argtypioparams[i] = typIOParam;
2579  }
2580 
2581  /************************************************************
2582  * Prepare the plan and check for errors
2583  ************************************************************/
2584  UTF_BEGIN;
2585  qdesc->plan = SPI_prepare(UTF_U2E(Tcl_GetString(objv[1])),
2586  nargs, qdesc->argtypes);
2587  UTF_END;
2588 
2589  if (qdesc->plan == NULL)
2590  elog(ERROR, "SPI_prepare() failed");
2591 
2592  /************************************************************
2593  * Save the plan into permanent memory (right now it's in the
2594  * SPI procCxt, which will go away at function end).
2595  ************************************************************/
2596  if (SPI_keepplan(qdesc->plan))
2597  elog(ERROR, "SPI_keepplan() failed");
2598 
2599  pltcl_subtrans_commit(oldcontext, oldowner);
2600  }
2601  PG_CATCH();
2602  {
2603  pltcl_subtrans_abort(interp, oldcontext, oldowner);
2604 
2605  MemoryContextDelete(plan_cxt);
2606 
2607  return TCL_ERROR;
2608  }
2609  PG_END_TRY();
2610 
2611  /************************************************************
2612  * Insert a hashtable entry for the plan and return
2613  * the key to the caller
2614  ************************************************************/
2616 
2617  hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
2618  Tcl_SetHashValue(hashent, (ClientData) qdesc);
2619 
2620  /* qname is ASCII, so no need for encoding conversion */
2621  Tcl_SetObjResult(interp, Tcl_NewStringObj(qdesc->qname, -1));
2622  return TCL_OK;
2623 }
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2237
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:2255
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:175
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:165
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2246
FmgrInfo * arginfuncs
Definition: pltcl.c:166
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:246
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:241
char qname[20]
Definition: pltcl.c:162
#define ERROR
Definition: elog.h:43
pltcl_interp_desc * interp_desc
Definition: pltcl.c:145
SPIPlanPtr plan
Definition: pltcl.c:163
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:2599
MemoryContext TopMemoryContext
Definition: mcxt.c:43
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:322
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:781
#define UTF_BEGIN
Definition: pltcl.c:85
#define PG_CATCH()
Definition: elog.h:293
pltcl_proc_desc * prodesc
Definition: pltcl.c:213
Oid * argtypioparams
Definition: pltcl.c:167
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
static void pltcl_subtrans_abort ( Tcl_Interp *  interp,
MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

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

2257 {
2258  ErrorData *edata;
2259 
2260  /* Save error info */
2261  MemoryContextSwitchTo(oldcontext);
2262  edata = CopyErrorData();
2263  FlushErrorState();
2264 
2265  /* Abort the inner transaction */
2267  MemoryContextSwitchTo(oldcontext);
2268  CurrentResourceOwner = oldowner;
2269 
2270  /* Pass the error data to Tcl */
2271  pltcl_construct_errorCode(interp, edata);
2272  UTF_BEGIN;
2273  Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
2274  UTF_END;
2275  FreeErrorData(edata);
2276 }
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:1805
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
static void pltcl_subtrans_begin ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2237 of file pltcl.c.

References BeginInternalSubTransaction(), and MemoryContextSwitchTo().

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

2238 {
2240 
2241  /* Want to run inside function's memory context */
2242  MemoryContextSwitchTo(oldcontext);
2243 }
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void BeginInternalSubTransaction(char *name)
Definition: xact.c:4171
static void pltcl_subtrans_commit ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2246 of file pltcl.c.

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

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

2247 {
2248  /* Commit the inner transaction, return to outer xact context */
2250  MemoryContextSwitchTo(oldcontext);
2251  CurrentResourceOwner = oldowner;
2252 }
ResourceOwner CurrentResourceOwner
Definition: resowner.c:138
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4242
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
static int pltcl_subtransaction ( ClientData  cdata,
Tcl_Interp *  interp,
int  objc,
Tcl_Obj *const  objv[] 
)
static

Definition at line 2871 of file pltcl.c.

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

Referenced by pltcl_init_interp().

2873 {
2874  MemoryContext oldcontext = CurrentMemoryContext;
2876  int retcode;
2877 
2878  if (objc != 2)
2879  {
2880  Tcl_WrongNumArgs(interp, 1, objv, "command");
2881  return TCL_ERROR;
2882  }
2883 
2884  /*
2885  * Note: we don't use pltcl_subtrans_begin and friends here because we
2886  * don't want the error handling in pltcl_subtrans_abort. But otherwise
2887  * the processing should be about the same as in those functions.
2888  */
2890  MemoryContextSwitchTo(oldcontext);
2891 
2892  retcode = Tcl_EvalObjEx(interp, objv[1], 0);
2893 
2894  if (retcode == TCL_ERROR)
2895  {
2896  /* Rollback the subtransaction */
2898  }
2899  else
2900  {
2901  /* Commit the subtransaction */
2903  }
2904 
2905  /* In either case, restore previous memory context and resource owner */
2906  MemoryContextSwitchTo(oldcontext);
2907  CurrentResourceOwner = oldowner;
2908 
2909  return retcode;
2910 }
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(char *name)
Definition: xact.c:4171
static HeapTuple pltcl_trigger_handler ( PG_FUNCTION_ARGS  ,
pltcl_call_state call_state,
bool  pltrusted 
)
static

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

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

Definition at line 380 of file pltcl.c.

Referenced by _PG_init().

381 {
382  return 0;
383 }
Datum pltclu_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 701 of file pltcl.c.

References pltcl_handler().

702 {
703  return pltcl_handler(fcinfo, false);
704 }
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:712
static void start_proc_error_callback ( void *  arg)
static

Definition at line 669 of file pltcl.c.

References errcontext.

Referenced by call_pltcl_start_proc().

670 {
671  const char *gucname = (const char *) arg;
672 
673  /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
674  errcontext("processing %s parameter", gucname);
675 }
#define errcontext
Definition: elog.h:164
void * arg
static void throw_tcl_error ( Tcl_Interp *  interp,
const char *  proname 
)
static

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

1330 {
1331  /*
1332  * Caution is needed here because Tcl_GetVar could overwrite the
1333  * interpreter result (even though it's not really supposed to), and we
1334  * can't control the order of evaluation of ereport arguments. Hence, make
1335  * real sure we have our own copy of the result string before invoking
1336  * Tcl_GetVar.
1337  */
1338  char *emsg;
1339  char *econtext;
1340 
1341  emsg = pstrdup(utf_u2e(Tcl_GetStringResult(interp)));
1342  econtext = utf_u2e(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
1343  ereport(ERROR,
1344  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1345  errmsg("%s", emsg),
1346  errcontext("%s\nin PL/Tcl function \"%s\"",
1347  econtext, proname)));
1348 }
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
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
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

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

Definition at line 252 of file pltcl.c.

PG_MODULE_MAGIC

Definition at line 42 of file pltcl.c.

pltcl_call_state* pltcl_current_call_state = NULL
static

Definition at line 241 of file pltcl.c.

Referenced by pltcl_handler(), and pltcl_returnnext().

Tcl_Interp* pltcl_hold_interp = NULL
static

Definition at line 236 of file pltcl.c.

Referenced by _PG_init(), and pltcl_init_interp().

HTAB* pltcl_interp_htab = NULL
static

Definition at line 237 of file pltcl.c.

bool pltcl_pm_init_done = false
static

Definition at line 235 of file pltcl.c.

Referenced by _PG_init().

HTAB* pltcl_proc_htab = NULL
static

Definition at line 238 of file pltcl.c.

char* pltcl_start_proc = NULL
static

Definition at line 233 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().

char* pltclu_start_proc = NULL
static

Definition at line 234 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().