PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 "parser/parse_func.h"
#include "parser/parse_type.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/guc.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 int Tcl_Size
 
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)
 
static void pltcl_init_interp (pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
 
static pltcl_interp_descpltcl_fetch_interp (Oid prolang, bool pltrusted)
 
static void call_pltcl_start_proc (Oid prolang, bool pltrusted)
 
static void start_proc_error_callback (void *arg)
 
static Datum pltcl_handler (PG_FUNCTION_ARGS, bool pltrusted)
 
static Datum pltcl_func_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static HeapTuple pltcl_trigger_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static void pltcl_event_trigger_handler (PG_FUNCTION_ARGS, pltcl_call_state *call_state, bool pltrusted)
 
static void throw_tcl_error (Tcl_Interp *interp, const char *proname)
 
static pltcl_proc_desccompile_pltcl_function (Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
 
static int pltcl_elog (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static void pltcl_construct_errorCode (Tcl_Interp *interp, ErrorData *edata)
 
static const char * pltcl_get_condition_name (int sqlstate)
 
static int pltcl_quote (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_argisnull (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_returnnull (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_returnnext (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_SPI_execute (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_process_SPI_result (Tcl_Interp *interp, const char *arrayname, Tcl_Obj *loop_body, int spi_rc, SPITupleTable *tuptable, uint64 ntuples)
 
static int pltcl_SPI_prepare (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_SPI_execute_plan (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_subtransaction (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_commit (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static int pltcl_rollback (ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
 
static void pltcl_subtrans_begin (MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_subtrans_commit (MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_subtrans_abort (Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
 
static void pltcl_set_tuple_values (Tcl_Interp *interp, const char *arrayname, uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
 
static Tcl_Obj * pltcl_build_tuple_argument (HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
 
static HeapTuple pltcl_build_tuple_result (Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
 
static void pltcl_init_tuple_store (pltcl_call_state *call_state)
 
static ClientData pltcl_InitNotifier (void)
 
static void pltcl_FinalizeNotifier (ClientData clientData)
 
static void pltcl_SetTimer (CONST86 Tcl_Time *timePtr)
 
static void pltcl_AlertNotifier (ClientData clientData)
 
static void pltcl_CreateFileHandler (int fd, int mask, Tcl_FileProc *proc, ClientData clientData)
 
static void pltcl_DeleteFileHandler (int fd)
 
static void pltcl_ServiceModeHook (int mode)
 
static int pltcl_WaitForEvent (CONST86 Tcl_Time *timePtr)
 
void _PG_init (void)
 
 PG_FUNCTION_INFO_V1 (pltcl_call_handler)
 
Datum pltcl_call_handler (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (pltclu_call_handler)
 
Datum pltclu_call_handler (PG_FUNCTION_ARGS)
 

Variables

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

Macro Definition Documentation

◆ CONST86

#define CONST86

Definition at line 55 of file pltcl.c.

◆ HAVE_TCL_VERSION

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

Definition at line 44 of file pltcl.c.

◆ TEXTDOMAIN

#define TEXTDOMAIN   PG_TEXTDOMAIN("pltcl")

Definition at line 64 of file pltcl.c.

◆ UTF_BEGIN

#define UTF_BEGIN
Value:
do { \
const char *_pltcl_utf_src = NULL; \
char *_pltcl_utf_dst = NULL

Definition at line 89 of file pltcl.c.

◆ UTF_E2U

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

Definition at line 102 of file pltcl.c.

◆ UTF_END

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

Definition at line 94 of file pltcl.c.

◆ UTF_U2E

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

Definition at line 99 of file pltcl.c.

Typedef Documentation

◆ pltcl_call_state

◆ pltcl_interp_desc

◆ pltcl_proc_desc

◆ pltcl_proc_key

◆ pltcl_proc_ptr

◆ pltcl_query_desc

◆ Tcl_Size

typedef int Tcl_Size

Definition at line 59 of file pltcl.c.

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 405 of file pltcl.c.

406{
407 Tcl_NotifierProcs notifier;
408 HASHCTL hash_ctl;
409
410 /* Be sure we do initialization only once (should be redundant now) */
412 return;
413
415
416#ifdef WIN32
417 /* Required on win32 to prevent error loading init.tcl */
418 Tcl_FindExecutable("");
419#endif
420
421 /*
422 * Override the functions in the Notifier subsystem. See comments above.
423 */
424 notifier.setTimerProc = pltcl_SetTimer;
425 notifier.waitForEventProc = pltcl_WaitForEvent;
426 notifier.createFileHandlerProc = pltcl_CreateFileHandler;
427 notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
428 notifier.initNotifierProc = pltcl_InitNotifier;
429 notifier.finalizeNotifierProc = pltcl_FinalizeNotifier;
430 notifier.alertNotifierProc = pltcl_AlertNotifier;
431 notifier.serviceModeHookProc = pltcl_ServiceModeHook;
432 Tcl_SetNotifier(&notifier);
433
434 /************************************************************
435 * Create the dummy hold interpreter to prevent close of
436 * stdout and stderr on DeleteInterp
437 ************************************************************/
438 if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
439 elog(ERROR, "could not create dummy Tcl interpreter");
440 if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
441 elog(ERROR, "could not initialize dummy Tcl interpreter");
442
443 /************************************************************
444 * Create the hash table for working interpreters
445 ************************************************************/
446 hash_ctl.keysize = sizeof(Oid);
447 hash_ctl.entrysize = sizeof(pltcl_interp_desc);
448 pltcl_interp_htab = hash_create("PL/Tcl interpreters",
449 8,
450 &hash_ctl,
452
453 /************************************************************
454 * Create the hash table for function lookup
455 ************************************************************/
456 hash_ctl.keysize = sizeof(pltcl_proc_key);
457 hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
458 pltcl_proc_htab = hash_create("PL/Tcl functions",
459 100,
460 &hash_ctl,
462
463 /************************************************************
464 * Define PL/Tcl's custom GUCs
465 ************************************************************/
466 DefineCustomStringVariable("pltcl.start_proc",
467 gettext_noop("PL/Tcl function to call once when pltcl is first used."),
468 NULL,
470 NULL,
471 PGC_SUSET, 0,
472 NULL, NULL, NULL);
473 DefineCustomStringVariable("pltclu.start_proc",
474 gettext_noop("PL/TclU function to call once when pltclu is first used."),
475 NULL,
477 NULL,
478 PGC_SUSET, 0,
479 NULL, NULL, NULL);
480
481 MarkGUCPrefixReserved("pltcl");
482 MarkGUCPrefixReserved("pltclu");
483
484 pltcl_pm_init_done = true;
485}
#define gettext_noop(x)
Definition: c.h:1150
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
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:5218
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5279
@ PGC_SUSET
Definition: guc.h:74
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
void pg_bindtextdomain(const char *domain)
Definition: miscinit.c:1936
static void pltcl_ServiceModeHook(int mode)
Definition: pltcl.c:385
static HTAB * pltcl_proc_htab
Definition: pltcl.c:247
static void pltcl_AlertNotifier(ClientData clientData)
Definition: pltcl.c:369
static int pltcl_WaitForEvent(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:390
static void pltcl_SetTimer(CONST86 Tcl_Time *timePtr)
Definition: pltcl.c:364
static void pltcl_DeleteFileHandler(int fd)
Definition: pltcl.c:380
static void pltcl_FinalizeNotifier(ClientData clientData)
Definition: pltcl.c:359
static void pltcl_CreateFileHandler(int fd, int mask, Tcl_FileProc *proc, ClientData clientData)
Definition: pltcl.c:374
static char * pltcl_start_proc
Definition: pltcl.c:242
static HTAB * pltcl_interp_htab
Definition: pltcl.c:246
struct pltcl_proc_key pltcl_proc_key
#define TEXTDOMAIN
Definition: pltcl.c:64
struct pltcl_interp_desc pltcl_interp_desc
static char * pltclu_start_proc
Definition: pltcl.c:243
static ClientData pltcl_InitNotifier(void)
Definition: pltcl.c:351
static Tcl_Interp * pltcl_hold_interp
Definition: pltcl.c:245
static bool pltcl_pm_init_done
Definition: pltcl.c:244
struct pltcl_proc_ptr pltcl_proc_ptr
unsigned int Oid
Definition: postgres_ext.h:31
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76

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

◆ call_pltcl_start_proc()

static void call_pltcl_start_proc ( Oid  prolang,
bool  pltrusted 
)
static

Definition at line 594 of file pltcl.c.

595{
596 LOCAL_FCINFO(fcinfo, 0);
597 char *start_proc;
598 const char *gucname;
599 ErrorContextCallback errcallback;
600 List *namelist;
601 Oid procOid;
602 HeapTuple procTup;
603 Form_pg_proc procStruct;
604 AclResult aclresult;
605 FmgrInfo finfo;
607
608 /* select appropriate GUC */
609 start_proc = pltrusted ? pltcl_start_proc : pltclu_start_proc;
610 gucname = pltrusted ? "pltcl.start_proc" : "pltclu.start_proc";
611
612 /* Nothing to do if it's empty or unset */
613 if (start_proc == NULL || start_proc[0] == '\0')
614 return;
615
616 /* Set up errcontext callback to make errors more helpful */
618 errcallback.arg = unconstify(char *, gucname);
619 errcallback.previous = error_context_stack;
620 error_context_stack = &errcallback;
621
622 /* Parse possibly-qualified identifier and look up the function */
623 namelist = stringToQualifiedNameList(start_proc, NULL);
624 procOid = LookupFuncName(namelist, 0, NULL, false);
625
626 /* Current user must have permission to call function */
627 aclresult = object_aclcheck(ProcedureRelationId, procOid, GetUserId(), ACL_EXECUTE);
628 if (aclresult != ACLCHECK_OK)
629 aclcheck_error(aclresult, OBJECT_FUNCTION, start_proc);
630
631 /* Get the function's pg_proc entry */
632 procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
633 if (!HeapTupleIsValid(procTup))
634 elog(ERROR, "cache lookup failed for function %u", procOid);
635 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
636
637 /* It must be same language as the function we're currently calling */
638 if (procStruct->prolang != prolang)
640 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
641 errmsg("function \"%s\" is in the wrong language",
642 start_proc)));
643
644 /*
645 * It must not be SECURITY DEFINER, either. This together with the
646 * language match check ensures that the function will execute in the same
647 * Tcl interpreter we just finished initializing.
648 */
649 if (procStruct->prosecdef)
651 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
652 errmsg("function \"%s\" must not be SECURITY DEFINER",
653 start_proc)));
654
655 /* A-OK */
656 ReleaseSysCache(procTup);
657
658 /*
659 * Call the function using the normal SQL function call mechanism. We
660 * could perhaps cheat and jump directly to pltcl_handler(), but it seems
661 * better to do it this way so that the call is exposed to, eg, call
662 * statistics collection.
663 */
665 fmgr_info(procOid, &finfo);
666 InitFunctionCallInfoData(*fcinfo, &finfo,
667 0,
668 InvalidOid, NULL, NULL);
669 pgstat_init_function_usage(fcinfo, &fcusage);
670 (void) FunctionCallInvoke(fcinfo);
671 pgstat_end_function_usage(&fcusage, true);
672
673 /* Pop the error context stack */
674 error_context_stack = errcallback.previous;
675}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3810
#define unconstify(underlying_type, expr)
Definition: c.h:1199
ErrorContextCallback * error_context_stack
Definition: elog.c:94
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
Oid GetUserId(void)
Definition: miscinit.c:517
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:213
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2144
@ OBJECT_FUNCTION
Definition: parsenodes.h:2287
#define ACL_EXECUTE
Definition: parsenodes.h:83
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
void pgstat_init_function_usage(FunctionCallInfo fcinfo, PgStat_FunctionCallUsage *fcu)
void pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
static void start_proc_error_callback(void *arg)
Definition: pltcl.c:681
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
List * stringToQualifiedNameList(const char *string, Node *escontext)
Definition: regproc.c:1797
struct ErrorContextCallback * previous
Definition: elog.h:296
void(* callback)(void *arg)
Definition: elog.h:297
Definition: fmgr.h:57
Definition: pg_list.h:54
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221

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

Referenced by pltcl_init_interp().

◆ compile_pltcl_function()

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

Definition at line 1414 of file pltcl.c.

1416{
1417 HeapTuple procTup;
1418 Form_pg_proc procStruct;
1419 pltcl_proc_key proc_key;
1420 pltcl_proc_ptr *proc_ptr;
1421 bool found;
1422 pltcl_proc_desc *prodesc;
1423 pltcl_proc_desc *old_prodesc;
1424 volatile MemoryContext proc_cxt = NULL;
1425 Tcl_DString proc_internal_def;
1426 Tcl_DString proc_internal_name;
1427 Tcl_DString proc_internal_body;
1428
1429 /* We'll need the pg_proc tuple in any case... */
1430 procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
1431 if (!HeapTupleIsValid(procTup))
1432 elog(ERROR, "cache lookup failed for function %u", fn_oid);
1433 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
1434
1435 /*
1436 * Look up function in pltcl_proc_htab; if it's not there, create an entry
1437 * and set the entry's proc_ptr to NULL.
1438 */
1439 proc_key.proc_id = fn_oid;
1440 proc_key.is_trigger = OidIsValid(tgreloid);
1441 proc_key.user_id = pltrusted ? GetUserId() : InvalidOid;
1442
1443 proc_ptr = hash_search(pltcl_proc_htab, &proc_key,
1444 HASH_ENTER,
1445 &found);
1446 if (!found)
1447 proc_ptr->proc_ptr = NULL;
1448
1449 prodesc = proc_ptr->proc_ptr;
1450
1451 /************************************************************
1452 * If it's present, must check whether it's still up to date.
1453 * This is needed because CREATE OR REPLACE FUNCTION can modify the
1454 * function's pg_proc entry without changing its OID.
1455 ************************************************************/
1456 if (prodesc != NULL &&
1457 prodesc->internal_proname != NULL &&
1458 prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
1459 ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
1460 {
1461 /* It's still up-to-date, so we can use it */
1462 ReleaseSysCache(procTup);
1463 return prodesc;
1464 }
1465
1466 /************************************************************
1467 * If we haven't found it in the hashtable, we analyze
1468 * the functions arguments and returntype and store
1469 * the in-/out-functions in the prodesc block and create
1470 * a new hashtable entry for it.
1471 *
1472 * Then we load the procedure into the Tcl interpreter.
1473 ************************************************************/
1474 Tcl_DStringInit(&proc_internal_def);
1475 Tcl_DStringInit(&proc_internal_name);
1476 Tcl_DStringInit(&proc_internal_body);
1477 PG_TRY();
1478 {
1479 bool is_trigger = OidIsValid(tgreloid);
1480 Tcl_CmdInfo cmdinfo;
1481 const char *user_proname;
1482 const char *internal_proname;
1483 bool need_underscore;
1484 HeapTuple typeTup;
1485 Form_pg_type typeStruct;
1486 char proc_internal_args[33 * FUNC_MAX_ARGS];
1487 Datum prosrcdatum;
1488 char *proc_source;
1489 char buf[48];
1490 pltcl_interp_desc *interp_desc;
1491 Tcl_Interp *interp;
1492 int i;
1493 int tcl_rc;
1494 MemoryContext oldcontext;
1495
1496 /************************************************************
1497 * Identify the interpreter to use for the function
1498 ************************************************************/
1499 interp_desc = pltcl_fetch_interp(procStruct->prolang, pltrusted);
1500 interp = interp_desc->interp;
1501
1502 /************************************************************
1503 * If redefining the function, try to remove the old internal
1504 * procedure from Tcl's namespace. The point of this is partly to
1505 * allow re-use of the same internal proc name, and partly to avoid
1506 * leaking the Tcl procedure object if we end up not choosing the same
1507 * name. We assume that Tcl is smart enough to not physically delete
1508 * the procedure object if it's currently being executed.
1509 ************************************************************/
1510 if (prodesc != NULL &&
1511 prodesc->internal_proname != NULL)
1512 {
1513 /* We simply ignore any error */
1514 (void) Tcl_DeleteCommand(interp, prodesc->internal_proname);
1515 /* Don't do this more than once */
1516 prodesc->internal_proname = NULL;
1517 }
1518
1519 /************************************************************
1520 * Build the proc name we'll use in error messages.
1521 ************************************************************/
1522 user_proname = format_procedure(fn_oid);
1523
1524 /************************************************************
1525 * Build the internal proc name from the user_proname and/or OID.
1526 * The internal name must be all-ASCII since we don't want to deal
1527 * with encoding conversions. We don't want to worry about Tcl
1528 * quoting rules either, so use only the characters of the function
1529 * name that are ASCII alphanumerics, plus underscores to separate
1530 * function name and arguments. If what we end up with isn't
1531 * unique (that is, it matches some existing Tcl command name),
1532 * append the function OID (perhaps repeatedly) so that it is unique.
1533 ************************************************************/
1534
1535 /* For historical reasons, use a function-type-specific prefix */
1536 if (is_event_trigger)
1537 Tcl_DStringAppend(&proc_internal_name,
1538 "__PLTcl_evttrigger_", -1);
1539 else if (is_trigger)
1540 Tcl_DStringAppend(&proc_internal_name,
1541 "__PLTcl_trigger_", -1);
1542 else
1543 Tcl_DStringAppend(&proc_internal_name,
1544 "__PLTcl_proc_", -1);
1545 /* Now add what we can from the user_proname */
1546 need_underscore = false;
1547 for (const char *ptr = user_proname; *ptr; ptr++)
1548 {
1549 if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1550 "abcdefghijklmnopqrstuvwxyz"
1551 "0123456789_", *ptr) != NULL)
1552 {
1553 /* Done this way to avoid adding a trailing underscore */
1554 if (need_underscore)
1555 {
1556 Tcl_DStringAppend(&proc_internal_name, "_", 1);
1557 need_underscore = false;
1558 }
1559 Tcl_DStringAppend(&proc_internal_name, ptr, 1);
1560 }
1561 else if (strchr("(, ", *ptr) != NULL)
1562 need_underscore = true;
1563 }
1564 /* If this name already exists, append fn_oid; repeat as needed */
1565 while (Tcl_GetCommandInfo(interp,
1566 Tcl_DStringValue(&proc_internal_name),
1567 &cmdinfo))
1568 {
1569 snprintf(buf, sizeof(buf), "_%u", fn_oid);
1570 Tcl_DStringAppend(&proc_internal_name, buf, -1);
1571 }
1572 internal_proname = Tcl_DStringValue(&proc_internal_name);
1573
1574 /************************************************************
1575 * Allocate a context that will hold all PG data for the procedure.
1576 ************************************************************/
1578 "PL/Tcl function",
1580
1581 /************************************************************
1582 * Allocate and fill a new procedure description block.
1583 * struct prodesc and subsidiary data must all live in proc_cxt.
1584 ************************************************************/
1585 oldcontext = MemoryContextSwitchTo(proc_cxt);
1586 prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
1587 prodesc->user_proname = pstrdup(user_proname);
1588 MemoryContextSetIdentifier(proc_cxt, prodesc->user_proname);
1589 prodesc->internal_proname = pstrdup(internal_proname);
1590 prodesc->fn_cxt = proc_cxt;
1591 prodesc->fn_refcount = 0;
1592 prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
1593 prodesc->fn_tid = procTup->t_self;
1594 prodesc->nargs = procStruct->pronargs;
1595 prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
1596 prodesc->arg_is_rowtype = (bool *) palloc0(prodesc->nargs * sizeof(bool));
1597 MemoryContextSwitchTo(oldcontext);
1598
1599 /* Remember if function is STABLE/IMMUTABLE */
1600 prodesc->fn_readonly =
1601 (procStruct->provolatile != PROVOLATILE_VOLATILE);
1602 /* And whether it is trusted */
1603 prodesc->lanpltrusted = pltrusted;
1604 /* Save the associated interpreter, too */
1605 prodesc->interp_desc = interp_desc;
1606
1607 /************************************************************
1608 * Get the required information for input conversion of the
1609 * return value.
1610 ************************************************************/
1611 if (!is_trigger && !is_event_trigger)
1612 {
1613 Oid rettype = procStruct->prorettype;
1614
1615 typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
1616 if (!HeapTupleIsValid(typeTup))
1617 elog(ERROR, "cache lookup failed for type %u", rettype);
1618 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1619
1620 /* Disallow pseudotype result, except VOID and RECORD */
1621 if (typeStruct->typtype == TYPTYPE_PSEUDO)
1622 {
1623 if (rettype == VOIDOID ||
1624 rettype == RECORDOID)
1625 /* okay */ ;
1626 else if (rettype == TRIGGEROID ||
1627 rettype == EVENT_TRIGGEROID)
1628 ereport(ERROR,
1629 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1630 errmsg("trigger functions can only be called as triggers")));
1631 else
1632 ereport(ERROR,
1633 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1634 errmsg("PL/Tcl functions cannot return type %s",
1635 format_type_be(rettype))));
1636 }
1637
1638 prodesc->result_typid = rettype;
1639 fmgr_info_cxt(typeStruct->typinput,
1640 &(prodesc->result_in_func),
1641 proc_cxt);
1642 prodesc->result_typioparam = getTypeIOParam(typeTup);
1643
1644 prodesc->fn_retisset = procStruct->proretset;
1645 prodesc->fn_retistuple = type_is_rowtype(rettype);
1646 prodesc->fn_retisdomain = (typeStruct->typtype == TYPTYPE_DOMAIN);
1647 prodesc->domain_info = NULL;
1648
1649 ReleaseSysCache(typeTup);
1650 }
1651
1652 /************************************************************
1653 * Get the required information for output conversion
1654 * of all procedure arguments, and set up argument naming info.
1655 ************************************************************/
1656 if (!is_trigger && !is_event_trigger)
1657 {
1658 proc_internal_args[0] = '\0';
1659 for (i = 0; i < prodesc->nargs; i++)
1660 {
1661 Oid argtype = procStruct->proargtypes.values[i];
1662
1663 typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
1664 if (!HeapTupleIsValid(typeTup))
1665 elog(ERROR, "cache lookup failed for type %u", argtype);
1666 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1667
1668 /* Disallow pseudotype argument, except RECORD */
1669 if (typeStruct->typtype == TYPTYPE_PSEUDO &&
1670 argtype != RECORDOID)
1671 ereport(ERROR,
1672 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1673 errmsg("PL/Tcl functions cannot accept type %s",
1674 format_type_be(argtype))));
1675
1676 if (type_is_rowtype(argtype))
1677 {
1678 prodesc->arg_is_rowtype[i] = true;
1679 snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
1680 }
1681 else
1682 {
1683 prodesc->arg_is_rowtype[i] = false;
1684 fmgr_info_cxt(typeStruct->typoutput,
1685 &(prodesc->arg_out_func[i]),
1686 proc_cxt);
1687 snprintf(buf, sizeof(buf), "%d", i + 1);
1688 }
1689
1690 if (i > 0)
1691 strcat(proc_internal_args, " ");
1692 strcat(proc_internal_args, buf);
1693
1694 ReleaseSysCache(typeTup);
1695 }
1696 }
1697 else if (is_trigger)
1698 {
1699 /* trigger procedure has fixed args */
1700 strcpy(proc_internal_args,
1701 "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");
1702 }
1703 else if (is_event_trigger)
1704 {
1705 /* event trigger procedure has fixed args */
1706 strcpy(proc_internal_args, "TG_event TG_tag");
1707 }
1708
1709 /************************************************************
1710 * Create the tcl command to define the internal
1711 * procedure
1712 *
1713 * Leave this code as DString - performance is not critical here,
1714 * and we don't want to duplicate the knowledge of the Tcl quoting
1715 * rules that's embedded in Tcl_DStringAppendElement.
1716 ************************************************************/
1717 Tcl_DStringAppendElement(&proc_internal_def, "proc");
1718 Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
1719 Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
1720
1721 /************************************************************
1722 * prefix procedure body with
1723 * upvar #0 <internal_proname> GD
1724 * and with appropriate setting of arguments
1725 ************************************************************/
1726 Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
1727 Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
1728 Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
1729 if (is_trigger)
1730 {
1731 Tcl_DStringAppend(&proc_internal_body,
1732 "array set NEW $__PLTcl_Tup_NEW\n", -1);
1733 Tcl_DStringAppend(&proc_internal_body,
1734 "array set OLD $__PLTcl_Tup_OLD\n", -1);
1735 Tcl_DStringAppend(&proc_internal_body,
1736 "set i 0\n"
1737 "set v 0\n"
1738 "foreach v $args {\n"
1739 " incr i\n"
1740 " set $i $v\n"
1741 "}\n"
1742 "unset i v\n\n", -1);
1743 }
1744 else if (is_event_trigger)
1745 {
1746 /* no argument support for event triggers */
1747 }
1748 else
1749 {
1750 for (i = 0; i < prodesc->nargs; i++)
1751 {
1752 if (prodesc->arg_is_rowtype[i])
1753 {
1754 snprintf(buf, sizeof(buf),
1755 "array set %d $__PLTcl_Tup_%d\n",
1756 i + 1, i + 1);
1757 Tcl_DStringAppend(&proc_internal_body, buf, -1);
1758 }
1759 }
1760 }
1761
1762 /************************************************************
1763 * Add user's function definition to proc body
1764 ************************************************************/
1765 prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
1766 Anum_pg_proc_prosrc);
1767 proc_source = TextDatumGetCString(prosrcdatum);
1768 UTF_BEGIN;
1769 Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
1770 UTF_END;
1771 pfree(proc_source);
1772 Tcl_DStringAppendElement(&proc_internal_def,
1773 Tcl_DStringValue(&proc_internal_body));
1774
1775 /************************************************************
1776 * Create the procedure in the interpreter
1777 ************************************************************/
1778 tcl_rc = Tcl_EvalEx(interp,
1779 Tcl_DStringValue(&proc_internal_def),
1780 Tcl_DStringLength(&proc_internal_def),
1781 TCL_EVAL_GLOBAL);
1782 if (tcl_rc != TCL_OK)
1783 ereport(ERROR,
1784 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1785 errmsg("could not create internal procedure \"%s\": %s",
1786 internal_proname,
1787 utf_u2e(Tcl_GetStringResult(interp)))));
1788 }
1789 PG_CATCH();
1790 {
1791 /*
1792 * If we failed anywhere above, clean up whatever got allocated. It
1793 * should all be in the proc_cxt, except for the DStrings.
1794 */
1795 if (proc_cxt)
1796 MemoryContextDelete(proc_cxt);
1797 Tcl_DStringFree(&proc_internal_def);
1798 Tcl_DStringFree(&proc_internal_name);
1799 Tcl_DStringFree(&proc_internal_body);
1800 PG_RE_THROW();
1801 }
1802 PG_END_TRY();
1803
1804 /*
1805 * Install the new proc description block in the hashtable, incrementing
1806 * its refcount (the hashtable link counts as a reference). Then, if
1807 * there was a previous definition of the function, decrement that one's
1808 * refcount, and delete it if no longer referenced. The order of
1809 * operations here is important: if something goes wrong during the
1810 * MemoryContextDelete, leaking some memory for the old definition is OK,
1811 * but we don't want to corrupt the live hashtable entry. (Likewise,
1812 * freeing the DStrings is pretty low priority if that happens.)
1813 */
1814 old_prodesc = proc_ptr->proc_ptr;
1815
1816 proc_ptr->proc_ptr = prodesc;
1817 prodesc->fn_refcount++;
1818
1819 if (old_prodesc != NULL)
1820 {
1821 Assert(old_prodesc->fn_refcount > 0);
1822 if (--old_prodesc->fn_refcount == 0)
1823 MemoryContextDelete(old_prodesc->fn_cxt);
1824 }
1825
1826 Tcl_DStringFree(&proc_internal_def);
1827 Tcl_DStringFree(&proc_internal_name);
1828 Tcl_DStringFree(&proc_internal_body);
1829
1830 ReleaseSysCache(procTup);
1831
1832 return prodesc;
1833}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define Assert(condition)
Definition: c.h:812
#define OidIsValid(objectId)
Definition: c.h:729
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
#define PG_RE_THROW()
Definition: elog.h:412
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define PG_CATCH(...)
Definition: elog.h:381
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
@ HASH_ENTER
Definition: hsearch.h:114
#define HeapTupleHeaderGetRawXmin(tup)
Definition: htup_details.h:304
int i
Definition: isn.c:72
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2655
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2303
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc0(Size size)
Definition: mcxt.c:1347
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
void MemoryContextSetIdentifier(MemoryContext context, const char *id)
Definition: mcxt.c:612
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define FUNC_MAX_ARGS
static char * buf
Definition: pg_test_fsync.c:72
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
#define UTF_E2U(x)
Definition: pltcl.c:102
#define UTF_END
Definition: pltcl.c:94
#define UTF_BEGIN
Definition: pltcl.c:89
static pltcl_interp_desc * pltcl_fetch_interp(Oid prolang, bool pltrusted)
Definition: pltcl.c:564
static char * utf_u2e(const char *src)
Definition: pltcl.c:78
#define snprintf
Definition: port.h:238
uintptr_t Datum
Definition: postgres.h:64
MemoryContextSwitchTo(old_ctx)
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
Tcl_Interp * interp
Definition: pltcl.c:119
FmgrInfo result_in_func
Definition: pltcl.c:153
Oid result_typid
Definition: pltcl.c:152
FmgrInfo * arg_out_func
Definition: pltcl.c:161
bool fn_retisdomain
Definition: pltcl.c:157
char * user_proname
Definition: pltcl.c:143
ItemPointerData fn_tid
Definition: pltcl.c:148
char * internal_proname
Definition: pltcl.c:144
pltcl_interp_desc * interp_desc
Definition: pltcl.c:151
bool fn_retisset
Definition: pltcl.c:155
unsigned long fn_refcount
Definition: pltcl.c:146
void * domain_info
Definition: pltcl.c:158
MemoryContext fn_cxt
Definition: pltcl.c:145
bool fn_readonly
Definition: pltcl.c:149
bool fn_retistuple
Definition: pltcl.c:156
Oid result_typioparam
Definition: pltcl.c:154
bool lanpltrusted
Definition: pltcl.c:150
bool * arg_is_rowtype
Definition: pltcl.c:162
TransactionId fn_xmin
Definition: pltcl.c:147
Oid is_trigger
Definition: pltcl.c:199
Oid user_id
Definition: pltcl.c:200
Oid proc_id
Definition: pltcl.c:193
pltcl_proc_desc * proc_ptr
Definition: pltcl.c:206
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631

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

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

◆ PG_FUNCTION_INFO_V1() [1/2]

PG_FUNCTION_INFO_V1 ( pltcl_call_handler  )

◆ PG_FUNCTION_INFO_V1() [2/2]

PG_FUNCTION_INFO_V1 ( pltclu_call_handler  )

◆ pltcl_AlertNotifier()

static void pltcl_AlertNotifier ( ClientData  clientData)
static

Definition at line 369 of file pltcl.c.

370{
371}

Referenced by _PG_init().

◆ pltcl_argisnull()

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

Definition at line 2144 of file pltcl.c.

2146{
2147 int argno;
2149
2150 /************************************************************
2151 * Check call syntax
2152 ************************************************************/
2153 if (objc != 2)
2154 {
2155 Tcl_WrongNumArgs(interp, 1, objv, "argno");
2156 return TCL_ERROR;
2157 }
2158
2159 /************************************************************
2160 * Check that we're called as a normal function
2161 ************************************************************/
2162 if (fcinfo == NULL)
2163 {
2164 Tcl_SetObjResult(interp,
2165 Tcl_NewStringObj("argisnull cannot be used in triggers", -1));
2166 return TCL_ERROR;
2167 }
2168
2169 /************************************************************
2170 * Get the argument number
2171 ************************************************************/
2172 if (Tcl_GetIntFromObj(interp, objv[1], &argno) != TCL_OK)
2173 return TCL_ERROR;
2174
2175 /************************************************************
2176 * Check that the argno is valid
2177 ************************************************************/
2178 argno--;
2179 if (argno < 0 || argno >= fcinfo->nargs)
2180 {
2181 Tcl_SetObjResult(interp,
2182 Tcl_NewStringObj("argno out of range", -1));
2183 return TCL_ERROR;
2184 }
2185
2186 /************************************************************
2187 * Get the requested NULL state
2188 ************************************************************/
2189 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(PG_ARGISNULL(argno)));
2190 return TCL_OK;
2191}
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
static pltcl_call_state * pltcl_current_call_state
Definition: pltcl.c:250
FunctionCallInfo fcinfo
Definition: pltcl.c:216

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

Referenced by pltcl_init_interp().

◆ pltcl_build_tuple_argument()

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

Definition at line 3185 of file pltcl.c.

3186{
3187 Tcl_Obj *retobj = Tcl_NewObj();
3188 int i;
3189 char *outputstr;
3190 Datum attr;
3191 bool isnull;
3192 char *attname;
3193 Oid typoutput;
3194 bool typisvarlena;
3195
3196 for (i = 0; i < tupdesc->natts; i++)
3197 {
3198 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3199
3200 /* ignore dropped attributes */
3201 if (att->attisdropped)
3202 continue;
3203
3204 if (att->attgenerated)
3205 {
3206 /* don't include unless requested */
3207 if (!include_generated)
3208 continue;
3209 }
3210
3211 /************************************************************
3212 * Get the attribute name
3213 ************************************************************/
3214 attname = NameStr(att->attname);
3215
3216 /************************************************************
3217 * Get the attributes value
3218 ************************************************************/
3219 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3220
3221 /************************************************************
3222 * If there is a value, append the attribute name and the
3223 * value to the list
3224 *
3225 * Hmmm - Null attributes will cause functions to
3226 * crash if they don't expect them - need something
3227 * smarter here.
3228 ************************************************************/
3229 if (!isnull)
3230 {
3231 getTypeOutputInfo(att->atttypid,
3232 &typoutput, &typisvarlena);
3233 outputstr = OidOutputFunctionCall(typoutput, attr);
3234 UTF_BEGIN;
3235 Tcl_ListObjAppendElement(NULL, retobj,
3236 Tcl_NewStringObj(UTF_E2U(attname), -1));
3237 UTF_END;
3238 UTF_BEGIN;
3239 Tcl_ListObjAppendElement(NULL, retobj,
3240 Tcl_NewStringObj(UTF_E2U(outputstr), -1));
3241 UTF_END;
3242 pfree(outputstr);
3243 }
3244 }
3245
3246 return retobj;
3247}
#define NameStr(name)
Definition: c.h:700
char * OidOutputFunctionCall(Oid functionId, Datum val)
Definition: fmgr.c:1763
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
NameData attname
Definition: pg_attribute.h:41
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:152

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

Referenced by pltcl_func_handler(), and pltcl_trigger_handler().

◆ pltcl_build_tuple_result()

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

Definition at line 3261 of file pltcl.c.

3263{
3264 HeapTuple tuple;
3265 TupleDesc tupdesc;
3266 AttInMetadata *attinmeta;
3267 char **values;
3268 int i;
3269
3270 if (call_state->ret_tupdesc)
3271 {
3272 tupdesc = call_state->ret_tupdesc;
3273 attinmeta = call_state->attinmeta;
3274 }
3275 else if (call_state->trigdata)
3276 {
3277 tupdesc = RelationGetDescr(call_state->trigdata->tg_relation);
3278 attinmeta = TupleDescGetAttInMetadata(tupdesc);
3279 }
3280 else
3281 {
3282 elog(ERROR, "PL/Tcl function does not return a tuple");
3283 tupdesc = NULL; /* keep compiler quiet */
3284 attinmeta = NULL;
3285 }
3286
3287 values = (char **) palloc0(tupdesc->natts * sizeof(char *));
3288
3289 if (kvObjc % 2 != 0)
3290 ereport(ERROR,
3291 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3292 errmsg("column name/value list must have even number of elements")));
3293
3294 for (i = 0; i < kvObjc; i += 2)
3295 {
3296 char *fieldName = utf_u2e(Tcl_GetString(kvObjv[i]));
3297 int attn = SPI_fnumber(tupdesc, fieldName);
3298
3299 /*
3300 * We silently ignore ".tupno", if it's present but doesn't match any
3301 * actual output column. This allows direct use of a row returned by
3302 * pltcl_set_tuple_values().
3303 */
3304 if (attn == SPI_ERROR_NOATTRIBUTE)
3305 {
3306 if (strcmp(fieldName, ".tupno") == 0)
3307 continue;
3308 ereport(ERROR,
3309 (errcode(ERRCODE_UNDEFINED_COLUMN),
3310 errmsg("column name/value list contains nonexistent column name \"%s\"",
3311 fieldName)));
3312 }
3313
3314 if (attn <= 0)
3315 ereport(ERROR,
3316 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3317 errmsg("cannot set system attribute \"%s\"",
3318 fieldName)));
3319
3320 if (TupleDescAttr(tupdesc, attn - 1)->attgenerated)
3321 ereport(ERROR,
3322 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3323 errmsg("cannot set generated column \"%s\"",
3324 fieldName)));
3325
3326 values[attn - 1] = utf_u2e(Tcl_GetString(kvObjv[i + 1]));
3327 }
3328
3329 tuple = BuildTupleFromCStrings(attinmeta, values);
3330
3331 /* if result type is domain-over-composite, check domain constraints */
3332 if (call_state->prodesc->fn_retisdomain)
3333 domain_check(HeapTupleGetDatum(tuple), false,
3334 call_state->prodesc->result_typid,
3335 &call_state->prodesc->domain_info,
3336 call_state->prodesc->fn_cxt);
3337
3338 return tuple;
3339}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:346
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:2322
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2273
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define RelationGetDescr(relation)
Definition: rel.h:531
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:1175
#define SPI_ERROR_NOATTRIBUTE
Definition: spi.h:76
Relation tg_relation
Definition: trigger.h:35
pltcl_proc_desc * prodesc
Definition: pltcl.c:222
TriggerData * trigdata
Definition: pltcl.c:219
AttInMetadata * attinmeta
Definition: pltcl.c:230
TupleDesc ret_tupdesc
Definition: pltcl.c:229

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

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

◆ pltcl_call_handler()

Datum pltcl_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 701 of file pltcl.c.

702{
703 return pltcl_handler(fcinfo, true);
704}
static Datum pltcl_handler(PG_FUNCTION_ARGS, bool pltrusted)
Definition: pltcl.c:724

References pltcl_handler().

◆ pltcl_commit()

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

Definition at line 3020 of file pltcl.c.

3022{
3024
3025 PG_TRY();
3026 {
3027 SPI_commit();
3028 }
3029 PG_CATCH();
3030 {
3031 ErrorData *edata;
3032
3033 /* Save error info */
3034 MemoryContextSwitchTo(oldcontext);
3035 edata = CopyErrorData();
3037
3038 /* Pass the error data to Tcl */
3039 pltcl_construct_errorCode(interp, edata);
3040 UTF_BEGIN;
3041 Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
3042 UTF_END;
3043 FreeErrorData(edata);
3044
3045 return TCL_ERROR;
3046 }
3047 PG_END_TRY();
3048
3049 return TCL_OK;
3050}
void FreeErrorData(ErrorData *edata)
Definition: elog.c:1818
ErrorData * CopyErrorData(void)
Definition: elog.c:1746
void FlushErrorState(void)
Definition: elog.c:1867
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
static void pltcl_construct_errorCode(Tcl_Interp *interp, ErrorData *edata)
Definition: pltcl.c:1927
void SPI_commit(void)
Definition: spi.c:320
char * message
Definition: elog.h:440

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

Referenced by pltcl_init_interp().

◆ pltcl_construct_errorCode()

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

Definition at line 1927 of file pltcl.c.

1928{
1929 Tcl_Obj *obj = Tcl_NewObj();
1930
1931 Tcl_ListObjAppendElement(interp, obj,
1932 Tcl_NewStringObj("POSTGRES", -1));
1933 Tcl_ListObjAppendElement(interp, obj,
1934 Tcl_NewStringObj(PG_VERSION, -1));
1935 Tcl_ListObjAppendElement(interp, obj,
1936 Tcl_NewStringObj("SQLSTATE", -1));
1937 Tcl_ListObjAppendElement(interp, obj,
1938 Tcl_NewStringObj(unpack_sql_state(edata->sqlerrcode), -1));
1939 Tcl_ListObjAppendElement(interp, obj,
1940 Tcl_NewStringObj("condition", -1));
1941 Tcl_ListObjAppendElement(interp, obj,
1942 Tcl_NewStringObj(pltcl_get_condition_name(edata->sqlerrcode), -1));
1943 Tcl_ListObjAppendElement(interp, obj,
1944 Tcl_NewStringObj("message", -1));
1945 UTF_BEGIN;
1946 Tcl_ListObjAppendElement(interp, obj,
1947 Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1948 UTF_END;
1949 if (edata->detail)
1950 {
1951 Tcl_ListObjAppendElement(interp, obj,
1952 Tcl_NewStringObj("detail", -1));
1953 UTF_BEGIN;
1954 Tcl_ListObjAppendElement(interp, obj,
1955 Tcl_NewStringObj(UTF_E2U(edata->detail), -1));
1956 UTF_END;
1957 }
1958 if (edata->hint)
1959 {
1960 Tcl_ListObjAppendElement(interp, obj,
1961 Tcl_NewStringObj("hint", -1));
1962 UTF_BEGIN;
1963 Tcl_ListObjAppendElement(interp, obj,
1964 Tcl_NewStringObj(UTF_E2U(edata->hint), -1));
1965 UTF_END;
1966 }
1967 if (edata->context)
1968 {
1969 Tcl_ListObjAppendElement(interp, obj,
1970 Tcl_NewStringObj("context", -1));
1971 UTF_BEGIN;
1972 Tcl_ListObjAppendElement(interp, obj,
1973 Tcl_NewStringObj(UTF_E2U(edata->context), -1));
1974 UTF_END;
1975 }
1976 if (edata->schema_name)
1977 {
1978 Tcl_ListObjAppendElement(interp, obj,
1979 Tcl_NewStringObj("schema", -1));
1980 UTF_BEGIN;
1981 Tcl_ListObjAppendElement(interp, obj,
1982 Tcl_NewStringObj(UTF_E2U(edata->schema_name), -1));
1983 UTF_END;
1984 }
1985 if (edata->table_name)
1986 {
1987 Tcl_ListObjAppendElement(interp, obj,
1988 Tcl_NewStringObj("table", -1));
1989 UTF_BEGIN;
1990 Tcl_ListObjAppendElement(interp, obj,
1991 Tcl_NewStringObj(UTF_E2U(edata->table_name), -1));
1992 UTF_END;
1993 }
1994 if (edata->column_name)
1995 {
1996 Tcl_ListObjAppendElement(interp, obj,
1997 Tcl_NewStringObj("column", -1));
1998 UTF_BEGIN;
1999 Tcl_ListObjAppendElement(interp, obj,
2000 Tcl_NewStringObj(UTF_E2U(edata->column_name), -1));
2001 UTF_END;
2002 }
2003 if (edata->datatype_name)
2004 {
2005 Tcl_ListObjAppendElement(interp, obj,
2006 Tcl_NewStringObj("datatype", -1));
2007 UTF_BEGIN;
2008 Tcl_ListObjAppendElement(interp, obj,
2009 Tcl_NewStringObj(UTF_E2U(edata->datatype_name), -1));
2010 UTF_END;
2011 }
2012 if (edata->constraint_name)
2013 {
2014 Tcl_ListObjAppendElement(interp, obj,
2015 Tcl_NewStringObj("constraint", -1));
2016 UTF_BEGIN;
2017 Tcl_ListObjAppendElement(interp, obj,
2018 Tcl_NewStringObj(UTF_E2U(edata->constraint_name), -1));
2019 UTF_END;
2020 }
2021 /* cursorpos is never interesting here; report internal query/pos */
2022 if (edata->internalquery)
2023 {
2024 Tcl_ListObjAppendElement(interp, obj,
2025 Tcl_NewStringObj("statement", -1));
2026 UTF_BEGIN;
2027 Tcl_ListObjAppendElement(interp, obj,
2028 Tcl_NewStringObj(UTF_E2U(edata->internalquery), -1));
2029 UTF_END;
2030 }
2031 if (edata->internalpos > 0)
2032 {
2033 Tcl_ListObjAppendElement(interp, obj,
2034 Tcl_NewStringObj("cursor_position", -1));
2035 Tcl_ListObjAppendElement(interp, obj,
2036 Tcl_NewIntObj(edata->internalpos));
2037 }
2038 if (edata->filename)
2039 {
2040 Tcl_ListObjAppendElement(interp, obj,
2041 Tcl_NewStringObj("filename", -1));
2042 UTF_BEGIN;
2043 Tcl_ListObjAppendElement(interp, obj,
2044 Tcl_NewStringObj(UTF_E2U(edata->filename), -1));
2045 UTF_END;
2046 }
2047 if (edata->lineno > 0)
2048 {
2049 Tcl_ListObjAppendElement(interp, obj,
2050 Tcl_NewStringObj("lineno", -1));
2051 Tcl_ListObjAppendElement(interp, obj,
2052 Tcl_NewIntObj(edata->lineno));
2053 }
2054 if (edata->funcname)
2055 {
2056 Tcl_ListObjAppendElement(interp, obj,
2057 Tcl_NewStringObj("funcname", -1));
2058 UTF_BEGIN;
2059 Tcl_ListObjAppendElement(interp, obj,
2060 Tcl_NewStringObj(UTF_E2U(edata->funcname), -1));
2061 UTF_END;
2062 }
2063
2064 Tcl_SetObjErrorCode(interp, obj);
2065}
char * unpack_sql_state(int sql_state)
Definition: elog.c:3169
static const char * pltcl_get_condition_name(int sqlstate)
Definition: pltcl.c:2072
int internalpos
Definition: elog.h:453
char * schema_name
Definition: elog.h:447
char * context
Definition: elog.h:444
char * internalquery
Definition: elog.h:454
int sqlerrcode
Definition: elog.h:439
const char * filename
Definition: elog.h:434
char * datatype_name
Definition: elog.h:450
char * detail
Definition: elog.h:441
const char * funcname
Definition: elog.h:436
char * table_name
Definition: elog.h:448
int lineno
Definition: elog.h:435
char * hint
Definition: elog.h:443
char * constraint_name
Definition: elog.h:451
char * column_name
Definition: elog.h:449

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

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

◆ pltcl_CreateFileHandler()

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

Definition at line 374 of file pltcl.c.

376{
377}

Referenced by _PG_init().

◆ pltcl_DeleteFileHandler()

static void pltcl_DeleteFileHandler ( int  fd)
static

Definition at line 380 of file pltcl.c.

381{
382}

Referenced by _PG_init().

◆ pltcl_elog()

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

Definition at line 1840 of file pltcl.c.

1842{
1843 volatile int level;
1844 MemoryContext oldcontext;
1845 int priIndex;
1846
1847 static const char *logpriorities[] = {
1848 "DEBUG", "LOG", "INFO", "NOTICE",
1849 "WARNING", "ERROR", "FATAL", (const char *) NULL
1850 };
1851
1852 static const int loglevels[] = {
1853 DEBUG2, LOG, INFO, NOTICE,
1855 };
1856
1857 if (objc != 3)
1858 {
1859 Tcl_WrongNumArgs(interp, 1, objv, "level msg");
1860 return TCL_ERROR;
1861 }
1862
1863 if (Tcl_GetIndexFromObj(interp, objv[1], logpriorities, "priority",
1864 TCL_EXACT, &priIndex) != TCL_OK)
1865 return TCL_ERROR;
1866
1867 level = loglevels[priIndex];
1868
1869 if (level == ERROR)
1870 {
1871 /*
1872 * We just pass the error back to Tcl. If it's not caught, it'll
1873 * eventually get converted to a PG error when we reach the call
1874 * handler.
1875 */
1876 Tcl_SetObjResult(interp, objv[2]);
1877 return TCL_ERROR;
1878 }
1879
1880 /*
1881 * For non-error messages, just pass 'em to ereport(). We do not expect
1882 * that this will fail, but just on the off chance it does, report the
1883 * error back to Tcl. Note we are assuming that ereport() can't have any
1884 * internal failures that are so bad as to require a transaction abort.
1885 *
1886 * This path is also used for FATAL errors, which aren't going to come
1887 * back to us at all.
1888 */
1889 oldcontext = CurrentMemoryContext;
1890 PG_TRY();
1891 {
1892 UTF_BEGIN;
1893 ereport(level,
1894 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1895 errmsg("%s", UTF_U2E(Tcl_GetString(objv[2])))));
1896 UTF_END;
1897 }
1898 PG_CATCH();
1899 {
1900 ErrorData *edata;
1901
1902 /* Must reset elog.c's state */
1903 MemoryContextSwitchTo(oldcontext);
1904 edata = CopyErrorData();
1906
1907 /* Pass the error data to Tcl */
1908 pltcl_construct_errorCode(interp, edata);
1909 UTF_BEGIN;
1910 Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
1911 UTF_END;
1912 FreeErrorData(edata);
1913
1914 return TCL_ERROR;
1915 }
1916 PG_END_TRY();
1917
1918 return TCL_OK;
1919}
#define LOG
Definition: elog.h:31
#define FATAL
Definition: elog.h:41
#define WARNING
Definition: elog.h:36
#define DEBUG2
Definition: elog.h:29
#define NOTICE
Definition: elog.h:35
#define INFO
Definition: elog.h:34
#define UTF_U2E(x)
Definition: pltcl.c:99

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

◆ pltcl_event_trigger_handler()

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

Definition at line 1315 of file pltcl.c.

1317{
1318 pltcl_proc_desc *prodesc;
1319 Tcl_Interp *volatile interp;
1320 EventTriggerData *tdata = (EventTriggerData *) fcinfo->context;
1321 Tcl_Obj *tcl_cmd;
1322 int tcl_rc;
1323
1324 /* Connect to SPI manager */
1325 SPI_connect();
1326
1327 /* Find or compile the function */
1328 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
1329 InvalidOid, true, pltrusted);
1330
1331 call_state->prodesc = prodesc;
1332 prodesc->fn_refcount++;
1333
1334 interp = prodesc->interp_desc->interp;
1335
1336 /* Create the tcl command and call the internal proc */
1337 tcl_cmd = Tcl_NewObj();
1338 Tcl_IncrRefCount(tcl_cmd);
1339 Tcl_ListObjAppendElement(NULL, tcl_cmd,
1340 Tcl_NewStringObj(prodesc->internal_proname, -1));
1341 Tcl_ListObjAppendElement(NULL, tcl_cmd,
1342 Tcl_NewStringObj(utf_e2u(tdata->event), -1));
1343 Tcl_ListObjAppendElement(NULL, tcl_cmd,
1344 Tcl_NewStringObj(utf_e2u(GetCommandTagName(tdata->tag)),
1345 -1));
1346
1347 tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
1348
1349 /* Release refcount to free tcl_cmd (and all subsidiary objects) */
1350 Tcl_DecrRefCount(tcl_cmd);
1351
1352 /* Check for errors reported by Tcl. */
1353 if (tcl_rc != TCL_OK)
1354 throw_tcl_error(interp, prodesc->user_proname);
1355
1356 if (SPI_finish() != SPI_OK_FINISH)
1357 elog(ERROR, "SPI_finish() failed");
1358}
const char * GetCommandTagName(CommandTag commandTag)
Definition: cmdtag.c:47
static char * utf_e2u(const char *src)
Definition: pltcl.c:84
static pltcl_proc_desc * compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool is_event_trigger, bool pltrusted)
Definition: pltcl.c:1414
static void throw_tcl_error(Tcl_Interp *interp, const char *proname)
Definition: pltcl.c:1369
int SPI_connect(void)
Definition: spi.c:94
int SPI_finish(void)
Definition: spi.c:182
#define SPI_OK_FINISH
Definition: spi.h:83
CommandTag tag
Definition: event_trigger.h:29
const char * event
Definition: event_trigger.h:27

References compile_pltcl_function(), elog, ERROR, EventTriggerData::event, pltcl_proc_desc::fn_refcount, GetCommandTagName(), 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_FINISH, EventTriggerData::tag, throw_tcl_error(), pltcl_proc_desc::user_proname, and utf_e2u().

Referenced by pltcl_handler().

◆ pltcl_fetch_interp()

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

Definition at line 564 of file pltcl.c.

565{
566 Oid user_id;
567 pltcl_interp_desc *interp_desc;
568 bool found;
569
570 /* Find or create the interpreter hashtable entry for this userid */
571 if (pltrusted)
572 user_id = GetUserId();
573 else
574 user_id = InvalidOid;
575
576 interp_desc = hash_search(pltcl_interp_htab, &user_id,
578 &found);
579 if (!found)
580 interp_desc->interp = NULL;
581
582 /* If we haven't yet successfully made an interpreter, try to do that */
583 if (!interp_desc->interp)
584 pltcl_init_interp(interp_desc, prolang, pltrusted);
585
586 return interp_desc;
587}
static void pltcl_init_interp(pltcl_interp_desc *interp_desc, Oid prolang, bool pltrusted)
Definition: pltcl.c:491

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

Referenced by compile_pltcl_function().

◆ pltcl_FinalizeNotifier()

static void pltcl_FinalizeNotifier ( ClientData  clientData)
static

Definition at line 359 of file pltcl.c.

360{
361}

Referenced by _PG_init().

◆ pltcl_func_handler()

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

Definition at line 798 of file pltcl.c.

800{
801 bool nonatomic;
802 pltcl_proc_desc *prodesc;
803 Tcl_Interp *volatile interp;
804 Tcl_Obj *tcl_cmd;
805 int i;
806 int tcl_rc;
807 Datum retval;
808
809 nonatomic = fcinfo->context &&
810 IsA(fcinfo->context, CallContext) &&
811 !castNode(CallContext, fcinfo->context)->atomic;
812
813 /* Connect to SPI manager */
814 SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
815
816 /* Find or compile the function */
817 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid,
818 false, pltrusted);
819
820 call_state->prodesc = prodesc;
821 prodesc->fn_refcount++;
822
823 interp = prodesc->interp_desc->interp;
824
825 /*
826 * If we're a SRF, check caller can handle materialize mode, and save
827 * relevant info into call_state. We must ensure that the returned
828 * tuplestore is owned by the caller's context, even if we first create it
829 * inside a subtransaction.
830 */
831 if (prodesc->fn_retisset)
832 {
833 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
834
835 if (!rsi || !IsA(rsi, ReturnSetInfo))
837 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
838 errmsg("set-valued function called in context that cannot accept a set")));
839
840 if (!(rsi->allowedModes & SFRM_Materialize))
842 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
843 errmsg("materialize mode required, but it is not allowed in this context")));
844
845 call_state->rsi = rsi;
848 }
849
850 /************************************************************
851 * Create the tcl command to call the internal
852 * proc in the Tcl interpreter
853 ************************************************************/
854 tcl_cmd = Tcl_NewObj();
855 Tcl_ListObjAppendElement(NULL, tcl_cmd,
856 Tcl_NewStringObj(prodesc->internal_proname, -1));
857
858 /* We hold a refcount on tcl_cmd just to be sure it stays around */
859 Tcl_IncrRefCount(tcl_cmd);
860
861 /************************************************************
862 * Add all call arguments to the command
863 ************************************************************/
864 PG_TRY();
865 {
866 for (i = 0; i < prodesc->nargs; i++)
867 {
868 if (prodesc->arg_is_rowtype[i])
869 {
870 /**************************************************
871 * For tuple values, add a list for 'array set ...'
872 **************************************************/
873 if (fcinfo->args[i].isnull)
874 Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
875 else
876 {
878 Oid tupType;
879 int32 tupTypmod;
880 TupleDesc tupdesc;
881 HeapTupleData tmptup;
882 Tcl_Obj *list_tmp;
883
884 td = DatumGetHeapTupleHeader(fcinfo->args[i].value);
885 /* Extract rowtype info and find a tupdesc */
886 tupType = HeapTupleHeaderGetTypeId(td);
887 tupTypmod = HeapTupleHeaderGetTypMod(td);
888 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
889 /* Build a temporary HeapTuple control structure */
891 tmptup.t_data = td;
892
893 list_tmp = pltcl_build_tuple_argument(&tmptup, tupdesc, true);
894 Tcl_ListObjAppendElement(NULL, tcl_cmd, list_tmp);
895
896 ReleaseTupleDesc(tupdesc);
897 }
898 }
899 else
900 {
901 /**************************************************
902 * Single values are added as string element
903 * of their external representation
904 **************************************************/
905 if (fcinfo->args[i].isnull)
906 Tcl_ListObjAppendElement(NULL, tcl_cmd, Tcl_NewObj());
907 else
908 {
909 char *tmp;
910
911 tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
912 fcinfo->args[i].value);
913 UTF_BEGIN;
914 Tcl_ListObjAppendElement(NULL, tcl_cmd,
915 Tcl_NewStringObj(UTF_E2U(tmp), -1));
916 UTF_END;
917 pfree(tmp);
918 }
919 }
920 }
921 }
922 PG_CATCH();
923 {
924 /* Release refcount to free tcl_cmd */
925 Tcl_DecrRefCount(tcl_cmd);
926 PG_RE_THROW();
927 }
928 PG_END_TRY();
929
930 /************************************************************
931 * Call the Tcl function
932 *
933 * We assume no PG error can be thrown directly from this call.
934 ************************************************************/
935 tcl_rc = Tcl_EvalObjEx(interp, tcl_cmd, (TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL));
936
937 /* Release refcount to free tcl_cmd (and all subsidiary objects) */
938 Tcl_DecrRefCount(tcl_cmd);
939
940 /************************************************************
941 * Check for errors reported by Tcl.
942 ************************************************************/
943 if (tcl_rc != TCL_OK)
944 throw_tcl_error(interp, prodesc->user_proname);
945
946 /************************************************************
947 * Disconnect from SPI manager and then create the return
948 * value datum (if the input function does a palloc for it
949 * this must not be allocated in the SPI memory context
950 * because SPI_finish would free it). But don't try to call
951 * the result_in_func if we've been told to return a NULL;
952 * the Tcl result may not be a valid value of the result type
953 * in that case.
954 ************************************************************/
955 if (SPI_finish() != SPI_OK_FINISH)
956 elog(ERROR, "SPI_finish() failed");
957
958 if (prodesc->fn_retisset)
959 {
960 ReturnSetInfo *rsi = call_state->rsi;
961
962 /* We already checked this is OK */
964
965 /* If we produced any tuples, send back the result */
966 if (call_state->tuple_store)
967 {
968 rsi->setResult = call_state->tuple_store;
969 if (call_state->ret_tupdesc)
970 {
971 MemoryContext oldcxt;
972
973 oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
974 rsi->setDesc = CreateTupleDescCopy(call_state->ret_tupdesc);
975 MemoryContextSwitchTo(oldcxt);
976 }
977 }
978 retval = (Datum) 0;
979 fcinfo->isnull = true;
980 }
981 else if (fcinfo->isnull)
982 {
983 retval = InputFunctionCall(&prodesc->result_in_func,
984 NULL,
985 prodesc->result_typioparam,
986 -1);
987 }
988 else if (prodesc->fn_retistuple)
989 {
990 TupleDesc td;
991 HeapTuple tup;
992 Tcl_Obj *resultObj;
993 Tcl_Obj **resultObjv;
994 Tcl_Size resultObjc;
995
996 /*
997 * Set up data about result type. XXX it's tempting to consider
998 * caching this in the prodesc, in the common case where the rowtype
999 * is determined by the function not the calling query. But we'd have
1000 * to be able to deal with ADD/DROP/ALTER COLUMN events when the
1001 * result type is a named composite type, so it's not exactly trivial.
1002 * Maybe worth improving someday.
1003 */
1004 switch (get_call_result_type(fcinfo, NULL, &td))
1005 {
1006 case TYPEFUNC_COMPOSITE:
1007 /* success */
1008 break;
1010 Assert(prodesc->fn_retisdomain);
1011 break;
1012 case TYPEFUNC_RECORD:
1013 /* failed to determine actual type of RECORD */
1014 ereport(ERROR,
1015 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1016 errmsg("function returning record called in context "
1017 "that cannot accept type record")));
1018 break;
1019 default:
1020 /* result type isn't composite? */
1021 elog(ERROR, "return type must be a row type");
1022 break;
1023 }
1024
1025 Assert(!call_state->ret_tupdesc);
1026 Assert(!call_state->attinmeta);
1027 call_state->ret_tupdesc = td;
1028 call_state->attinmeta = TupleDescGetAttInMetadata(td);
1029
1030 /* Convert function result to tuple */
1031 resultObj = Tcl_GetObjResult(interp);
1032 if (Tcl_ListObjGetElements(interp, resultObj, &resultObjc, &resultObjv) == TCL_ERROR)
1033 ereport(ERROR,
1034 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1035 errmsg("could not parse function return value: %s",
1036 utf_u2e(Tcl_GetStringResult(interp)))));
1037
1038 tup = pltcl_build_tuple_result(interp, resultObjv, resultObjc,
1039 call_state);
1040 retval = HeapTupleGetDatum(tup);
1041 }
1042 else
1043 retval = InputFunctionCall(&prodesc->result_in_func,
1044 utf_u2e(Tcl_GetStringResult(interp)),
1045 prodesc->result_typioparam,
1046 -1);
1047
1048 return retval;
1049}
int32_t int32
Definition: c.h:481
@ SFRM_Materialize
Definition: execnodes.h:320
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1530
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
@ TYPEFUNC_RECORD
Definition: funcapi.h:151
@ TYPEFUNC_COMPOSITE_DOMAIN
Definition: funcapi.h:150
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
static Tcl_Obj * pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, bool include_generated)
Definition: pltcl.c:3185
int Tcl_Size
Definition: pltcl.c:59
static HeapTuple pltcl_build_tuple_result(Tcl_Interp *interp, Tcl_Obj **kvObjv, int kvObjc, pltcl_call_state *call_state)
Definition: pltcl.c:3261
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
int SPI_connect_ext(int options)
Definition: spi.c:100
#define SPI_OPT_NONATOMIC
Definition: spi.h:102
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:265
uint32 t_len
Definition: htup.h:64
SetFunctionReturnMode returnMode
Definition: execnodes.h:339
ExprContext * econtext
Definition: execnodes.h:335
TupleDesc setDesc
Definition: execnodes.h:343
Tuplestorestate * setResult
Definition: execnodes.h:342
int allowedModes
Definition: execnodes.h:337
ReturnSetInfo * rsi
Definition: pltcl.c:232
MemoryContext tuple_store_cxt
Definition: pltcl.c:234
Tuplestorestate * tuple_store
Definition: pltcl.c:233
ResourceOwner tuple_store_owner
Definition: pltcl.c:235
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:235
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:213
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1920

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

