1 /**********************************************************************
2 * pltcl.c - PostgreSQL support for Tcl as
3 * procedural language (PL)
7 **********************************************************************/
16 /* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
21 #include "access/xact.h"
22 #include "catalog/pg_language.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_type.h"
25 #include "commands/trigger.h"
26 #include "executor/spi.h"
28 #include "miscadmin.h"
29 #include "nodes/makefuncs.h"
30 #include "parser/parse_type.h"
31 #include "tcop/tcopprot.h"
32 #include "utils/builtins.h"
33 #include "utils/lsyscache.h"
34 #include "utils/memutils.h"
35 #include "utils/syscache.h"
36 #include "utils/typcache.h"
39 #define HAVE_TCL_VERSION(maj,min) \
40 ((TCL_MAJOR_VERSION > maj) || \
41 (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
43 /* In Tcl >= 8.0, really not supposed to touch interp->result directly */
44 #if !HAVE_TCL_VERSION(8,0)
45 #define Tcl_GetStringResult(interp) ((interp)->result)
48 /* define our text domain for translations */
50 #define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
52 #if defined(UNICODE_CONVERSION) && HAVE_TCL_VERSION(8,1)
54 #include "mb/pg_wchar.h"
56 static unsigned char *
57 utf_u2e(unsigned char *src
)
59 return pg_do_encoding_conversion(src
, strlen(src
), PG_UTF8
, GetDatabaseEncoding());
62 static unsigned char *
63 utf_e2u(unsigned char *src
)
65 return pg_do_encoding_conversion(src
, strlen(src
), GetDatabaseEncoding(), PG_UTF8
);
69 #define UTF_BEGIN do { \
70 unsigned char *_pltcl_utf_src; \
71 unsigned char *_pltcl_utf_dst
72 #define UTF_END if (_pltcl_utf_src!=_pltcl_utf_dst) \
73 pfree(_pltcl_utf_dst); } while (0)
74 #define UTF_U2E(x) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
75 #define UTF_E2U(x) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
76 #else /* !PLTCL_UTF */
80 #define UTF_U2E(x) (x)
81 #define UTF_E2U(x) (x)
82 #endif /* PLTCL_UTF */
86 /**********************************************************************
87 * The information we cache about loaded procedures
88 **********************************************************************/
89 typedef struct pltcl_proc_desc
92 char *internal_proname
;
93 TransactionId fn_xmin
;
94 ItemPointerData fn_tid
;
97 FmgrInfo result_in_func
;
98 Oid result_typioparam
;
100 FmgrInfo arg_out_func
[FUNC_MAX_ARGS
];
101 bool arg_is_rowtype
[FUNC_MAX_ARGS
];
105 /**********************************************************************
106 * The information we cache about prepared and saved plans
107 **********************************************************************/
108 typedef struct pltcl_query_desc
114 FmgrInfo
*arginfuncs
;
119 /**********************************************************************
121 **********************************************************************/
122 static bool pltcl_pm_init_done
= false;
123 static bool pltcl_be_init_done
= false;
124 static Tcl_Interp
*pltcl_hold_interp
= NULL
;
125 static Tcl_Interp
*pltcl_norm_interp
= NULL
;
126 static Tcl_Interp
*pltcl_safe_interp
= NULL
;
127 static Tcl_HashTable
*pltcl_proc_hash
= NULL
;
128 static Tcl_HashTable
*pltcl_norm_query_hash
= NULL
;
129 static Tcl_HashTable
*pltcl_safe_query_hash
= NULL
;
131 /* these are saved and restored by pltcl_call_handler */
132 static FunctionCallInfo pltcl_current_fcinfo
= NULL
;
133 static pltcl_proc_desc
*pltcl_current_prodesc
= NULL
;
135 /**********************************************************************
136 * Forward declarations
137 **********************************************************************/
138 Datum
pltcl_call_handler(PG_FUNCTION_ARGS
);
139 Datum
pltclu_call_handler(PG_FUNCTION_ARGS
);
142 static void pltcl_init_all(void);
143 static void pltcl_init_interp(Tcl_Interp
*interp
);
144 static void pltcl_init_load_unknown(Tcl_Interp
*interp
);
146 static Datum
pltcl_func_handler(PG_FUNCTION_ARGS
);
148 static HeapTuple
pltcl_trigger_handler(PG_FUNCTION_ARGS
);
150 static void throw_tcl_error(Tcl_Interp
*interp
, const char *proname
);
152 static pltcl_proc_desc
*compile_pltcl_function(Oid fn_oid
, Oid tgreloid
);
154 static int pltcl_elog(ClientData cdata
, Tcl_Interp
*interp
,
155 int argc
, CONST84
char *argv
[]);
156 static int pltcl_quote(ClientData cdata
, Tcl_Interp
*interp
,
157 int argc
, CONST84
char *argv
[]);
158 static int pltcl_argisnull(ClientData cdata
, Tcl_Interp
*interp
,
159 int argc
, CONST84
char *argv
[]);
160 static int pltcl_returnnull(ClientData cdata
, Tcl_Interp
*interp
,
161 int argc
, CONST84
char *argv
[]);
163 static int pltcl_SPI_execute(ClientData cdata
, Tcl_Interp
*interp
,
164 int argc
, CONST84
char *argv
[]);
165 static int pltcl_process_SPI_result(Tcl_Interp
*interp
,
166 CONST84
char *arrayname
,
167 CONST84
char *loop_body
,
169 SPITupleTable
*tuptable
,
171 static int pltcl_SPI_prepare(ClientData cdata
, Tcl_Interp
*interp
,
172 int argc
, CONST84
char *argv
[]);
173 static int pltcl_SPI_execute_plan(ClientData cdata
, Tcl_Interp
*interp
,
174 int argc
, CONST84
char *argv
[]);
175 static int pltcl_SPI_lastoid(ClientData cdata
, Tcl_Interp
*interp
,
176 int argc
, CONST84
char *argv
[]);
178 static void pltcl_set_tuple_values(Tcl_Interp
*interp
, CONST84
char *arrayname
,
179 int tupno
, HeapTuple tuple
, TupleDesc tupdesc
);
180 static void pltcl_build_tuple_argument(HeapTuple tuple
, TupleDesc tupdesc
,
181 Tcl_DString
*retval
);
185 * Hack to override Tcl's builtin Notifier subsystem. This prevents the
186 * backend from becoming multithreaded, which breaks all sorts of things.
187 * That happens in the default version of Tcl_InitNotifier if the TCL library
188 * has been compiled with multithreading support (i.e. when TCL_THREADS is
189 * defined under Unix, and in all cases under Windows).
190 * It's okay to disable the notifier because we never enter the Tcl event loop
191 * from Postgres, so the notifier capabilities are initialized, but never
192 * used. Only InitNotifier and DeleteFileHandler ever seem to get called
193 * within Postgres, but we implement all the functions for completeness.
194 * We can only fix this with Tcl >= 8.4, when Tcl_SetNotifier() appeared.
196 #if HAVE_TCL_VERSION(8,4)
199 pltcl_InitNotifier(void)
201 static int fakeThreadKey
; /* To give valid address for ClientData */
203 return (ClientData
) &(fakeThreadKey
);
207 pltcl_FinalizeNotifier(ClientData clientData
)
212 pltcl_SetTimer(Tcl_Time
*timePtr
)
217 pltcl_AlertNotifier(ClientData clientData
)
222 pltcl_CreateFileHandler(int fd
, int mask
,
223 Tcl_FileProc
*proc
, ClientData clientData
)
228 pltcl_DeleteFileHandler(int fd
)
233 pltcl_ServiceModeHook(int mode
)
238 pltcl_WaitForEvent(Tcl_Time
*timePtr
)
242 #endif /* HAVE_TCL_VERSION(8,4) */
246 * This routine is a crock, and so is everyplace that calls it. The problem
247 * is that the cached form of pltcl functions/queries is allocated permanently
248 * (mostly via malloc()) and never released until backend exit. Subsidiary
249 * data structures such as fmgr info records therefore must live forever
250 * as well. A better implementation would store all this stuff in a per-
251 * function memory context that could be reclaimed at need. In the meantime,
252 * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
253 * it might allocate, and whatever the eventual function might allocate using
254 * fn_mcxt, will live forever too.
257 perm_fmgr_info(Oid functionId
, FmgrInfo
*finfo
)
259 fmgr_info_cxt(functionId
, finfo
, TopMemoryContext
);
263 * _PG_init() - library load-time initialization
265 * DO NOT make this static nor change its name!
270 /* Be sure we do initialization only once (should be redundant now) */
271 if (pltcl_pm_init_done
)
274 pg_bindtextdomain(TEXTDOMAIN
);
277 /* Required on win32 to prevent error loading init.tcl */
278 Tcl_FindExecutable("");
281 #if HAVE_TCL_VERSION(8,4)
284 * Override the functions in the Notifier subsystem. See comments above.
287 Tcl_NotifierProcs notifier
;
289 notifier
.setTimerProc
= pltcl_SetTimer
;
290 notifier
.waitForEventProc
= pltcl_WaitForEvent
;
291 notifier
.createFileHandlerProc
= pltcl_CreateFileHandler
;
292 notifier
.deleteFileHandlerProc
= pltcl_DeleteFileHandler
;
293 notifier
.initNotifierProc
= pltcl_InitNotifier
;
294 notifier
.finalizeNotifierProc
= pltcl_FinalizeNotifier
;
295 notifier
.alertNotifierProc
= pltcl_AlertNotifier
;
296 notifier
.serviceModeHookProc
= pltcl_ServiceModeHook
;
297 Tcl_SetNotifier(¬ifier
);
301 /************************************************************
302 * Create the dummy hold interpreter to prevent close of
303 * stdout and stderr on DeleteInterp
304 ************************************************************/
305 if ((pltcl_hold_interp
= Tcl_CreateInterp()) == NULL
)
306 elog(ERROR
, "could not create \"hold\" interpreter");
308 /************************************************************
309 * Create the two interpreters
310 ************************************************************/
311 if ((pltcl_norm_interp
=
312 Tcl_CreateSlave(pltcl_hold_interp
, "norm", 0)) == NULL
)
313 elog(ERROR
, "could not create \"normal\" interpreter");
314 pltcl_init_interp(pltcl_norm_interp
);
316 if ((pltcl_safe_interp
=
317 Tcl_CreateSlave(pltcl_hold_interp
, "safe", 1)) == NULL
)
318 elog(ERROR
, "could not create \"safe\" interpreter");
319 pltcl_init_interp(pltcl_safe_interp
);
321 /************************************************************
322 * Initialize the proc and query hash tables
323 ************************************************************/
324 pltcl_proc_hash
= (Tcl_HashTable
*) malloc(sizeof(Tcl_HashTable
));
325 pltcl_norm_query_hash
= (Tcl_HashTable
*) malloc(sizeof(Tcl_HashTable
));
326 pltcl_safe_query_hash
= (Tcl_HashTable
*) malloc(sizeof(Tcl_HashTable
));
327 Tcl_InitHashTable(pltcl_proc_hash
, TCL_STRING_KEYS
);
328 Tcl_InitHashTable(pltcl_norm_query_hash
, TCL_STRING_KEYS
);
329 Tcl_InitHashTable(pltcl_safe_query_hash
, TCL_STRING_KEYS
);
331 pltcl_pm_init_done
= true;
334 /**********************************************************************
335 * pltcl_init_all() - Initialize all
337 * This does initialization that can't be done in the postmaster, and
338 * hence is not safe to do at library load time.
339 **********************************************************************/
343 /************************************************************
344 * Try to load the unknown procedure from pltcl_modules
345 ************************************************************/
346 if (!pltcl_be_init_done
)
348 if (SPI_connect() != SPI_OK_CONNECT
)
349 elog(ERROR
, "SPI_connect failed");
350 pltcl_init_load_unknown(pltcl_norm_interp
);
351 pltcl_init_load_unknown(pltcl_safe_interp
);
352 if (SPI_finish() != SPI_OK_FINISH
)
353 elog(ERROR
, "SPI_finish failed");
354 pltcl_be_init_done
= true;
359 /**********************************************************************
360 * pltcl_init_interp() - initialize a Tcl interpreter
361 **********************************************************************/
363 pltcl_init_interp(Tcl_Interp
*interp
)
365 /************************************************************
366 * Install the commands for SPI support in the interpreter
367 ************************************************************/
368 Tcl_CreateCommand(interp
, "elog",
369 pltcl_elog
, NULL
, NULL
);
370 Tcl_CreateCommand(interp
, "quote",
371 pltcl_quote
, NULL
, NULL
);
372 Tcl_CreateCommand(interp
, "argisnull",
373 pltcl_argisnull
, NULL
, NULL
);
374 Tcl_CreateCommand(interp
, "return_null",
375 pltcl_returnnull
, NULL
, NULL
);
377 Tcl_CreateCommand(interp
, "spi_exec",
378 pltcl_SPI_execute
, NULL
, NULL
);
379 Tcl_CreateCommand(interp
, "spi_prepare",
380 pltcl_SPI_prepare
, NULL
, NULL
);
381 Tcl_CreateCommand(interp
, "spi_execp",
382 pltcl_SPI_execute_plan
, NULL
, NULL
);
383 Tcl_CreateCommand(interp
, "spi_lastoid",
384 pltcl_SPI_lastoid
, NULL
, NULL
);
388 /**********************************************************************
389 * pltcl_init_load_unknown() - Load the unknown procedure from
390 * table pltcl_modules (if it exists)
391 **********************************************************************/
393 pltcl_init_load_unknown(Tcl_Interp
*interp
)
397 Tcl_DString unknown_src
;
402 /************************************************************
403 * Check if table pltcl_modules exists
404 ************************************************************/
405 spi_rc
= SPI_execute("select 1 from pg_catalog.pg_class "
406 "where relname = 'pltcl_modules'",
408 SPI_freetuptable(SPI_tuptable
);
409 if (spi_rc
!= SPI_OK_SELECT
)
410 elog(ERROR
, "select from pg_class failed");
411 if (SPI_processed
== 0)
414 /************************************************************
415 * Read all the row's from it where modname = 'unknown' in
416 * the order of modseq
417 ************************************************************/
418 Tcl_DStringInit(&unknown_src
);
420 spi_rc
= SPI_execute("select modseq, modsrc from pltcl_modules "
421 "where modname = 'unknown' "
424 if (spi_rc
!= SPI_OK_SELECT
)
425 elog(ERROR
, "select from pltcl_modules failed");
427 /************************************************************
428 * If there's nothing, module unknown doesn't exist
429 ************************************************************/
430 if (SPI_processed
== 0)
432 Tcl_DStringFree(&unknown_src
);
433 SPI_freetuptable(SPI_tuptable
);
434 elog(WARNING
, "module \"unknown\" not found in pltcl_modules");
438 /************************************************************
439 * There is a module named unknown. Resemble the
440 * source from the modsrc attributes and evaluate
441 * it in the Tcl interpreter
442 ************************************************************/
443 fno
= SPI_fnumber(SPI_tuptable
->tupdesc
, "modsrc");
445 for (i
= 0; i
< SPI_processed
; i
++)
447 part
= SPI_getvalue(SPI_tuptable
->vals
[i
],
448 SPI_tuptable
->tupdesc
, fno
);
452 Tcl_DStringAppend(&unknown_src
, UTF_E2U(part
), -1);
457 tcl_rc
= Tcl_GlobalEval(interp
, Tcl_DStringValue(&unknown_src
));
458 Tcl_DStringFree(&unknown_src
);
459 SPI_freetuptable(SPI_tuptable
);
463 /**********************************************************************
464 * pltcl_call_handler - This is the only visible function
465 * of the PL interpreter. The PostgreSQL
466 * function manager and trigger manager
467 * call this function for execution of
469 **********************************************************************/
470 PG_FUNCTION_INFO_V1(pltcl_call_handler
);
472 /* keep non-static */
474 pltcl_call_handler(PG_FUNCTION_ARGS
)
477 FunctionCallInfo save_fcinfo
;
478 pltcl_proc_desc
*save_prodesc
;
481 * Initialize interpreters if first time through
486 * Ensure that static pointers are saved/restored properly
488 save_fcinfo
= pltcl_current_fcinfo
;
489 save_prodesc
= pltcl_current_prodesc
;
494 * Determine if called as function or trigger and call appropriate
497 if (CALLED_AS_TRIGGER(fcinfo
))
499 pltcl_current_fcinfo
= NULL
;
500 retval
= PointerGetDatum(pltcl_trigger_handler(fcinfo
));
504 pltcl_current_fcinfo
= fcinfo
;
505 retval
= pltcl_func_handler(fcinfo
);
510 pltcl_current_fcinfo
= save_fcinfo
;
511 pltcl_current_prodesc
= save_prodesc
;
516 pltcl_current_fcinfo
= save_fcinfo
;
517 pltcl_current_prodesc
= save_prodesc
;
524 * Alternative handler for unsafe functions
526 PG_FUNCTION_INFO_V1(pltclu_call_handler
);
528 /* keep non-static */
530 pltclu_call_handler(PG_FUNCTION_ARGS
)
532 return pltcl_call_handler(fcinfo
);
535 /**********************************************************************
536 * pltcl_func_handler() - Handler for regular function calls
537 **********************************************************************/
539 pltcl_func_handler(PG_FUNCTION_ARGS
)
541 pltcl_proc_desc
*prodesc
;
542 Tcl_Interp
*volatile interp
;
544 Tcl_DString list_tmp
;
549 /* Connect to SPI manager */
550 if (SPI_connect() != SPI_OK_CONNECT
)
551 elog(ERROR
, "could not connect to SPI manager");
553 /* Find or compile the function */
554 prodesc
= compile_pltcl_function(fcinfo
->flinfo
->fn_oid
, InvalidOid
);
556 pltcl_current_prodesc
= prodesc
;
558 if (prodesc
->lanpltrusted
)
559 interp
= pltcl_safe_interp
;
561 interp
= pltcl_norm_interp
;
563 /************************************************************
564 * Create the tcl command to call the internal
565 * proc in the Tcl interpreter
566 ************************************************************/
567 Tcl_DStringInit(&tcl_cmd
);
568 Tcl_DStringInit(&list_tmp
);
569 Tcl_DStringAppendElement(&tcl_cmd
, prodesc
->internal_proname
);
571 /************************************************************
572 * Add all call arguments to the command
573 ************************************************************/
576 for (i
= 0; i
< prodesc
->nargs
; i
++)
578 if (prodesc
->arg_is_rowtype
[i
])
580 /**************************************************
581 * For tuple values, add a list for 'array set ...'
582 **************************************************/
583 if (fcinfo
->argnull
[i
])
584 Tcl_DStringAppendElement(&tcl_cmd
, "");
591 HeapTupleData tmptup
;
593 td
= DatumGetHeapTupleHeader(fcinfo
->arg
[i
]);
594 /* Extract rowtype info and find a tupdesc */
595 tupType
= HeapTupleHeaderGetTypeId(td
);
596 tupTypmod
= HeapTupleHeaderGetTypMod(td
);
597 tupdesc
= lookup_rowtype_tupdesc(tupType
, tupTypmod
);
598 /* Build a temporary HeapTuple control structure */
599 tmptup
.t_len
= HeapTupleHeaderGetDatumLength(td
);
602 Tcl_DStringSetLength(&list_tmp
, 0);
603 pltcl_build_tuple_argument(&tmptup
, tupdesc
, &list_tmp
);
604 Tcl_DStringAppendElement(&tcl_cmd
,
605 Tcl_DStringValue(&list_tmp
));
606 ReleaseTupleDesc(tupdesc
);
611 /**************************************************
612 * Single values are added as string element
613 * of their external representation
614 **************************************************/
615 if (fcinfo
->argnull
[i
])
616 Tcl_DStringAppendElement(&tcl_cmd
, "");
621 tmp
= OutputFunctionCall(&prodesc
->arg_out_func
[i
],
624 Tcl_DStringAppendElement(&tcl_cmd
, UTF_E2U(tmp
));
633 Tcl_DStringFree(&tcl_cmd
);
634 Tcl_DStringFree(&list_tmp
);
638 Tcl_DStringFree(&list_tmp
);
640 /************************************************************
641 * Call the Tcl function
643 * We assume no PG error can be thrown directly from this call.
644 ************************************************************/
645 tcl_rc
= Tcl_GlobalEval(interp
, Tcl_DStringValue(&tcl_cmd
));
646 Tcl_DStringFree(&tcl_cmd
);
648 /************************************************************
649 * Check for errors reported by Tcl.
650 ************************************************************/
651 if (tcl_rc
!= TCL_OK
)
652 throw_tcl_error(interp
, prodesc
->user_proname
);
654 /************************************************************
655 * Disconnect from SPI manager and then create the return
656 * value datum (if the input function does a palloc for it
657 * this must not be allocated in the SPI memory context
658 * because SPI_finish would free it). But don't try to call
659 * the result_in_func if we've been told to return a NULL;
660 * the Tcl result may not be a valid value of the result type
662 ************************************************************/
663 if (SPI_finish() != SPI_OK_FINISH
)
664 elog(ERROR
, "SPI_finish() failed");
667 retval
= InputFunctionCall(&prodesc
->result_in_func
,
669 prodesc
->result_typioparam
,
674 retval
= InputFunctionCall(&prodesc
->result_in_func
,
675 UTF_U2E((char *) Tcl_GetStringResult(interp
)),
676 prodesc
->result_typioparam
,
685 /**********************************************************************
686 * pltcl_trigger_handler() - Handler for trigger calls
687 **********************************************************************/
689 pltcl_trigger_handler(PG_FUNCTION_ARGS
)
691 pltcl_proc_desc
*prodesc
;
692 Tcl_Interp
*volatile interp
;
693 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
696 volatile HeapTuple rettup
;
698 Tcl_DString tcl_trigtup
;
699 Tcl_DString tcl_newtup
;
706 CONST84
char *result
;
707 CONST84
char **ret_values
;
709 /* Connect to SPI manager */
710 if (SPI_connect() != SPI_OK_CONNECT
)
711 elog(ERROR
, "could not connect to SPI manager");
713 /* Find or compile the function */
714 prodesc
= compile_pltcl_function(fcinfo
->flinfo
->fn_oid
,
715 RelationGetRelid(trigdata
->tg_relation
));
717 pltcl_current_prodesc
= prodesc
;
719 if (prodesc
->lanpltrusted
)
720 interp
= pltcl_safe_interp
;
722 interp
= pltcl_norm_interp
;
724 tupdesc
= trigdata
->tg_relation
->rd_att
;
726 /************************************************************
727 * Create the tcl command to call the internal
728 * proc in the interpreter
729 ************************************************************/
730 Tcl_DStringInit(&tcl_cmd
);
731 Tcl_DStringInit(&tcl_trigtup
);
732 Tcl_DStringInit(&tcl_newtup
);
735 /* The procedure name */
736 Tcl_DStringAppendElement(&tcl_cmd
, prodesc
->internal_proname
);
738 /* The trigger name for argument TG_name */
739 Tcl_DStringAppendElement(&tcl_cmd
, trigdata
->tg_trigger
->tgname
);
741 /* The oid of the trigger relation for argument TG_relid */
742 stroid
= DatumGetCString(DirectFunctionCall1(oidout
,
743 ObjectIdGetDatum(trigdata
->tg_relation
->rd_id
)));
744 Tcl_DStringAppendElement(&tcl_cmd
, stroid
);
747 /* The name of the table the trigger is acting on: TG_table_name */
748 stroid
= SPI_getrelname(trigdata
->tg_relation
);
749 Tcl_DStringAppendElement(&tcl_cmd
, stroid
);
752 /* The schema of the table the trigger is acting on: TG_table_schema */
753 stroid
= SPI_getnspname(trigdata
->tg_relation
);
754 Tcl_DStringAppendElement(&tcl_cmd
, stroid
);
757 /* A list of attribute names for argument TG_relatts */
758 Tcl_DStringAppendElement(&tcl_trigtup
, "");
759 for (i
= 0; i
< tupdesc
->natts
; i
++)
761 if (tupdesc
->attrs
[i
]->attisdropped
)
762 Tcl_DStringAppendElement(&tcl_trigtup
, "");
764 Tcl_DStringAppendElement(&tcl_trigtup
,
765 NameStr(tupdesc
->attrs
[i
]->attname
));
767 Tcl_DStringAppendElement(&tcl_cmd
, Tcl_DStringValue(&tcl_trigtup
));
768 Tcl_DStringFree(&tcl_trigtup
);
769 Tcl_DStringInit(&tcl_trigtup
);
771 /* The when part of the event for TG_when */
772 if (TRIGGER_FIRED_BEFORE(trigdata
->tg_event
))
773 Tcl_DStringAppendElement(&tcl_cmd
, "BEFORE");
774 else if (TRIGGER_FIRED_AFTER(trigdata
->tg_event
))
775 Tcl_DStringAppendElement(&tcl_cmd
, "AFTER");
777 elog(ERROR
, "unrecognized WHEN tg_event: %u", trigdata
->tg_event
);
779 /* The level part of the event for TG_level */
780 if (TRIGGER_FIRED_FOR_ROW(trigdata
->tg_event
))
782 Tcl_DStringAppendElement(&tcl_cmd
, "ROW");
784 /* Build the data list for the trigtuple */
785 pltcl_build_tuple_argument(trigdata
->tg_trigtuple
,
786 tupdesc
, &tcl_trigtup
);
789 * Now the command part of the event for TG_op and data for NEW
792 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
794 Tcl_DStringAppendElement(&tcl_cmd
, "INSERT");
796 Tcl_DStringAppendElement(&tcl_cmd
, Tcl_DStringValue(&tcl_trigtup
));
797 Tcl_DStringAppendElement(&tcl_cmd
, "");
799 rettup
= trigdata
->tg_trigtuple
;
801 else if (TRIGGER_FIRED_BY_DELETE(trigdata
->tg_event
))
803 Tcl_DStringAppendElement(&tcl_cmd
, "DELETE");
805 Tcl_DStringAppendElement(&tcl_cmd
, "");
806 Tcl_DStringAppendElement(&tcl_cmd
, Tcl_DStringValue(&tcl_trigtup
));
808 rettup
= trigdata
->tg_trigtuple
;
810 else if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
812 Tcl_DStringAppendElement(&tcl_cmd
, "UPDATE");
814 pltcl_build_tuple_argument(trigdata
->tg_newtuple
,
815 tupdesc
, &tcl_newtup
);
817 Tcl_DStringAppendElement(&tcl_cmd
, Tcl_DStringValue(&tcl_newtup
));
818 Tcl_DStringAppendElement(&tcl_cmd
, Tcl_DStringValue(&tcl_trigtup
));
820 rettup
= trigdata
->tg_newtuple
;
823 elog(ERROR
, "unrecognized OP tg_event: %u", trigdata
->tg_event
);
825 else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata
->tg_event
))
827 Tcl_DStringAppendElement(&tcl_cmd
, "STATEMENT");
829 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
830 Tcl_DStringAppendElement(&tcl_cmd
, "INSERT");
831 else if (TRIGGER_FIRED_BY_DELETE(trigdata
->tg_event
))
832 Tcl_DStringAppendElement(&tcl_cmd
, "DELETE");
833 else if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
834 Tcl_DStringAppendElement(&tcl_cmd
, "UPDATE");
835 else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata
->tg_event
))
836 Tcl_DStringAppendElement(&tcl_cmd
, "TRUNCATE");
838 elog(ERROR
, "unrecognized OP tg_event: %u", trigdata
->tg_event
);
840 Tcl_DStringAppendElement(&tcl_cmd
, "");
841 Tcl_DStringAppendElement(&tcl_cmd
, "");
843 rettup
= (HeapTuple
) NULL
;
846 elog(ERROR
, "unrecognized LEVEL tg_event: %u", trigdata
->tg_event
);
848 /* Finally append the arguments from CREATE TRIGGER */
849 for (i
= 0; i
< trigdata
->tg_trigger
->tgnargs
; i
++)
850 Tcl_DStringAppendElement(&tcl_cmd
, trigdata
->tg_trigger
->tgargs
[i
]);
855 Tcl_DStringFree(&tcl_cmd
);
856 Tcl_DStringFree(&tcl_trigtup
);
857 Tcl_DStringFree(&tcl_newtup
);
861 Tcl_DStringFree(&tcl_trigtup
);
862 Tcl_DStringFree(&tcl_newtup
);
864 /************************************************************
865 * Call the Tcl function
867 * We assume no PG error can be thrown directly from this call.
868 ************************************************************/
869 tcl_rc
= Tcl_GlobalEval(interp
, Tcl_DStringValue(&tcl_cmd
));
870 Tcl_DStringFree(&tcl_cmd
);
872 /************************************************************
873 * Check for errors reported by Tcl.
874 ************************************************************/
875 if (tcl_rc
!= TCL_OK
)
876 throw_tcl_error(interp
, prodesc
->user_proname
);
878 /************************************************************
879 * The return value from the procedure might be one of
880 * the magic strings OK or SKIP or a list from array get.
881 * We can check for OK or SKIP without worrying about encoding.
882 ************************************************************/
883 if (SPI_finish() != SPI_OK_FINISH
)
884 elog(ERROR
, "SPI_finish() failed");
886 result
= Tcl_GetStringResult(interp
);
888 if (strcmp(result
, "OK") == 0)
890 if (strcmp(result
, "SKIP") == 0)
891 return (HeapTuple
) NULL
;
893 /************************************************************
894 * Convert the result value from the Tcl interpreter
895 * and setup structures for SPI_modifytuple();
896 ************************************************************/
897 if (Tcl_SplitList(interp
, result
,
898 &ret_numvals
, &ret_values
) != TCL_OK
)
901 elog(ERROR
, "could not split return value from trigger: %s",
902 UTF_U2E(Tcl_GetStringResult(interp
)));
906 /* Use a TRY to ensure ret_values will get freed */
909 if (ret_numvals
% 2 != 0)
910 elog(ERROR
, "invalid return list from trigger - must have even # of elements");
912 modattrs
= (int *) palloc(tupdesc
->natts
* sizeof(int));
913 modvalues
= (Datum
*) palloc(tupdesc
->natts
* sizeof(Datum
));
914 for (i
= 0; i
< tupdesc
->natts
; i
++)
917 modvalues
[i
] = (Datum
) NULL
;
920 modnulls
= palloc(tupdesc
->natts
);
921 memset(modnulls
, 'n', tupdesc
->natts
);
923 for (i
= 0; i
< ret_numvals
; i
+= 2)
925 CONST84
char *ret_name
= ret_values
[i
];
926 CONST84
char *ret_value
= ret_values
[i
+ 1];
933 /************************************************************
934 * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
935 ************************************************************/
936 if (strcmp(ret_name
, ".tupno") == 0)
939 /************************************************************
940 * Get the attribute number
941 ************************************************************/
942 attnum
= SPI_fnumber(tupdesc
, ret_name
);
943 if (attnum
== SPI_ERROR_NOATTRIBUTE
)
944 elog(ERROR
, "invalid attribute \"%s\"", ret_name
);
946 elog(ERROR
, "cannot set system attribute \"%s\"", ret_name
);
948 /************************************************************
949 * Ignore dropped columns
950 ************************************************************/
951 if (tupdesc
->attrs
[attnum
- 1]->attisdropped
)
954 /************************************************************
955 * Lookup the attribute type in the syscache
956 * for the input function
957 ************************************************************/
958 typeTup
= SearchSysCache(TYPEOID
,
959 ObjectIdGetDatum(tupdesc
->attrs
[attnum
- 1]->atttypid
),
961 if (!HeapTupleIsValid(typeTup
))
962 elog(ERROR
, "cache lookup failed for type %u",
963 tupdesc
->attrs
[attnum
- 1]->atttypid
);
964 typinput
= ((Form_pg_type
) GETSTRUCT(typeTup
))->typinput
;
965 typioparam
= getTypeIOParam(typeTup
);
966 ReleaseSysCache(typeTup
);
968 /************************************************************
969 * Set the attribute to NOT NULL and convert the contents
970 ************************************************************/
971 modnulls
[attnum
- 1] = ' ';
972 fmgr_info(typinput
, &finfo
);
974 modvalues
[attnum
- 1] = InputFunctionCall(&finfo
,
975 (char *) UTF_U2E(ret_value
),
977 tupdesc
->attrs
[attnum
- 1]->atttypmod
);
981 rettup
= SPI_modifytuple(trigdata
->tg_relation
, rettup
, tupdesc
->natts
,
982 modattrs
, modvalues
, modnulls
);
989 elog(ERROR
, "SPI_modifytuple() failed - RC = %d", SPI_result
);
994 ckfree((char *) ret_values
);
998 ckfree((char *) ret_values
);
1004 /**********************************************************************
1005 * throw_tcl_error - ereport an error returned from the Tcl interpreter
1006 **********************************************************************/
1008 throw_tcl_error(Tcl_Interp
*interp
, const char *proname
)
1011 * Caution is needed here because Tcl_GetVar could overwrite the
1012 * interpreter result (even though it's not really supposed to), and we
1013 * can't control the order of evaluation of ereport arguments. Hence, make
1014 * real sure we have our own copy of the result string before invoking
1021 emsg
= pstrdup(UTF_U2E(Tcl_GetStringResult(interp
)));
1024 econtext
= UTF_U2E((char *) Tcl_GetVar(interp
, "errorInfo",
1027 (errmsg("%s", emsg
),
1028 errcontext("%s\nin PL/Tcl function \"%s\"",
1029 econtext
, proname
)));
1034 /**********************************************************************
1035 * compile_pltcl_function - compile (or hopefully just look up) function
1037 * tgreloid is the OID of the relation when compiling a trigger, or zero
1038 * (InvalidOid) when compiling a plain function.
1039 **********************************************************************/
1040 static pltcl_proc_desc
*
1041 compile_pltcl_function(Oid fn_oid
, Oid tgreloid
)
1043 bool is_trigger
= OidIsValid(tgreloid
);
1045 Form_pg_proc procStruct
;
1046 char internal_proname
[128];
1047 Tcl_HashEntry
*hashent
;
1048 pltcl_proc_desc
*prodesc
= NULL
;
1054 /* We'll need the pg_proc tuple in any case... */
1055 procTup
= SearchSysCache(PROCOID
,
1056 ObjectIdGetDatum(fn_oid
),
1058 if (!HeapTupleIsValid(procTup
))
1059 elog(ERROR
, "cache lookup failed for function %u", fn_oid
);
1060 procStruct
= (Form_pg_proc
) GETSTRUCT(procTup
);
1062 /************************************************************
1063 * Build our internal proc name from the functions Oid
1064 ************************************************************/
1066 snprintf(internal_proname
, sizeof(internal_proname
),
1067 "__PLTcl_proc_%u", fn_oid
);
1069 snprintf(internal_proname
, sizeof(internal_proname
),
1070 "__PLTcl_proc_%u_trigger_%u", fn_oid
, tgreloid
);
1072 /************************************************************
1073 * Lookup the internal proc name in the hashtable
1074 ************************************************************/
1075 hashent
= Tcl_FindHashEntry(pltcl_proc_hash
, internal_proname
);
1077 /************************************************************
1078 * If it's present, must check whether it's still up to date.
1079 * This is needed because CREATE OR REPLACE FUNCTION can modify the
1080 * function's pg_proc entry without changing its OID.
1081 ************************************************************/
1082 if (hashent
!= NULL
)
1086 prodesc
= (pltcl_proc_desc
*) Tcl_GetHashValue(hashent
);
1088 uptodate
= (prodesc
->fn_xmin
== HeapTupleHeaderGetXmin(procTup
->t_data
) &&
1089 ItemPointerEquals(&prodesc
->fn_tid
, &procTup
->t_self
));
1093 Tcl_DeleteHashEntry(hashent
);
1098 /************************************************************
1099 * If we haven't found it in the hashtable, we analyze
1100 * the functions arguments and returntype and store
1101 * the in-/out-functions in the prodesc block and create
1102 * a new hashtable entry for it.
1104 * Then we load the procedure into the Tcl interpreter.
1105 ************************************************************/
1106 if (hashent
== NULL
)
1110 Form_pg_language langStruct
;
1111 Form_pg_type typeStruct
;
1112 Tcl_DString proc_internal_def
;
1113 Tcl_DString proc_internal_body
;
1114 char proc_internal_args
[33 * FUNC_MAX_ARGS
];
1120 /************************************************************
1121 * Allocate a new procedure description block
1122 ************************************************************/
1123 prodesc
= (pltcl_proc_desc
*) malloc(sizeof(pltcl_proc_desc
));
1124 if (prodesc
== NULL
)
1126 (errcode(ERRCODE_OUT_OF_MEMORY
),
1127 errmsg("out of memory")));
1128 MemSet(prodesc
, 0, sizeof(pltcl_proc_desc
));
1129 prodesc
->user_proname
= strdup(NameStr(procStruct
->proname
));
1130 prodesc
->internal_proname
= strdup(internal_proname
);
1131 prodesc
->fn_xmin
= HeapTupleHeaderGetXmin(procTup
->t_data
);
1132 prodesc
->fn_tid
= procTup
->t_self
;
1134 /* Remember if function is STABLE/IMMUTABLE */
1135 prodesc
->fn_readonly
=
1136 (procStruct
->provolatile
!= PROVOLATILE_VOLATILE
);
1138 /************************************************************
1139 * Lookup the pg_language tuple by Oid
1140 ************************************************************/
1141 langTup
= SearchSysCache(LANGOID
,
1142 ObjectIdGetDatum(procStruct
->prolang
),
1144 if (!HeapTupleIsValid(langTup
))
1146 free(prodesc
->user_proname
);
1147 free(prodesc
->internal_proname
);
1149 elog(ERROR
, "cache lookup failed for language %u",
1150 procStruct
->prolang
);
1152 langStruct
= (Form_pg_language
) GETSTRUCT(langTup
);
1153 prodesc
->lanpltrusted
= langStruct
->lanpltrusted
;
1154 ReleaseSysCache(langTup
);
1156 if (prodesc
->lanpltrusted
)
1157 interp
= pltcl_safe_interp
;
1159 interp
= pltcl_norm_interp
;
1161 /************************************************************
1162 * Get the required information for input conversion of the
1164 ************************************************************/
1167 typeTup
= SearchSysCache(TYPEOID
,
1168 ObjectIdGetDatum(procStruct
->prorettype
),
1170 if (!HeapTupleIsValid(typeTup
))
1172 free(prodesc
->user_proname
);
1173 free(prodesc
->internal_proname
);
1175 elog(ERROR
, "cache lookup failed for type %u",
1176 procStruct
->prorettype
);
1178 typeStruct
= (Form_pg_type
) GETSTRUCT(typeTup
);
1180 /* Disallow pseudotype result, except VOID */
1181 if (typeStruct
->typtype
== TYPTYPE_PSEUDO
)
1183 if (procStruct
->prorettype
== VOIDOID
)
1185 else if (procStruct
->prorettype
== TRIGGEROID
)
1187 free(prodesc
->user_proname
);
1188 free(prodesc
->internal_proname
);
1191 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1192 errmsg("trigger functions can only be called as triggers")));
1196 free(prodesc
->user_proname
);
1197 free(prodesc
->internal_proname
);
1200 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1201 errmsg("PL/Tcl functions cannot return type %s",
1202 format_type_be(procStruct
->prorettype
))));
1206 if (typeStruct
->typtype
== TYPTYPE_COMPOSITE
)
1208 free(prodesc
->user_proname
);
1209 free(prodesc
->internal_proname
);
1212 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1213 errmsg("PL/Tcl functions cannot return composite types")));
1216 perm_fmgr_info(typeStruct
->typinput
, &(prodesc
->result_in_func
));
1217 prodesc
->result_typioparam
= getTypeIOParam(typeTup
);
1219 ReleaseSysCache(typeTup
);
1222 /************************************************************
1223 * Get the required information for output conversion
1224 * of all procedure arguments
1225 ************************************************************/
1228 prodesc
->nargs
= procStruct
->pronargs
;
1229 proc_internal_args
[0] = '\0';
1230 for (i
= 0; i
< prodesc
->nargs
; i
++)
1232 typeTup
= SearchSysCache(TYPEOID
,
1233 ObjectIdGetDatum(procStruct
->proargtypes
.values
[i
]),
1235 if (!HeapTupleIsValid(typeTup
))
1237 free(prodesc
->user_proname
);
1238 free(prodesc
->internal_proname
);
1240 elog(ERROR
, "cache lookup failed for type %u",
1241 procStruct
->proargtypes
.values
[i
]);
1243 typeStruct
= (Form_pg_type
) GETSTRUCT(typeTup
);
1245 /* Disallow pseudotype argument */
1246 if (typeStruct
->typtype
== TYPTYPE_PSEUDO
)
1248 free(prodesc
->user_proname
);
1249 free(prodesc
->internal_proname
);
1252 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1253 errmsg("PL/Tcl functions cannot accept type %s",
1254 format_type_be(procStruct
->proargtypes
.values
[i
]))));
1257 if (typeStruct
->typtype
== TYPTYPE_COMPOSITE
)
1259 prodesc
->arg_is_rowtype
[i
] = true;
1260 snprintf(buf
, sizeof(buf
), "__PLTcl_Tup_%d", i
+ 1);
1264 prodesc
->arg_is_rowtype
[i
] = false;
1265 perm_fmgr_info(typeStruct
->typoutput
,
1266 &(prodesc
->arg_out_func
[i
]));
1267 snprintf(buf
, sizeof(buf
), "%d", i
+ 1);
1271 strcat(proc_internal_args
, " ");
1272 strcat(proc_internal_args
, buf
);
1274 ReleaseSysCache(typeTup
);
1279 /* trigger procedure has fixed args */
1280 strcpy(proc_internal_args
,
1281 "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");
1284 /************************************************************
1285 * Create the tcl command to define the internal
1287 ************************************************************/
1288 Tcl_DStringInit(&proc_internal_def
);
1289 Tcl_DStringInit(&proc_internal_body
);
1290 Tcl_DStringAppendElement(&proc_internal_def
, "proc");
1291 Tcl_DStringAppendElement(&proc_internal_def
, internal_proname
);
1292 Tcl_DStringAppendElement(&proc_internal_def
, proc_internal_args
);
1294 /************************************************************
1295 * prefix procedure body with
1296 * upvar #0 <internal_procname> GD
1297 * and with appropriate setting of arguments
1298 ************************************************************/
1299 Tcl_DStringAppend(&proc_internal_body
, "upvar #0 ", -1);
1300 Tcl_DStringAppend(&proc_internal_body
, internal_proname
, -1);
1301 Tcl_DStringAppend(&proc_internal_body
, " GD\n", -1);
1304 for (i
= 0; i
< prodesc
->nargs
; i
++)
1306 if (prodesc
->arg_is_rowtype
[i
])
1308 snprintf(buf
, sizeof(buf
),
1309 "array set %d $__PLTcl_Tup_%d\n",
1311 Tcl_DStringAppend(&proc_internal_body
, buf
, -1);
1317 Tcl_DStringAppend(&proc_internal_body
,
1318 "array set NEW $__PLTcl_Tup_NEW\n", -1);
1319 Tcl_DStringAppend(&proc_internal_body
,
1320 "array set OLD $__PLTcl_Tup_OLD\n", -1);
1322 Tcl_DStringAppend(&proc_internal_body
,
1325 "foreach v $args {\n"
1329 "unset i v\n\n", -1);
1332 /************************************************************
1333 * Add user's function definition to proc body
1334 ************************************************************/
1335 prosrcdatum
= SysCacheGetAttr(PROCOID
, procTup
,
1336 Anum_pg_proc_prosrc
, &isnull
);
1338 elog(ERROR
, "null prosrc");
1339 proc_source
= TextDatumGetCString(prosrcdatum
);
1341 Tcl_DStringAppend(&proc_internal_body
, UTF_E2U(proc_source
), -1);
1344 Tcl_DStringAppendElement(&proc_internal_def
,
1345 Tcl_DStringValue(&proc_internal_body
));
1346 Tcl_DStringFree(&proc_internal_body
);
1348 /************************************************************
1349 * Create the procedure in the interpreter
1350 ************************************************************/
1351 tcl_rc
= Tcl_GlobalEval(interp
,
1352 Tcl_DStringValue(&proc_internal_def
));
1353 Tcl_DStringFree(&proc_internal_def
);
1354 if (tcl_rc
!= TCL_OK
)
1356 free(prodesc
->user_proname
);
1357 free(prodesc
->internal_proname
);
1360 elog(ERROR
, "could not create internal procedure \"%s\": %s",
1361 internal_proname
, UTF_U2E(Tcl_GetStringResult(interp
)));
1365 /************************************************************
1366 * Add the proc description block to the hashtable
1367 ************************************************************/
1368 hashent
= Tcl_CreateHashEntry(pltcl_proc_hash
,
1369 prodesc
->internal_proname
, &hashnew
);
1370 Tcl_SetHashValue(hashent
, (ClientData
) prodesc
);
1373 ReleaseSysCache(procTup
);
1379 /**********************************************************************
1380 * pltcl_elog() - elog() support for PLTcl
1381 **********************************************************************/
1383 pltcl_elog(ClientData cdata
, Tcl_Interp
*interp
,
1384 int argc
, CONST84
char *argv
[])
1387 MemoryContext oldcontext
;
1391 Tcl_SetResult(interp
, "syntax error - 'elog level msg'", TCL_STATIC
);
1395 if (strcmp(argv
[1], "DEBUG") == 0)
1397 else if (strcmp(argv
[1], "LOG") == 0)
1399 else if (strcmp(argv
[1], "INFO") == 0)
1401 else if (strcmp(argv
[1], "NOTICE") == 0)
1403 else if (strcmp(argv
[1], "WARNING") == 0)
1405 else if (strcmp(argv
[1], "ERROR") == 0)
1407 else if (strcmp(argv
[1], "FATAL") == 0)
1411 Tcl_AppendResult(interp
, "Unknown elog level '", argv
[1],
1419 * We just pass the error back to Tcl. If it's not caught, it'll
1420 * eventually get converted to a PG error when we reach the call
1423 Tcl_SetResult(interp
, (char *) argv
[2], TCL_VOLATILE
);
1428 * For non-error messages, just pass 'em to elog(). We do not expect that
1429 * this will fail, but just on the off chance it does, report the error
1430 * back to Tcl. Note we are assuming that elog() can't have any internal
1431 * failures that are so bad as to require a transaction abort.
1433 * This path is also used for FATAL errors, which aren't going to come
1434 * back to us at all.
1436 oldcontext
= CurrentMemoryContext
;
1440 elog(level
, "%s", UTF_U2E(argv
[2]));
1447 /* Must reset elog.c's state */
1448 MemoryContextSwitchTo(oldcontext
);
1449 edata
= CopyErrorData();
1452 /* Pass the error message to Tcl */
1454 Tcl_SetResult(interp
, UTF_E2U(edata
->message
), TCL_VOLATILE
);
1456 FreeErrorData(edata
);
1466 /**********************************************************************
1467 * pltcl_quote() - quote literal strings that are to
1468 * be used in SPI_execute query strings
1469 **********************************************************************/
1471 pltcl_quote(ClientData cdata
, Tcl_Interp
*interp
,
1472 int argc
, CONST84
char *argv
[])
1478 /************************************************************
1480 ************************************************************/
1483 Tcl_SetResult(interp
, "syntax error - 'quote string'", TCL_STATIC
);
1487 /************************************************************
1488 * Allocate space for the maximum the string can
1489 * grow to and initialize pointers
1490 ************************************************************/
1491 tmp
= palloc(strlen(argv
[1]) * 2 + 1);
1495 /************************************************************
1496 * Walk through string and double every quote and backslash
1497 ************************************************************/
1510 /************************************************************
1511 * Terminate the string and set it as result
1512 ************************************************************/
1514 Tcl_SetResult(interp
, tmp
, TCL_VOLATILE
);
1520 /**********************************************************************
1521 * pltcl_argisnull() - determine if a specific argument is NULL
1522 **********************************************************************/
1524 pltcl_argisnull(ClientData cdata
, Tcl_Interp
*interp
,
1525 int argc
, CONST84
char *argv
[])
1528 FunctionCallInfo fcinfo
= pltcl_current_fcinfo
;
1530 /************************************************************
1532 ************************************************************/
1535 Tcl_SetResult(interp
, "syntax error - 'argisnull argno'",
1540 /************************************************************
1541 * Check that we're called as a normal function
1542 ************************************************************/
1545 Tcl_SetResult(interp
, "argisnull cannot be used in triggers",
1550 /************************************************************
1551 * Get the argument number
1552 ************************************************************/
1553 if (Tcl_GetInt(interp
, argv
[1], &argno
) != TCL_OK
)
1556 /************************************************************
1557 * Check that the argno is valid
1558 ************************************************************/
1560 if (argno
< 0 || argno
>= fcinfo
->nargs
)
1562 Tcl_SetResult(interp
, "argno out of range", TCL_STATIC
);
1566 /************************************************************
1567 * Get the requested NULL state
1568 ************************************************************/
1569 if (PG_ARGISNULL(argno
))
1570 Tcl_SetResult(interp
, "1", TCL_STATIC
);
1572 Tcl_SetResult(interp
, "0", TCL_STATIC
);
1578 /**********************************************************************
1579 * pltcl_returnnull() - Cause a NULL return from a function
1580 **********************************************************************/
1582 pltcl_returnnull(ClientData cdata
, Tcl_Interp
*interp
,
1583 int argc
, CONST84
char *argv
[])
1585 FunctionCallInfo fcinfo
= pltcl_current_fcinfo
;
1587 /************************************************************
1589 ************************************************************/
1592 Tcl_SetResult(interp
, "syntax error - 'return_null'", TCL_STATIC
);
1596 /************************************************************
1597 * Check that we're called as a normal function
1598 ************************************************************/
1601 Tcl_SetResult(interp
, "return_null cannot be used in triggers",
1606 /************************************************************
1607 * Set the NULL return flag and cause Tcl to return from the
1609 ************************************************************/
1610 fcinfo
->isnull
= true;
1617 * Support for running SPI operations inside subtransactions
1619 * Intended usage pattern is:
1621 * MemoryContext oldcontext = CurrentMemoryContext;
1622 * ResourceOwner oldowner = CurrentResourceOwner;
1625 * pltcl_subtrans_begin(oldcontext, oldowner);
1628 * do something risky;
1629 * pltcl_subtrans_commit(oldcontext, oldowner);
1633 * pltcl_subtrans_abort(interp, oldcontext, oldowner);
1641 pltcl_subtrans_begin(MemoryContext oldcontext
, ResourceOwner oldowner
)
1643 BeginInternalSubTransaction(NULL
);
1645 /* Want to run inside function's memory context */
1646 MemoryContextSwitchTo(oldcontext
);
1650 pltcl_subtrans_commit(MemoryContext oldcontext
, ResourceOwner oldowner
)
1652 /* Commit the inner transaction, return to outer xact context */
1653 ReleaseCurrentSubTransaction();
1654 MemoryContextSwitchTo(oldcontext
);
1655 CurrentResourceOwner
= oldowner
;
1658 * AtEOSubXact_SPI() should not have popped any SPI context, but just in
1659 * case it did, make sure we remain connected.
1661 SPI_restore_connection();
1665 pltcl_subtrans_abort(Tcl_Interp
*interp
,
1666 MemoryContext oldcontext
, ResourceOwner oldowner
)
1670 /* Save error info */
1671 MemoryContextSwitchTo(oldcontext
);
1672 edata
= CopyErrorData();
1675 /* Abort the inner transaction */
1676 RollbackAndReleaseCurrentSubTransaction();
1677 MemoryContextSwitchTo(oldcontext
);
1678 CurrentResourceOwner
= oldowner
;
1681 * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
1682 * have left us in a disconnected state. We need this hack to return to
1685 SPI_restore_connection();
1687 /* Pass the error message to Tcl */
1689 Tcl_SetResult(interp
, UTF_E2U(edata
->message
), TCL_VOLATILE
);
1691 FreeErrorData(edata
);
1695 /**********************************************************************
1696 * pltcl_SPI_execute() - The builtin SPI_execute command
1697 * for the Tcl interpreter
1698 **********************************************************************/
1700 pltcl_SPI_execute(ClientData cdata
, Tcl_Interp
*interp
,
1701 int argc
, CONST84
char *argv
[])
1708 CONST84
char *volatile arrayname
= NULL
;
1709 CONST84
char *volatile loop_body
= NULL
;
1710 MemoryContext oldcontext
= CurrentMemoryContext
;
1711 ResourceOwner oldowner
= CurrentResourceOwner
;
1713 char *usage
= "syntax error - 'SPI_exec "
1715 "?-array name? query ?loop body?";
1717 /************************************************************
1718 * Check the call syntax and get the options
1719 ************************************************************/
1722 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
1729 if (strcmp(argv
[i
], "-array") == 0)
1733 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
1736 arrayname
= argv
[i
++];
1740 if (strcmp(argv
[i
], "-count") == 0)
1744 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
1747 if (Tcl_GetInt(interp
, argv
[i
++], &count
) != TCL_OK
)
1756 if (query_idx
>= argc
|| query_idx
+ 2 < argc
)
1758 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
1761 if (query_idx
+ 1 < argc
)
1762 loop_body
= argv
[query_idx
+ 1];
1764 /************************************************************
1765 * Execute the query inside a sub-transaction, so we can cope with
1767 ************************************************************/
1769 pltcl_subtrans_begin(oldcontext
, oldowner
);
1774 spi_rc
= SPI_execute(UTF_U2E(argv
[query_idx
]),
1775 pltcl_current_prodesc
->fn_readonly
, count
);
1778 my_rc
= pltcl_process_SPI_result(interp
,
1785 pltcl_subtrans_commit(oldcontext
, oldowner
);
1789 pltcl_subtrans_abort(interp
, oldcontext
, oldowner
);
1798 * Process the result from SPI_execute or SPI_execute_plan
1800 * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
1803 pltcl_process_SPI_result(Tcl_Interp
*interp
,
1804 CONST84
char *arrayname
,
1805 CONST84
char *loop_body
,
1807 SPITupleTable
*tuptable
,
1819 case SPI_OK_SELINTO
:
1823 snprintf(buf
, sizeof(buf
), "%d", ntuples
);
1824 Tcl_SetResult(interp
, buf
, TCL_VOLATILE
);
1827 case SPI_OK_UTILITY
:
1828 case SPI_OK_REWRITTEN
:
1829 if (tuptable
== NULL
)
1831 Tcl_SetResult(interp
, "0", TCL_STATIC
);
1834 /* FALL THRU for utility returning tuples */
1837 case SPI_OK_INSERT_RETURNING
:
1838 case SPI_OK_DELETE_RETURNING
:
1839 case SPI_OK_UPDATE_RETURNING
:
1842 * Process the tuples we got
1844 tuples
= tuptable
->vals
;
1845 tupdesc
= tuptable
->tupdesc
;
1847 if (loop_body
== NULL
)
1850 * If there is no loop body given, just set the variables from
1851 * the first tuple (if any)
1854 pltcl_set_tuple_values(interp
, arrayname
, 0,
1855 tuples
[0], tupdesc
);
1860 * There is a loop body - process all tuples and evaluate the
1863 for (i
= 0; i
< ntuples
; i
++)
1865 pltcl_set_tuple_values(interp
, arrayname
, i
,
1866 tuples
[i
], tupdesc
);
1868 loop_rc
= Tcl_Eval(interp
, loop_body
);
1870 if (loop_rc
== TCL_OK
)
1872 if (loop_rc
== TCL_CONTINUE
)
1874 if (loop_rc
== TCL_RETURN
)
1879 if (loop_rc
== TCL_BREAK
)
1886 if (my_rc
== TCL_OK
)
1888 snprintf(buf
, sizeof(buf
), "%d", ntuples
);
1889 Tcl_SetResult(interp
, buf
, TCL_VOLATILE
);
1894 Tcl_AppendResult(interp
, "pltcl: SPI_execute failed: ",
1895 SPI_result_code_string(spi_rc
), NULL
);
1900 SPI_freetuptable(tuptable
);
1906 /**********************************************************************
1907 * pltcl_SPI_prepare() - Builtin support for prepared plans
1908 * The Tcl command SPI_prepare
1909 * always saves the plan using
1910 * SPI_saveplan and returns a key for
1911 * access. There is no chance to prepare
1912 * and not save the plan currently.
1913 **********************************************************************/
1915 pltcl_SPI_prepare(ClientData cdata
, Tcl_Interp
*interp
,
1916 int argc
, CONST84
char *argv
[])
1919 CONST84
char **args
;
1920 pltcl_query_desc
*qdesc
;
1923 Tcl_HashEntry
*hashent
;
1925 Tcl_HashTable
*query_hash
;
1926 MemoryContext oldcontext
= CurrentMemoryContext
;
1927 ResourceOwner oldowner
= CurrentResourceOwner
;
1929 /************************************************************
1930 * Check the call syntax
1931 ************************************************************/
1934 Tcl_SetResult(interp
, "syntax error - 'SPI_prepare query argtypes'",
1939 /************************************************************
1940 * Split the argument type list
1941 ************************************************************/
1942 if (Tcl_SplitList(interp
, argv
[2], &nargs
, &args
) != TCL_OK
)
1945 /************************************************************
1946 * Allocate the new querydesc structure
1947 ************************************************************/
1948 qdesc
= (pltcl_query_desc
*) malloc(sizeof(pltcl_query_desc
));
1949 snprintf(qdesc
->qname
, sizeof(qdesc
->qname
), "%lx", (long) qdesc
);
1950 qdesc
->nargs
= nargs
;
1951 qdesc
->argtypes
= (Oid
*) malloc(nargs
* sizeof(Oid
));
1952 qdesc
->arginfuncs
= (FmgrInfo
*) malloc(nargs
* sizeof(FmgrInfo
));
1953 qdesc
->argtypioparams
= (Oid
*) malloc(nargs
* sizeof(Oid
));
1955 /************************************************************
1956 * Execute the prepare inside a sub-transaction, so we can cope with
1958 ************************************************************/
1960 pltcl_subtrans_begin(oldcontext
, oldowner
);
1964 /************************************************************
1965 * Resolve argument type names and then look them up by oid
1966 * in the system cache, and remember the required information
1967 * for input conversion.
1968 ************************************************************/
1969 for (i
= 0; i
< nargs
; i
++)
1976 parseTypeString(args
[i
], &typId
, &typmod
);
1978 getTypeInputInfo(typId
, &typInput
, &typIOParam
);
1980 qdesc
->argtypes
[i
] = typId
;
1981 perm_fmgr_info(typInput
, &(qdesc
->arginfuncs
[i
]));
1982 qdesc
->argtypioparams
[i
] = typIOParam
;
1985 /************************************************************
1986 * Prepare the plan and check for errors
1987 ************************************************************/
1989 plan
= SPI_prepare(UTF_U2E(argv
[1]), nargs
, qdesc
->argtypes
);
1993 elog(ERROR
, "SPI_prepare() failed");
1995 /************************************************************
1996 * Save the plan into permanent memory (right now it's in the
1997 * SPI procCxt, which will go away at function end).
1998 ************************************************************/
1999 qdesc
->plan
= SPI_saveplan(plan
);
2000 if (qdesc
->plan
== NULL
)
2001 elog(ERROR
, "SPI_saveplan() failed");
2003 /* Release the procCxt copy to avoid within-function memory leak */
2006 pltcl_subtrans_commit(oldcontext
, oldowner
);
2010 pltcl_subtrans_abort(interp
, oldcontext
, oldowner
);
2012 free(qdesc
->argtypes
);
2013 free(qdesc
->arginfuncs
);
2014 free(qdesc
->argtypioparams
);
2016 ckfree((char *) args
);
2022 /************************************************************
2023 * Insert a hashtable entry for the plan and return
2024 * the key to the caller
2025 ************************************************************/
2026 if (interp
== pltcl_norm_interp
)
2027 query_hash
= pltcl_norm_query_hash
;
2029 query_hash
= pltcl_safe_query_hash
;
2031 hashent
= Tcl_CreateHashEntry(query_hash
, qdesc
->qname
, &hashnew
);
2032 Tcl_SetHashValue(hashent
, (ClientData
) qdesc
);
2034 ckfree((char *) args
);
2036 /* qname is ASCII, so no need for encoding conversion */
2037 Tcl_SetResult(interp
, qdesc
->qname
, TCL_VOLATILE
);
2042 /**********************************************************************
2043 * pltcl_SPI_execute_plan() - Execute a prepared plan
2044 **********************************************************************/
2046 pltcl_SPI_execute_plan(ClientData cdata
, Tcl_Interp
*interp
,
2047 int argc
, CONST84
char *argv
[])
2053 Tcl_HashEntry
*hashent
;
2054 pltcl_query_desc
*qdesc
;
2055 const char *volatile nulls
= NULL
;
2056 CONST84
char *volatile arrayname
= NULL
;
2057 CONST84
char *volatile loop_body
= NULL
;
2060 CONST84
char **callargs
= NULL
;
2062 MemoryContext oldcontext
= CurrentMemoryContext
;
2063 ResourceOwner oldowner
= CurrentResourceOwner
;
2064 Tcl_HashTable
*query_hash
;
2066 char *usage
= "syntax error - 'SPI_execp "
2067 "?-nulls string? ?-count n? "
2068 "?-array name? query ?args? ?loop body?";
2070 /************************************************************
2071 * Get the options and check syntax
2072 ************************************************************/
2076 if (strcmp(argv
[i
], "-array") == 0)
2080 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
2083 arrayname
= argv
[i
++];
2086 if (strcmp(argv
[i
], "-nulls") == 0)
2090 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
2096 if (strcmp(argv
[i
], "-count") == 0)
2100 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
2103 if (Tcl_GetInt(interp
, argv
[i
++], &count
) != TCL_OK
)
2111 /************************************************************
2112 * Get the prepared plan descriptor by its key
2113 ************************************************************/
2116 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
2120 if (interp
== pltcl_norm_interp
)
2121 query_hash
= pltcl_norm_query_hash
;
2123 query_hash
= pltcl_safe_query_hash
;
2125 hashent
= Tcl_FindHashEntry(query_hash
, argv
[i
]);
2126 if (hashent
== NULL
)
2128 Tcl_AppendResult(interp
, "invalid queryid '", argv
[i
], "'", NULL
);
2131 qdesc
= (pltcl_query_desc
*) Tcl_GetHashValue(hashent
);
2134 /************************************************************
2135 * If a nulls string is given, check for correct length
2136 ************************************************************/
2139 if (strlen(nulls
) != qdesc
->nargs
)
2141 Tcl_SetResult(interp
,
2142 "length of nulls string doesn't match # of arguments",
2148 /************************************************************
2149 * If there was a argtype list on preparation, we need
2150 * an argument value list now
2151 ************************************************************/
2152 if (qdesc
->nargs
> 0)
2156 Tcl_SetResult(interp
, "missing argument list", TCL_STATIC
);
2160 /************************************************************
2161 * Split the argument values
2162 ************************************************************/
2163 if (Tcl_SplitList(interp
, argv
[i
++], &callnargs
, &callargs
) != TCL_OK
)
2166 /************************************************************
2167 * Check that the # of arguments matches
2168 ************************************************************/
2169 if (callnargs
!= qdesc
->nargs
)
2171 Tcl_SetResult(interp
,
2172 "argument list length doesn't match # of arguments for query",
2174 ckfree((char *) callargs
);
2181 /************************************************************
2182 * Get loop body if present
2183 ************************************************************/
2185 loop_body
= argv
[i
++];
2189 Tcl_SetResult(interp
, usage
, TCL_STATIC
);
2193 /************************************************************
2194 * Execute the plan inside a sub-transaction, so we can cope with
2196 ************************************************************/
2198 pltcl_subtrans_begin(oldcontext
, oldowner
);
2202 /************************************************************
2203 * Setup the value array for SPI_execute_plan() using
2204 * the type specific input functions
2205 ************************************************************/
2206 argvalues
= (Datum
*) palloc(callnargs
* sizeof(Datum
));
2208 for (j
= 0; j
< callnargs
; j
++)
2210 if (nulls
&& nulls
[j
] == 'n')
2212 argvalues
[j
] = InputFunctionCall(&qdesc
->arginfuncs
[j
],
2214 qdesc
->argtypioparams
[j
],
2220 argvalues
[j
] = InputFunctionCall(&qdesc
->arginfuncs
[j
],
2221 (char *) UTF_U2E(callargs
[j
]),
2222 qdesc
->argtypioparams
[j
],
2229 ckfree((char *) callargs
);
2232 /************************************************************
2234 ************************************************************/
2235 spi_rc
= SPI_execute_plan(qdesc
->plan
, argvalues
, nulls
,
2236 pltcl_current_prodesc
->fn_readonly
, count
);
2238 my_rc
= pltcl_process_SPI_result(interp
,
2245 pltcl_subtrans_commit(oldcontext
, oldowner
);
2249 pltcl_subtrans_abort(interp
, oldcontext
, oldowner
);
2252 ckfree((char *) callargs
);
2262 /**********************************************************************
2263 * pltcl_SPI_lastoid() - return the last oid. To
2264 * be used after insert queries
2265 **********************************************************************/
2267 pltcl_SPI_lastoid(ClientData cdata
, Tcl_Interp
*interp
,
2268 int argc
, CONST84
char *argv
[])
2272 snprintf(buf
, sizeof(buf
), "%u", SPI_lastoid
);
2273 Tcl_SetResult(interp
, buf
, TCL_VOLATILE
);
2278 /**********************************************************************
2279 * pltcl_set_tuple_values() - Set variables for all attributes
2281 **********************************************************************/
2283 pltcl_set_tuple_values(Tcl_Interp
*interp
, CONST84
char *arrayname
,
2284 int tupno
, HeapTuple tuple
, TupleDesc tupdesc
)
2292 CONST84
char *attname
;
2296 CONST84
char **arrptr
;
2297 CONST84
char **nameptr
;
2298 CONST84
char *nullname
= NULL
;
2300 /************************************************************
2301 * Prepare pointers for Tcl_SetVar2() below and in array
2302 * mode set the .tupno element
2303 ************************************************************/
2304 if (arrayname
== NULL
)
2307 nameptr
= &nullname
;
2311 arrptr
= &arrayname
;
2313 snprintf(buf
, sizeof(buf
), "%d", tupno
);
2314 Tcl_SetVar2(interp
, arrayname
, ".tupno", buf
, 0);
2317 for (i
= 0; i
< tupdesc
->natts
; i
++)
2319 /* ignore dropped attributes */
2320 if (tupdesc
->attrs
[i
]->attisdropped
)
2323 /************************************************************
2324 * Get the attribute name
2325 ************************************************************/
2326 attname
= NameStr(tupdesc
->attrs
[i
]->attname
);
2328 /************************************************************
2329 * Get the attributes value
2330 ************************************************************/
2331 attr
= heap_getattr(tuple
, i
+ 1, tupdesc
, &isnull
);
2333 /************************************************************
2334 * Lookup the attribute type in the syscache
2335 * for the output function
2336 ************************************************************/
2337 typeTup
= SearchSysCache(TYPEOID
,
2338 ObjectIdGetDatum(tupdesc
->attrs
[i
]->atttypid
),
2340 if (!HeapTupleIsValid(typeTup
))
2341 elog(ERROR
, "cache lookup failed for type %u",
2342 tupdesc
->attrs
[i
]->atttypid
);
2344 typoutput
= ((Form_pg_type
) GETSTRUCT(typeTup
))->typoutput
;
2345 ReleaseSysCache(typeTup
);
2347 /************************************************************
2348 * If there is a value, set the variable
2351 * Hmmm - Null attributes will cause functions to
2352 * crash if they don't expect them - need something
2354 ************************************************************/
2355 if (!isnull
&& OidIsValid(typoutput
))
2357 outputstr
= OidOutputFunctionCall(typoutput
, attr
);
2359 Tcl_SetVar2(interp
, *arrptr
, *nameptr
, UTF_E2U(outputstr
), 0);
2364 Tcl_UnsetVar2(interp
, *arrptr
, *nameptr
, 0);
2369 /**********************************************************************
2370 * pltcl_build_tuple_argument() - Build a string usable for 'array set'
2371 * from all attributes of a given tuple
2372 **********************************************************************/
2374 pltcl_build_tuple_argument(HeapTuple tuple
, TupleDesc tupdesc
,
2375 Tcl_DString
*retval
)
2386 for (i
= 0; i
< tupdesc
->natts
; i
++)
2388 /* ignore dropped attributes */
2389 if (tupdesc
->attrs
[i
]->attisdropped
)
2392 /************************************************************
2393 * Get the attribute name
2394 ************************************************************/
2395 attname
= NameStr(tupdesc
->attrs
[i
]->attname
);
2397 /************************************************************
2398 * Get the attributes value
2399 ************************************************************/
2400 attr
= heap_getattr(tuple
, i
+ 1, tupdesc
, &isnull
);
2402 /************************************************************
2403 * Lookup the attribute type in the syscache
2404 * for the output function
2405 ************************************************************/
2406 typeTup
= SearchSysCache(TYPEOID
,
2407 ObjectIdGetDatum(tupdesc
->attrs
[i
]->atttypid
),
2409 if (!HeapTupleIsValid(typeTup
))
2410 elog(ERROR
, "cache lookup failed for type %u",
2411 tupdesc
->attrs
[i
]->atttypid
);
2413 typoutput
= ((Form_pg_type
) GETSTRUCT(typeTup
))->typoutput
;
2414 ReleaseSysCache(typeTup
);
2416 /************************************************************
2417 * If there is a value, append the attribute name and the
2420 * Hmmm - Null attributes will cause functions to
2421 * crash if they don't expect them - need something
2423 ************************************************************/
2424 if (!isnull
&& OidIsValid(typoutput
))
2426 outputstr
= OidOutputFunctionCall(typoutput
, attr
);
2427 Tcl_DStringAppendElement(retval
, attname
);
2429 Tcl_DStringAppendElement(retval
, UTF_E2U(outputstr
));