Referenced by pltcl_handler().

◆ pltcl_get_condition_name()

static const char * pltcl_get_condition_name ( int  sqlstate)
static

Definition at line 2072 of file pltcl.c.

2073{
2074 int i;
2075
2076 for (i = 0; exception_name_map[i].label != NULL; i++)
2077 {
2078 if (exception_name_map[i].sqlerrstate == sqlstate)
2079 return exception_name_map[i].label;
2080 }
2081 return "unrecognized_sqlstate";
2082}
static const TclExceptionNameMap exception_name_map[]
Definition: pltcl.c:261
const char * label
Definition: pltcl.c:257

References exception_name_map, i, and TclExceptionNameMap::label.

Referenced by pltcl_construct_errorCode().

◆ pltcl_handler()

static Datum pltcl_handler ( PG_FUNCTION_ARGS  ,
bool  pltrusted 
)
static

Definition at line 724 of file pltcl.c.

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

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

Referenced by pltcl_call_handler(), and pltclu_call_handler().

◆ pltcl_init_interp()

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

Definition at line 491 of file pltcl.c.

492{
493 Tcl_Interp *interp;
494 char interpname[32];
495
496 /************************************************************
497 * Create the Tcl interpreter subsidiary to pltcl_hold_interp.
498 * Note: Tcl automatically does Tcl_Init in the untrusted case,
499 * and it's not wanted in the trusted case.
500 ************************************************************/
501 snprintf(interpname, sizeof(interpname), "subsidiary_%u", interp_desc->user_id);
502 if ((interp = Tcl_CreateSlave(pltcl_hold_interp, interpname,
503 pltrusted ? 1 : 0)) == NULL)
504 elog(ERROR, "could not create subsidiary Tcl interpreter");
505
506 /************************************************************
507 * Initialize the query hash table associated with interpreter
508 ************************************************************/
509 Tcl_InitHashTable(&interp_desc->query_hash, TCL_STRING_KEYS);
510
511 /************************************************************
512 * Install the commands for SPI support in the interpreter
513 ************************************************************/
514 Tcl_CreateObjCommand(interp, "elog",
515 pltcl_elog, NULL, NULL);
516 Tcl_CreateObjCommand(interp, "quote",
517 pltcl_quote, NULL, NULL);
518 Tcl_CreateObjCommand(interp, "argisnull",
519 pltcl_argisnull, NULL, NULL);
520 Tcl_CreateObjCommand(interp, "return_null",
521 pltcl_returnnull, NULL, NULL);
522 Tcl_CreateObjCommand(interp, "return_next",
523 pltcl_returnnext, NULL, NULL);
524 Tcl_CreateObjCommand(interp, "spi_exec",
525 pltcl_SPI_execute, NULL, NULL);
526 Tcl_CreateObjCommand(interp, "spi_prepare",
527 pltcl_SPI_prepare, NULL, NULL);
528 Tcl_CreateObjCommand(interp, "spi_execp",
529 pltcl_SPI_execute_plan, NULL, NULL);
530 Tcl_CreateObjCommand(interp, "subtransaction",
531 pltcl_subtransaction, NULL, NULL);
532 Tcl_CreateObjCommand(interp, "commit",
533 pltcl_commit, NULL, NULL);
534 Tcl_CreateObjCommand(interp, "rollback",
535 pltcl_rollback, NULL, NULL);
536
537 /************************************************************
538 * Call the appropriate start_proc, if there is one.
539 *
540 * We must set interp_desc->interp before the call, else the start_proc
541 * won't find the interpreter it's supposed to use. But, if the
542 * start_proc fails, we want to abandon use of the interpreter.
543 ************************************************************/
544 PG_TRY();
545 {
546 interp_desc->interp = interp;
547 call_pltcl_start_proc(prolang, pltrusted);
548 }
549 PG_CATCH();
550 {
551 interp_desc->interp = NULL;
552 Tcl_DeleteInterp(interp);
553 PG_RE_THROW();
554 }
555 PG_END_TRY();
556}
static void call_pltcl_start_proc(Oid prolang, bool pltrusted)
Definition: pltcl.c:594
static int pltcl_returnnext(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2236
static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2406
static int pltcl_elog(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:1840
static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2090
static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2628
static int pltcl_rollback(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:3059
static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2144
static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2198
static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2756
static int pltcl_subtransaction(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:2972
static int pltcl_commit(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
Definition: pltcl.c:3020
Tcl_HashTable query_hash
Definition: pltcl.c:120

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

Referenced by pltcl_fetch_interp().

◆ pltcl_init_tuple_store()

static void pltcl_init_tuple_store ( pltcl_call_state call_state)
static

Definition at line 3345 of file pltcl.c.

3346{
3347 ReturnSetInfo *rsi = call_state->rsi;
3348 MemoryContext oldcxt;
3349 ResourceOwner oldowner;
3350
3351 /* Should be in a SRF */
3352 Assert(rsi);
3353 /* Should be first time through */
3354 Assert(!call_state->tuple_store);
3355 Assert(!call_state->attinmeta);
3356
3357 /* We expect caller to provide an appropriate result tupdesc */
3358 Assert(rsi->expectedDesc);
3359 call_state->ret_tupdesc = rsi->expectedDesc;
3360
3361 /*
3362 * Switch to the right memory context and resource owner for storing the
3363 * tuplestore. If we're within a subtransaction opened for an exception
3364 * block, for example, we must still create the tuplestore in the resource
3365 * owner that was active when this function was entered, and not in the
3366 * subtransaction's resource owner.
3367 */
3368 oldcxt = MemoryContextSwitchTo(call_state->tuple_store_cxt);
3369 oldowner = CurrentResourceOwner;
3371
3372 call_state->tuple_store =
3374 false, work_mem);
3375
3376 /* Build attinmeta in this context, too */
3377 call_state->attinmeta = TupleDescGetAttInMetadata(call_state->ret_tupdesc);
3378
3379 CurrentResourceOwner = oldowner;
3380 MemoryContextSwitchTo(oldcxt);
3381}
@ SFRM_Materialize_Random
Definition: execnodes.h:321
int work_mem
Definition: globals.c:130
TupleDesc expectedDesc
Definition: execnodes.h:336
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330

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

◆ pltcl_InitNotifier()

static ClientData pltcl_InitNotifier ( void  )
static

Definition at line 351 of file pltcl.c.

352{
353 static int fakeThreadKey; /* To give valid address for ClientData */
354
355 return (ClientData) &(fakeThreadKey);
356}

Referenced by _PG_init().

◆ pltcl_process_SPI_result()

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

Definition at line 2515 of file pltcl.c.

2521{
2522 int my_rc = TCL_OK;
2523 int loop_rc;
2524 HeapTuple *tuples;
2525 TupleDesc tupdesc;
2526
2527 switch (spi_rc)
2528 {
2529 case SPI_OK_SELINTO:
2530 case SPI_OK_INSERT:
2531 case SPI_OK_DELETE:
2532 case SPI_OK_UPDATE:
2533 case SPI_OK_MERGE:
2534 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2535 break;
2536
2537 case SPI_OK_UTILITY:
2538 case SPI_OK_REWRITTEN:
2539 if (tuptable == NULL)
2540 {
2541 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
2542 break;
2543 }
2544 /* fall through for utility returning tuples */
2545 /* FALLTHROUGH */
2546
2547 case SPI_OK_SELECT:
2552
2553 /*
2554 * Process the tuples we got
2555 */
2556 tuples = tuptable->vals;
2557 tupdesc = tuptable->tupdesc;
2558
2559 if (loop_body == NULL)
2560 {
2561 /*
2562 * If there is no loop body given, just set the variables from
2563 * the first tuple (if any)
2564 */
2565 if (ntuples > 0)
2566 pltcl_set_tuple_values(interp, arrayname, 0,
2567 tuples[0], tupdesc);
2568 }
2569 else
2570 {
2571 /*
2572 * There is a loop body - process all tuples and evaluate the
2573 * body on each
2574 */
2575 uint64 i;
2576
2577 for (i = 0; i < ntuples; i++)
2578 {
2579 pltcl_set_tuple_values(interp, arrayname, i,
2580 tuples[i], tupdesc);
2581
2582 loop_rc = Tcl_EvalObjEx(interp, loop_body, 0);
2583
2584 if (loop_rc == TCL_OK)
2585 continue;
2586 if (loop_rc == TCL_CONTINUE)
2587 continue;
2588 if (loop_rc == TCL_RETURN)
2589 {
2590 my_rc = TCL_RETURN;
2591 break;
2592 }
2593 if (loop_rc == TCL_BREAK)
2594 break;
2595 my_rc = TCL_ERROR;
2596 break;
2597 }
2598 }
2599
2600 if (my_rc == TCL_OK)
2601 {
2602 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(ntuples));
2603 }
2604 break;
2605
2606 default:
2607 Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
2608 SPI_result_code_string(spi_rc), NULL);
2609 my_rc = TCL_ERROR;
2610 break;
2611 }
2612
2613 SPI_freetuptable(tuptable);
2614
2615 return my_rc;
2616}
uint64_t uint64
Definition: c.h:486
static void pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname, uint64 tupno, HeapTuple tuple, TupleDesc tupdesc)
Definition: pltcl.c:3099
const char * SPI_result_code_string(int code)
Definition: spi.c:1972
void SPI_freetuptable(SPITupleTable *tuptable)
Definition: spi.c:1386
#define SPI_OK_UTILITY
Definition: spi.h:85
#define SPI_OK_REWRITTEN
Definition: spi.h:95
#define SPI_OK_INSERT
Definition: spi.h:88
#define SPI_OK_UPDATE
Definition: spi.h:90
#define SPI_OK_MERGE
Definition: spi.h:99
#define SPI_OK_SELINTO
Definition: spi.h:87
#define SPI_OK_UPDATE_RETURNING
Definition: spi.h:94
#define SPI_OK_DELETE
Definition: spi.h:89
#define SPI_OK_INSERT_RETURNING
Definition: spi.h:92
#define SPI_OK_DELETE_RETURNING
Definition: spi.h:93
#define SPI_OK_MERGE_RETURNING
Definition: spi.h:100
#define SPI_OK_SELECT
Definition: spi.h:86
TupleDesc tupdesc
Definition: spi.h:25
HeapTuple * vals
Definition: spi.h:26

References i, pltcl_set_tuple_values(), SPI_freetuptable(), SPI_OK_DELETE, SPI_OK_DELETE_RETURNING, SPI_OK_INSERT, SPI_OK_INSERT_RETURNING, SPI_OK_MERGE, SPI_OK_MERGE_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().

◆ pltcl_quote()

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

Definition at line 2090 of file pltcl.c.

2092{
2093 char *tmp;
2094 const char *cp1;
2095 char *cp2;
2096 Tcl_Size length;
2097
2098 /************************************************************
2099 * Check call syntax
2100 ************************************************************/
2101 if (objc != 2)
2102 {
2103 Tcl_WrongNumArgs(interp, 1, objv, "string");
2104 return TCL_ERROR;
2105 }
2106
2107 /************************************************************
2108 * Allocate space for the maximum the string can
2109 * grow to and initialize pointers
2110 ************************************************************/
2111 cp1 = Tcl_GetStringFromObj(objv[1], &length);
2112 tmp = palloc(length * 2 + 1);
2113 cp2 = tmp;
2114
2115 /************************************************************
2116 * Walk through string and double every quote and backslash
2117 ************************************************************/
2118 while (*cp1)
2119 {
2120 if (*cp1 == '\'')
2121 *cp2++ = '\'';
2122 else
2123 {
2124 if (*cp1 == '\\')
2125 *cp2++ = '\\';
2126 }
2127 *cp2++ = *cp1++;
2128 }
2129
2130 /************************************************************
2131 * Terminate the string and set it as result
2132 ************************************************************/
2133 *cp2 = '\0';
2134 Tcl_SetObjResult(interp, Tcl_NewStringObj(tmp, -1));
2135 pfree(tmp);
2136 return TCL_OK;
2137}
void * palloc(Size size)
Definition: mcxt.c:1317

References palloc(), and pfree().

Referenced by pltcl_init_interp().

◆ pltcl_returnnext()

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

Definition at line 2236 of file pltcl.c.

2238{
2240 FunctionCallInfo fcinfo = call_state->fcinfo;
2241 pltcl_proc_desc *prodesc = call_state->prodesc;
2244 volatile int result = TCL_OK;
2245
2246 /*
2247 * Check that we're called as a set-returning function
2248 */
2249 if (fcinfo == NULL)
2250 {
2251 Tcl_SetObjResult(interp,
2252 Tcl_NewStringObj("return_next cannot be used in triggers", -1));
2253 return TCL_ERROR;
2254 }
2255
2256 if (!prodesc->fn_retisset)
2257 {
2258 Tcl_SetObjResult(interp,
2259 Tcl_NewStringObj("return_next cannot be used in non-set-returning functions", -1));
2260 return TCL_ERROR;
2261 }
2262
2263 /*
2264 * Check call syntax
2265 */
2266 if (objc != 2)
2267 {
2268 Tcl_WrongNumArgs(interp, 1, objv, "result");
2269 return TCL_ERROR;
2270 }
2271
2272 /*
2273 * The rest might throw elog(ERROR), so must run in a subtransaction.
2274 *
2275 * A small advantage of using a subtransaction is that it provides a
2276 * short-lived memory context for free, so we needn't worry about leaking
2277 * memory here. To use that context, call BeginInternalSubTransaction
2278 * directly instead of going through pltcl_subtrans_begin.
2279 */
2281 PG_TRY();
2282 {
2283 /* Set up tuple store if first output row */
2284 if (call_state->tuple_store == NULL)
2285 pltcl_init_tuple_store(call_state);
2286
2287 if (prodesc->fn_retistuple)
2288 {
2289 Tcl_Obj **rowObjv;
2290 Tcl_Size rowObjc;
2291
2292 /* result should be a list, so break it down */
2293 if (Tcl_ListObjGetElements(interp, objv[1], &rowObjc, &rowObjv) == TCL_ERROR)
2294 result = TCL_ERROR;
2295 else
2296 {
2297 HeapTuple tuple;
2298
2299 tuple = pltcl_build_tuple_result(interp, rowObjv, rowObjc,
2300 call_state);
2301 tuplestore_puttuple(call_state->tuple_store, tuple);
2302 }
2303 }
2304 else
2305 {
2306 Datum retval;
2307 bool isNull = false;
2308
2309 /* for paranoia's sake, check that tupdesc has exactly one column */
2310 if (call_state->ret_tupdesc->natts != 1)
2311 elog(ERROR, "wrong result type supplied in return_next");
2312
2313 retval = InputFunctionCall(&prodesc->result_in_func,
2314 utf_u2e((char *) Tcl_GetString(objv[1])),
2315 prodesc->result_typioparam,
2316 -1);
2317 tuplestore_putvalues(call_state->tuple_store, call_state->ret_tupdesc,
2318 &retval, &isNull);
2319 }
2320
2321 pltcl_subtrans_commit(oldcontext, oldowner);
2322 }
2323 PG_CATCH();
2324 {
2325 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2326 return TCL_ERROR;
2327 }
2328 PG_END_TRY();
2329
2330 return result;
2331}
static void pltcl_init_tuple_store(pltcl_call_state *call_state)
Definition: pltcl.c:3345
static void pltcl_subtrans_abort(Tcl_Interp *interp, MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2377
static void pltcl_subtrans_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2368
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:764
void BeginInternalSubTransaction(const char *name)
Definition: xact.c:4686

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

Referenced by pltcl_init_interp().

◆ pltcl_returnnull()

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

Definition at line 2198 of file pltcl.c.

2200{
2202
2203 /************************************************************
2204 * Check call syntax
2205 ************************************************************/
2206 if (objc != 1)
2207 {
2208 Tcl_WrongNumArgs(interp, 1, objv, "");
2209 return TCL_ERROR;
2210 }
2211
2212 /************************************************************
2213 * Check that we're called as a normal function
2214 ************************************************************/
2215 if (fcinfo == NULL)
2216 {
2217 Tcl_SetObjResult(interp,
2218 Tcl_NewStringObj("return_null cannot be used in triggers", -1));
2219 return TCL_ERROR;
2220 }
2221
2222 /************************************************************
2223 * Set the NULL return flag and cause Tcl to return from the
2224 * procedure.
2225 ************************************************************/
2226 fcinfo->isnull = true;
2227
2228 return TCL_RETURN;
2229}

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

Referenced by pltcl_init_interp().

◆ pltcl_rollback()

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

Definition at line 3059 of file pltcl.c.

3061{
3063
3064 PG_TRY();
3065 {
3066 SPI_rollback();
3067 }
3068 PG_CATCH();
3069 {
3070 ErrorData *edata;
3071
3072 /* Save error info */
3073 MemoryContextSwitchTo(oldcontext);
3074 edata = CopyErrorData();
3076
3077 /* Pass the error data to Tcl */
3078 pltcl_construct_errorCode(interp, edata);
3079 UTF_BEGIN;
3080 Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
3081 UTF_END;
3082 FreeErrorData(edata);
3083
3084 return TCL_ERROR;
3085 }
3086 PG_END_TRY();
3087
3088 return TCL_OK;
3089}
void SPI_rollback(void)
Definition: spi.c:413

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

Referenced by pltcl_init_interp().

◆ pltcl_ServiceModeHook()

static void pltcl_ServiceModeHook ( int  mode)
static

Definition at line 385 of file pltcl.c.

386{
387}

Referenced by _PG_init().

◆ pltcl_set_tuple_values()

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

Definition at line 3099 of file pltcl.c.

3101{
3102 int i;
3103 char *outputstr;
3104 Datum attr;
3105 bool isnull;
3106 const char *attname;
3107 Oid typoutput;
3108 bool typisvarlena;
3109 const char **arrptr;
3110 const char **nameptr;
3111 const char *nullname = NULL;
3112
3113 /************************************************************
3114 * Prepare pointers for Tcl_SetVar2Ex() below
3115 ************************************************************/
3116 if (arrayname == NULL)
3117 {
3118 arrptr = &attname;
3119 nameptr = &nullname;
3120 }
3121 else
3122 {
3123 arrptr = &arrayname;
3124 nameptr = &attname;
3125
3126 /*
3127 * When outputting to an array, fill the ".tupno" element with the
3128 * current tuple number. This will be overridden below if ".tupno" is
3129 * in use as an actual field name in the rowtype.
3130 */
3131 Tcl_SetVar2Ex(interp, arrayname, ".tupno", Tcl_NewWideIntObj(tupno), 0);
3132 }
3133
3134 for (i = 0; i < tupdesc->natts; i++)
3135 {
3136 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3137
3138 /* ignore dropped attributes */
3139 if (att->attisdropped)
3140 continue;
3141
3142 /************************************************************
3143 * Get the attribute name
3144 ************************************************************/
3145 UTF_BEGIN;
3146 attname = pstrdup(UTF_E2U(NameStr(att->attname)));
3147 UTF_END;
3148
3149 /************************************************************
3150 * Get the attributes value
3151 ************************************************************/
3152 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
3153
3154 /************************************************************
3155 * If there is a value, set the variable
3156 * If not, unset it
3157 *
3158 * Hmmm - Null attributes will cause functions to
3159 * crash if they don't expect them - need something
3160 * smarter here.
3161 ************************************************************/
3162 if (!isnull)
3163 {
3164 getTypeOutputInfo(att->atttypid, &typoutput, &typisvarlena);
3165 outputstr = OidOutputFunctionCall(typoutput, attr);
3166 UTF_BEGIN;
3167 Tcl_SetVar2Ex(interp, *arrptr, *nameptr,
3168 Tcl_NewStringObj(UTF_E2U(outputstr), -1), 0);
3169 UTF_END;
3170 pfree(outputstr);
3171 }
3172 else
3173 Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
3174
3175 pfree(unconstify(char *, attname));
3176 }
3177}

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

Referenced by pltcl_process_SPI_result().

◆ pltcl_SetTimer()

static void pltcl_SetTimer ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 364 of file pltcl.c.

365{
366}

Referenced by _PG_init().

◆ pltcl_SPI_execute()

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

Definition at line 2406 of file pltcl.c.

2408{
2409 int my_rc;
2410 int spi_rc;
2411 int query_idx;
2412 int i;
2413 int optIndex;
2414 int count = 0;
2415 const char *volatile arrayname = NULL;
2416 Tcl_Obj *volatile loop_body = NULL;
2419
2420 enum options
2421 {
2422 OPT_ARRAY, OPT_COUNT
2423 };
2424
2425 static const char *options[] = {
2426 "-array", "-count", (const char *) NULL
2427 };
2428
2429 /************************************************************
2430 * Check the call syntax and get the options
2431 ************************************************************/
2432 if (objc < 2)
2433 {
2434 Tcl_WrongNumArgs(interp, 1, objv,
2435 "?-count n? ?-array name? query ?loop body?");
2436 return TCL_ERROR;
2437 }
2438
2439 i = 1;
2440 while (i < objc)
2441 {
2442 if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2443 TCL_EXACT, &optIndex) != TCL_OK)
2444 break;
2445
2446 if (++i >= objc)
2447 {
2448 Tcl_SetObjResult(interp,
2449 Tcl_NewStringObj("missing argument to -count or -array", -1));
2450 return TCL_ERROR;
2451 }
2452
2453 switch ((enum options) optIndex)
2454 {
2455 case OPT_ARRAY:
2456 arrayname = Tcl_GetString(objv[i++]);
2457 break;
2458
2459 case OPT_COUNT:
2460 if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2461 return TCL_ERROR;
2462 break;
2463 }
2464 }
2465
2466 query_idx = i;
2467 if (query_idx >= objc || query_idx + 2 < objc)
2468 {
2469 Tcl_WrongNumArgs(interp, query_idx - 1, objv, "query ?loop body?");
2470 return TCL_ERROR;
2471 }
2472
2473 if (query_idx + 1 < objc)
2474 loop_body = objv[query_idx + 1];
2475
2476 /************************************************************
2477 * Execute the query inside a sub-transaction, so we can cope with
2478 * errors sanely
2479 ************************************************************/
2480
2481 pltcl_subtrans_begin(oldcontext, oldowner);
2482
2483 PG_TRY();
2484 {
2485 UTF_BEGIN;
2486 spi_rc = SPI_execute(UTF_U2E(Tcl_GetString(objv[query_idx])),
2488 UTF_END;
2489
2490 my_rc = pltcl_process_SPI_result(interp,
2491 arrayname,
2492 loop_body,
2493 spi_rc,
2496
2497 pltcl_subtrans_commit(oldcontext, oldowner);
2498 }
2499 PG_CATCH();
2500 {
2501 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2502 return TCL_ERROR;
2503 }
2504 PG_END_TRY();
2505
2506 return my_rc;
2507}
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:2515
static void pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition: pltcl.c:2359
uint64 SPI_processed
Definition: spi.c:44
SPITupleTable * SPI_tuptable
Definition: spi.c:45
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:596

References CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, PG_CATCH, PG_END_TRY, PG_TRY, pltcl_current_call_state, 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().

◆ pltcl_SPI_execute_plan()

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

Definition at line 2756 of file pltcl.c.

2758{
2759 int my_rc;
2760 int spi_rc;
2761 int i;
2762 int j;
2763 int optIndex;
2764 Tcl_HashEntry *hashent;
2765 pltcl_query_desc *qdesc;
2766 const char *nulls = NULL;
2767 const char *arrayname = NULL;
2768 Tcl_Obj *loop_body = NULL;
2769 int count = 0;
2770 Tcl_Size callObjc;
2771 Tcl_Obj **callObjv = NULL;
2772 Datum *argvalues;
2775 Tcl_HashTable *query_hash;
2776
2777 enum options
2778 {
2779 OPT_ARRAY, OPT_COUNT, OPT_NULLS
2780 };
2781
2782 static const char *options[] = {
2783 "-array", "-count", "-nulls", (const char *) NULL
2784 };
2785
2786 /************************************************************
2787 * Get the options and check syntax
2788 ************************************************************/
2789 i = 1;
2790 while (i < objc)
2791 {
2792 if (Tcl_GetIndexFromObj(NULL, objv[i], options, NULL,
2793 TCL_EXACT, &optIndex) != TCL_OK)
2794 break;
2795
2796 if (++i >= objc)
2797 {
2798 Tcl_SetObjResult(interp,
2799 Tcl_NewStringObj("missing argument to -array, -count or -nulls", -1));
2800 return TCL_ERROR;
2801 }
2802
2803 switch ((enum options) optIndex)
2804 {
2805 case OPT_ARRAY:
2806 arrayname = Tcl_GetString(objv[i++]);
2807 break;
2808
2809 case OPT_COUNT:
2810 if (Tcl_GetIntFromObj(interp, objv[i++], &count) != TCL_OK)
2811 return TCL_ERROR;
2812 break;
2813
2814 case OPT_NULLS:
2815 nulls = Tcl_GetString(objv[i++]);
2816 break;
2817 }
2818 }
2819
2820 /************************************************************
2821 * Get the prepared plan descriptor by its key
2822 ************************************************************/
2823 if (i >= objc)
2824 {
2825 Tcl_SetObjResult(interp,
2826 Tcl_NewStringObj("missing argument to -count or -array", -1));
2827 return TCL_ERROR;
2828 }
2829
2831
2832 hashent = Tcl_FindHashEntry(query_hash, Tcl_GetString(objv[i]));
2833 if (hashent == NULL)
2834 {
2835 Tcl_AppendResult(interp, "invalid queryid '", Tcl_GetString(objv[i]), "'", NULL);
2836 return TCL_ERROR;
2837 }
2838 qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
2839 i++;
2840
2841 /************************************************************
2842 * If a nulls string is given, check for correct length
2843 ************************************************************/
2844 if (nulls != NULL)
2845 {
2846 if (strlen(nulls) != qdesc->nargs)
2847 {
2848 Tcl_SetObjResult(interp,
2849 Tcl_NewStringObj("length of nulls string doesn't match number of arguments",
2850 -1));
2851 return TCL_ERROR;
2852 }
2853 }
2854
2855 /************************************************************
2856 * If there was an argtype list on preparation, we need
2857 * an argument value list now
2858 ************************************************************/
2859 if (qdesc->nargs > 0)
2860 {
2861 if (i >= objc)
2862 {
2863 Tcl_SetObjResult(interp,
2864 Tcl_NewStringObj("argument list length doesn't match number of arguments for query",
2865 -1));
2866 return TCL_ERROR;
2867 }
2868
2869 /************************************************************
2870 * Split the argument values
2871 ************************************************************/
2872 if (Tcl_ListObjGetElements(interp, objv[i++], &callObjc, &callObjv) != TCL_OK)
2873 return TCL_ERROR;
2874
2875 /************************************************************
2876 * Check that the number of arguments matches
2877 ************************************************************/
2878 if (callObjc != qdesc->nargs)
2879 {
2880 Tcl_SetObjResult(interp,
2881 Tcl_NewStringObj("argument list length doesn't match number of arguments for query",
2882 -1));
2883 return TCL_ERROR;
2884 }
2885 }
2886 else
2887 callObjc = 0;
2888
2889 /************************************************************
2890 * Get loop body if present
2891 ************************************************************/
2892 if (i < objc)
2893 loop_body = objv[i++];
2894
2895 if (i != objc)
2896 {
2897 Tcl_WrongNumArgs(interp, 1, objv,
2898 "?-count n? ?-array name? ?-nulls string? "
2899 "query ?args? ?loop body?");
2900 return TCL_ERROR;
2901 }
2902
2903 /************************************************************
2904 * Execute the plan inside a sub-transaction, so we can cope with
2905 * errors sanely
2906 ************************************************************/
2907
2908 pltcl_subtrans_begin(oldcontext, oldowner);
2909
2910 PG_TRY();
2911 {
2912 /************************************************************
2913 * Setup the value array for SPI_execute_plan() using
2914 * the type specific input functions
2915 ************************************************************/
2916 argvalues = (Datum *) palloc(callObjc * sizeof(Datum));
2917
2918 for (j = 0; j < callObjc; j++)
2919 {
2920 if (nulls && nulls[j] == 'n')
2921 {
2922 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2923 NULL,
2924 qdesc->argtypioparams[j],
2925 -1);
2926 }
2927 else
2928 {
2929 UTF_BEGIN;
2930 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2931 UTF_U2E(Tcl_GetString(callObjv[j])),
2932 qdesc->argtypioparams[j],
2933 -1);
2934 UTF_END;
2935 }
2936 }
2937
2938 /************************************************************
2939 * Execute the plan
2940 ************************************************************/
2941 spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls,
2943 count);
2944
2945 my_rc = pltcl_process_SPI_result(interp,
2946 arrayname,
2947 loop_body,
2948 spi_rc,
2951
2952 pltcl_subtrans_commit(oldcontext, oldowner);
2953 }
2954 PG_CATCH();
2955 {
2956 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2957 return TCL_ERROR;
2958 }
2959 PG_END_TRY();
2960
2961 return my_rc;
2962}
int j
Definition: isn.c:73
int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition: spi.c:672
Oid * argtypioparams
Definition: pltcl.c:176
SPIPlanPtr plan
Definition: pltcl.c:172
FmgrInfo * arginfuncs
Definition: pltcl.c:175

References pltcl_query_desc::arginfuncs, pltcl_query_desc::argtypioparams, CurrentMemoryContext, CurrentResourceOwner, pltcl_proc_desc::fn_readonly, i, InputFunctionCall(), pltcl_proc_desc::interp_desc, j, pltcl_query_desc::nargs, palloc(), PG_CATCH, PG_END_TRY, PG_TRY, pltcl_query_desc::plan, pltcl_current_call_state, 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().

◆ pltcl_SPI_prepare()

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

Definition at line 2628 of file pltcl.c.

2630{
2631 volatile MemoryContext plan_cxt = NULL;
2632 Tcl_Size nargs;
2633 Tcl_Obj **argsObj;
2634 pltcl_query_desc *qdesc;
2635 int i;
2636 Tcl_HashEntry *hashent;
2637 int hashnew;
2638 Tcl_HashTable *query_hash;
2641
2642 /************************************************************
2643 * Check the call syntax
2644 ************************************************************/
2645 if (objc != 3)
2646 {
2647 Tcl_WrongNumArgs(interp, 1, objv, "query argtypes");
2648 return TCL_ERROR;
2649 }
2650
2651 /************************************************************
2652 * Split the argument type list
2653 ************************************************************/
2654 if (Tcl_ListObjGetElements(interp, objv[2], &nargs, &argsObj) != TCL_OK)
2655 return TCL_ERROR;
2656
2657 /************************************************************
2658 * Allocate the new querydesc structure
2659 *
2660 * struct qdesc and subsidiary data all live in plan_cxt. Note that if the
2661 * function is recompiled for whatever reason, permanent memory leaks
2662 * occur. FIXME someday.
2663 ************************************************************/
2665 "PL/Tcl spi_prepare query",
2667 MemoryContextSwitchTo(plan_cxt);
2668 qdesc = (pltcl_query_desc *) palloc0(sizeof(pltcl_query_desc));
2669 snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
2670 qdesc->nargs = nargs;
2671 qdesc->argtypes = (Oid *) palloc(nargs * sizeof(Oid));
2672 qdesc->arginfuncs = (FmgrInfo *) palloc(nargs * sizeof(FmgrInfo));
2673 qdesc->argtypioparams = (Oid *) palloc(nargs * sizeof(Oid));
2674 MemoryContextSwitchTo(oldcontext);
2675
2676 /************************************************************
2677 * Execute the prepare inside a sub-transaction, so we can cope with
2678 * errors sanely
2679 ************************************************************/
2680
2681 pltcl_subtrans_begin(oldcontext, oldowner);
2682
2683 PG_TRY();
2684 {
2685 /************************************************************
2686 * Resolve argument type names and then look them up by oid
2687 * in the system cache, and remember the required information
2688 * for input conversion.
2689 ************************************************************/
2690 for (i = 0; i < nargs; i++)
2691 {
2692 Oid typId,
2693 typInput,
2694 typIOParam;
2695 int32 typmod;
2696
2697 (void) parseTypeString(Tcl_GetString(argsObj[i]),
2698 &typId, &typmod, NULL);
2699
2700 getTypeInputInfo(typId, &typInput, &typIOParam);
2701
2702 qdesc->argtypes[i] = typId;
2703 fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
2704 qdesc->argtypioparams[i] = typIOParam;
2705 }
2706
2707 /************************************************************
2708 * Prepare the plan and check for errors
2709 ************************************************************/
2710 UTF_BEGIN;
2711 qdesc->plan = SPI_prepare(UTF_U2E(Tcl_GetString(objv[1])),
2712 nargs, qdesc->argtypes);
2713 UTF_END;
2714
2715 if (qdesc->plan == NULL)
2716 elog(ERROR, "SPI_prepare() failed");
2717
2718 /************************************************************
2719 * Save the plan into permanent memory (right now it's in the
2720 * SPI procCxt, which will go away at function end).
2721 ************************************************************/
2722 if (SPI_keepplan(qdesc->plan))
2723 elog(ERROR, "SPI_keepplan() failed");
2724
2725 pltcl_subtrans_commit(oldcontext, oldowner);
2726 }
2727 PG_CATCH();
2728 {
2729 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2730
2731 MemoryContextDelete(plan_cxt);
2732
2733 return TCL_ERROR;
2734 }
2735 PG_END_TRY();
2736
2737 /************************************************************
2738 * Insert a hashtable entry for the plan and return
2739 * the key to the caller
2740 ************************************************************/
2742
2743 hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
2744 Tcl_SetHashValue(hashent, (ClientData) qdesc);
2745
2746 /* qname is ASCII, so no need for encoding conversion */
2747 Tcl_SetObjResult(interp, Tcl_NewStringObj(qdesc->qname, -1));
2748 return TCL_OK;
2749}
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
bool parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, Node *escontext)
Definition: parse_type.c:785
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:860
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:976
char qname[20]
Definition: pltcl.c:171
Oid * argtypes
Definition: pltcl.c:174

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

◆ pltcl_subtrans_abort()

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

Definition at line 2377 of file pltcl.c.

2379{
2380 ErrorData *edata;
2381
2382 /* Save error info */
2383 MemoryContextSwitchTo(oldcontext);
2384 edata = CopyErrorData();
2386
2387 /* Abort the inner transaction */
2389 MemoryContextSwitchTo(oldcontext);
2390 CurrentResourceOwner = oldowner;
2391
2392 /* Pass the error data to Tcl */
2393 pltcl_construct_errorCode(interp, edata);
2394 UTF_BEGIN;
2395 Tcl_SetObjResult(interp, Tcl_NewStringObj(UTF_E2U(edata->message), -1));
2396 UTF_END;
2397 FreeErrorData(edata);
2398}
void RollbackAndReleaseCurrentSubTransaction(void)
Definition: xact.c:4788

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

◆ pltcl_subtrans_begin()

static void pltcl_subtrans_begin ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2359 of file pltcl.c.

2360{
2362
2363 /* Want to run inside function's memory context */
2364 MemoryContextSwitchTo(oldcontext);
2365}

References BeginInternalSubTransaction(), and MemoryContextSwitchTo().

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

◆ pltcl_subtrans_commit()

static void pltcl_subtrans_commit ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
static

Definition at line 2368 of file pltcl.c.

2369{
2370 /* Commit the inner transaction, return to outer xact context */
2372 MemoryContextSwitchTo(oldcontext);
2373 CurrentResourceOwner = oldowner;
2374}
void ReleaseCurrentSubTransaction(void)
Definition: xact.c:4760

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

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

◆ pltcl_subtransaction()

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

Definition at line 2972 of file pltcl.c.

2974{
2977 int retcode;
2978
2979 if (objc != 2)
2980 {
2981 Tcl_WrongNumArgs(interp, 1, objv, "command");
2982 return TCL_ERROR;
2983 }
2984
2985 /*
2986 * Note: we don't use pltcl_subtrans_begin and friends here because we
2987 * don't want the error handling in pltcl_subtrans_abort. But otherwise
2988 * the processing should be about the same as in those functions.
2989 */
2991 MemoryContextSwitchTo(oldcontext);
2992
2993 retcode = Tcl_EvalObjEx(interp, objv[1], 0);
2994
2995 if (retcode == TCL_ERROR)
2996 {
2997 /* Rollback the subtransaction */
2999 }
3000 else
3001 {
3002 /* Commit the subtransaction */
3004 }
3005
3006 /* In either case, restore previous memory context and resource owner */
3007 MemoryContextSwitchTo(oldcontext);
3008 CurrentResourceOwner = oldowner;
3009
3010 return retcode;
3011}

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

Referenced by pltcl_init_interp().

◆ pltcl_trigger_handler()

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

Definition at line 1056 of file pltcl.c.

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

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

◆ pltcl_WaitForEvent()

static int pltcl_WaitForEvent ( CONST86 Tcl_Time *  timePtr)
static

Definition at line 390 of file pltcl.c.

391{
392 return 0;
393}

Referenced by _PG_init().

◆ pltclu_call_handler()

Datum pltclu_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 713 of file pltcl.c.

714{
715 return pltcl_handler(fcinfo, false);
716}

References pltcl_handler().

◆ start_proc_error_callback()

static void start_proc_error_callback ( void *  arg)
static

Definition at line 681 of file pltcl.c.

682{
683 const char *gucname = (const char *) arg;
684
685 /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
686 errcontext("processing %s parameter", gucname);
687}
#define errcontext
Definition: elog.h:196
void * arg

References arg, and errcontext.

Referenced by call_pltcl_start_proc().

◆ throw_tcl_error()

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

Definition at line 1369 of file pltcl.c.

1370{
1371 /*
1372 * Caution is needed here because Tcl_GetVar could overwrite the
1373 * interpreter result (even though it's not really supposed to), and we
1374 * can't control the order of evaluation of ereport arguments. Hence, make
1375 * real sure we have our own copy of the result string before invoking
1376 * Tcl_GetVar.
1377 */
1378 char *emsg;
1379 char *econtext;
1380 int emsglen;
1381
1382 emsg = pstrdup(utf_u2e(Tcl_GetStringResult(interp)));
1383 econtext = utf_u2e(Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY));
1384
1385 /*
1386 * Typically, the first line of errorInfo matches the primary error
1387 * message (the interpreter result); don't print that twice if so.
1388 */
1389 emsglen = strlen(emsg);
1390 if (strncmp(emsg, econtext, emsglen) == 0 &&
1391 econtext[emsglen] == '\n')
1392 econtext += emsglen + 1;
1393
1394 /* Tcl likes to prefix the next line with some spaces, too */
1395 while (*econtext == ' ')
1396 econtext++;
1397
1398 /* Note: proname will already contain quoting if any is needed */
1399 ereport(ERROR,
1400 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
1401 errmsg("%s", emsg),
1402 errcontext("%s\nin PL/Tcl function %s",
1403 econtext, proname)));
1404}
NameData proname
Definition: pg_proc.h:35

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

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

◆ utf_e2u()

static char * utf_e2u ( const char *  src)
inlinestatic

Definition at line 84 of file pltcl.c.

85{
86 return pg_server_to_any(src, strlen(src), PG_UTF8);
87}
char * pg_server_to_any(const char *s, int len, int encoding)
Definition: mbutils.c:749
@ PG_UTF8
Definition: pg_wchar.h:232

References pg_server_to_any(), and PG_UTF8.

Referenced by pltcl_event_trigger_handler(), and pltcl_trigger_handler().

◆ utf_u2e()

static char * utf_u2e ( const char *  src)
inlinestatic

Definition at line 78 of file pltcl.c.

79{
80 return pg_any_to_server(src, strlen(src), PG_UTF8);
81}
char * pg_any_to_server(const char *s, int len, int encoding)
Definition: mbutils.c:676

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

Variable Documentation

◆ exception_name_map

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

Definition at line 261 of file pltcl.c.

Referenced by pltcl_get_condition_name().

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 42 of file pltcl.c.

◆ pltcl_current_call_state

pltcl_call_state* pltcl_current_call_state = NULL
static

◆ pltcl_hold_interp

Tcl_Interp* pltcl_hold_interp = NULL
static

Definition at line 245 of file pltcl.c.

Referenced by _PG_init(), and pltcl_init_interp().

◆ pltcl_interp_htab

HTAB* pltcl_interp_htab = NULL
static

Definition at line 246 of file pltcl.c.

Referenced by _PG_init(), and pltcl_fetch_interp().

◆ pltcl_pm_init_done

bool pltcl_pm_init_done = false
static

Definition at line 244 of file pltcl.c.

Referenced by _PG_init().

◆ pltcl_proc_htab

HTAB* pltcl_proc_htab = NULL
static

Definition at line 247 of file pltcl.c.

Referenced by _PG_init(), and compile_pltcl_function().

◆ pltcl_start_proc

char* pltcl_start_proc = NULL
static

Definition at line 242 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().

◆ pltclu_start_proc

char* pltclu_start_proc = NULL
static

Definition at line 243 of file pltcl.c.

Referenced by _PG_init(), and call_pltcl_start_proc().