Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / pl / tcl / pltcl.c
blob55d6867b1d1b01b001fe4a79809d581ccbe6ff95
1 /**********************************************************************
2 * pltcl.c - PostgreSQL support for Tcl as
3 * procedural language (PL)
5 * $PostgreSQL$
7 **********************************************************************/
9 #include "postgres.h"
11 #include <tcl.h>
13 #include <unistd.h>
14 #include <fcntl.h>
16 /* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
17 #ifndef CONST84
18 #define CONST84
19 #endif
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"
27 #include "fmgr.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)
46 #endif
48 /* define our text domain for translations */
49 #undef TEXTDOMAIN
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);
68 #define PLTCL_UTF
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 */
78 #define UTF_BEGIN
79 #define UTF_END
80 #define UTF_U2E(x) (x)
81 #define UTF_E2U(x) (x)
82 #endif /* PLTCL_UTF */
84 PG_MODULE_MAGIC;
86 /**********************************************************************
87 * The information we cache about loaded procedures
88 **********************************************************************/
89 typedef struct pltcl_proc_desc
91 char *user_proname;
92 char *internal_proname;
93 TransactionId fn_xmin;
94 ItemPointerData fn_tid;
95 bool fn_readonly;
96 bool lanpltrusted;
97 FmgrInfo result_in_func;
98 Oid result_typioparam;
99 int nargs;
100 FmgrInfo arg_out_func[FUNC_MAX_ARGS];
101 bool arg_is_rowtype[FUNC_MAX_ARGS];
102 } pltcl_proc_desc;
105 /**********************************************************************
106 * The information we cache about prepared and saved plans
107 **********************************************************************/
108 typedef struct pltcl_query_desc
110 char qname[20];
111 void *plan;
112 int nargs;
113 Oid *argtypes;
114 FmgrInfo *arginfuncs;
115 Oid *argtypioparams;
116 } pltcl_query_desc;
119 /**********************************************************************
120 * Global data
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);
140 void _PG_init(void);
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,
168 int spi_rc,
169 SPITupleTable *tuptable,
170 int ntuples);
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)
198 static ClientData
199 pltcl_InitNotifier(void)
201 static int fakeThreadKey; /* To give valid address for ClientData */
203 return (ClientData) &(fakeThreadKey);
206 static void
207 pltcl_FinalizeNotifier(ClientData clientData)
211 static void
212 pltcl_SetTimer(Tcl_Time *timePtr)
216 static void
217 pltcl_AlertNotifier(ClientData clientData)
221 static void
222 pltcl_CreateFileHandler(int fd, int mask,
223 Tcl_FileProc *proc, ClientData clientData)
227 static void
228 pltcl_DeleteFileHandler(int fd)
232 static void
233 pltcl_ServiceModeHook(int mode)
237 static int
238 pltcl_WaitForEvent(Tcl_Time *timePtr)
240 return 0;
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.
256 static void
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!
267 void
268 _PG_init(void)
270 /* Be sure we do initialization only once (should be redundant now) */
271 if (pltcl_pm_init_done)
272 return;
274 pg_bindtextdomain(TEXTDOMAIN);
276 #ifdef WIN32
277 /* Required on win32 to prevent error loading init.tcl */
278 Tcl_FindExecutable("");
279 #endif
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(&notifier);
299 #endif
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 **********************************************************************/
340 static void
341 pltcl_init_all(void)
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 **********************************************************************/
362 static void
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 **********************************************************************/
392 static void
393 pltcl_init_load_unknown(Tcl_Interp *interp)
395 int spi_rc;
396 int tcl_rc;
397 Tcl_DString unknown_src;
398 char *part;
399 int i;
400 int fno;
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'",
407 false, 1);
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)
412 return;
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' "
422 "order by modseq",
423 false, 0);
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");
435 return;
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);
449 if (part != NULL)
451 UTF_BEGIN;
452 Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
453 UTF_END;
454 pfree(part);
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
468 * PL/Tcl procedures.
469 **********************************************************************/
470 PG_FUNCTION_INFO_V1(pltcl_call_handler);
472 /* keep non-static */
473 Datum
474 pltcl_call_handler(PG_FUNCTION_ARGS)
476 Datum retval;
477 FunctionCallInfo save_fcinfo;
478 pltcl_proc_desc *save_prodesc;
481 * Initialize interpreters if first time through
483 pltcl_init_all();
486 * Ensure that static pointers are saved/restored properly
488 save_fcinfo = pltcl_current_fcinfo;
489 save_prodesc = pltcl_current_prodesc;
491 PG_TRY();
494 * Determine if called as function or trigger and call appropriate
495 * subhandler
497 if (CALLED_AS_TRIGGER(fcinfo))
499 pltcl_current_fcinfo = NULL;
500 retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
502 else
504 pltcl_current_fcinfo = fcinfo;
505 retval = pltcl_func_handler(fcinfo);
508 PG_CATCH();
510 pltcl_current_fcinfo = save_fcinfo;
511 pltcl_current_prodesc = save_prodesc;
512 PG_RE_THROW();
514 PG_END_TRY();
516 pltcl_current_fcinfo = save_fcinfo;
517 pltcl_current_prodesc = save_prodesc;
519 return retval;
524 * Alternative handler for unsafe functions
526 PG_FUNCTION_INFO_V1(pltclu_call_handler);
528 /* keep non-static */
529 Datum
530 pltclu_call_handler(PG_FUNCTION_ARGS)
532 return pltcl_call_handler(fcinfo);
535 /**********************************************************************
536 * pltcl_func_handler() - Handler for regular function calls
537 **********************************************************************/
538 static Datum
539 pltcl_func_handler(PG_FUNCTION_ARGS)
541 pltcl_proc_desc *prodesc;
542 Tcl_Interp *volatile interp;
543 Tcl_DString tcl_cmd;
544 Tcl_DString list_tmp;
545 int i;
546 int tcl_rc;
547 Datum retval;
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;
560 else
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 ************************************************************/
574 PG_TRY();
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, "");
585 else
587 HeapTupleHeader td;
588 Oid tupType;
589 int32 tupTypmod;
590 TupleDesc tupdesc;
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);
600 tmptup.t_data = 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);
609 else
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, "");
617 else
619 char *tmp;
621 tmp = OutputFunctionCall(&prodesc->arg_out_func[i],
622 fcinfo->arg[i]);
623 UTF_BEGIN;
624 Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
625 UTF_END;
626 pfree(tmp);
631 PG_CATCH();
633 Tcl_DStringFree(&tcl_cmd);
634 Tcl_DStringFree(&list_tmp);
635 PG_RE_THROW();
637 PG_END_TRY();
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
661 * in that case.
662 ************************************************************/
663 if (SPI_finish() != SPI_OK_FINISH)
664 elog(ERROR, "SPI_finish() failed");
666 if (fcinfo->isnull)
667 retval = InputFunctionCall(&prodesc->result_in_func,
668 NULL,
669 prodesc->result_typioparam,
670 -1);
671 else
673 UTF_BEGIN;
674 retval = InputFunctionCall(&prodesc->result_in_func,
675 UTF_U2E((char *) Tcl_GetStringResult(interp)),
676 prodesc->result_typioparam,
677 -1);
678 UTF_END;
681 return retval;
685 /**********************************************************************
686 * pltcl_trigger_handler() - Handler for trigger calls
687 **********************************************************************/
688 static HeapTuple
689 pltcl_trigger_handler(PG_FUNCTION_ARGS)
691 pltcl_proc_desc *prodesc;
692 Tcl_Interp *volatile interp;
693 TriggerData *trigdata = (TriggerData *) fcinfo->context;
694 char *stroid;
695 TupleDesc tupdesc;
696 volatile HeapTuple rettup;
697 Tcl_DString tcl_cmd;
698 Tcl_DString tcl_trigtup;
699 Tcl_DString tcl_newtup;
700 int tcl_rc;
701 int i;
702 int *modattrs;
703 Datum *modvalues;
704 char *modnulls;
705 int ret_numvals;
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;
721 else
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);
733 PG_TRY();
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);
745 pfree(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);
750 pfree(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);
755 pfree(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, "");
763 else
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");
776 else
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
790 * and OLD
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;
822 else
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");
837 else
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;
845 else
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]);
853 PG_CATCH();
855 Tcl_DStringFree(&tcl_cmd);
856 Tcl_DStringFree(&tcl_trigtup);
857 Tcl_DStringFree(&tcl_newtup);
858 PG_RE_THROW();
860 PG_END_TRY();
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)
889 return rettup;
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)
900 UTF_BEGIN;
901 elog(ERROR, "could not split return value from trigger: %s",
902 UTF_U2E(Tcl_GetStringResult(interp)));
903 UTF_END;
906 /* Use a TRY to ensure ret_values will get freed */
907 PG_TRY();
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++)
916 modattrs[i] = i + 1;
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];
927 int attnum;
928 HeapTuple typeTup;
929 Oid typinput;
930 Oid typioparam;
931 FmgrInfo finfo;
933 /************************************************************
934 * Ignore ".tupno" pseudo elements (see pltcl_set_tuple_values)
935 ************************************************************/
936 if (strcmp(ret_name, ".tupno") == 0)
937 continue;
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);
945 if (attnum <= 0)
946 elog(ERROR, "cannot set system attribute \"%s\"", ret_name);
948 /************************************************************
949 * Ignore dropped columns
950 ************************************************************/
951 if (tupdesc->attrs[attnum - 1]->attisdropped)
952 continue;
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),
960 0, 0, 0);
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);
973 UTF_BEGIN;
974 modvalues[attnum - 1] = InputFunctionCall(&finfo,
975 (char *) UTF_U2E(ret_value),
976 typioparam,
977 tupdesc->attrs[attnum - 1]->atttypmod);
978 UTF_END;
981 rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
982 modattrs, modvalues, modnulls);
984 pfree(modattrs);
985 pfree(modvalues);
986 pfree(modnulls);
988 if (rettup == NULL)
989 elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
992 PG_CATCH();
994 ckfree((char *) ret_values);
995 PG_RE_THROW();
997 PG_END_TRY();
998 ckfree((char *) ret_values);
1000 return rettup;
1004 /**********************************************************************
1005 * throw_tcl_error - ereport an error returned from the Tcl interpreter
1006 **********************************************************************/
1007 static void
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
1015 * Tcl_GetVar.
1017 char *emsg;
1018 char *econtext;
1020 UTF_BEGIN;
1021 emsg = pstrdup(UTF_U2E(Tcl_GetStringResult(interp)));
1022 UTF_END;
1023 UTF_BEGIN;
1024 econtext = UTF_U2E((char *) Tcl_GetVar(interp, "errorInfo",
1025 TCL_GLOBAL_ONLY));
1026 ereport(ERROR,
1027 (errmsg("%s", emsg),
1028 errcontext("%s\nin PL/Tcl function \"%s\"",
1029 econtext, proname)));
1030 UTF_END;
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);
1044 HeapTuple procTup;
1045 Form_pg_proc procStruct;
1046 char internal_proname[128];
1047 Tcl_HashEntry *hashent;
1048 pltcl_proc_desc *prodesc = NULL;
1049 Tcl_Interp *interp;
1050 int i;
1051 int hashnew;
1052 int tcl_rc;
1054 /* We'll need the pg_proc tuple in any case... */
1055 procTup = SearchSysCache(PROCOID,
1056 ObjectIdGetDatum(fn_oid),
1057 0, 0, 0);
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 ************************************************************/
1065 if (!is_trigger)
1066 snprintf(internal_proname, sizeof(internal_proname),
1067 "__PLTcl_proc_%u", fn_oid);
1068 else
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)
1084 bool uptodate;
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));
1091 if (!uptodate)
1093 Tcl_DeleteHashEntry(hashent);
1094 hashent = NULL;
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)
1108 HeapTuple langTup;
1109 HeapTuple typeTup;
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];
1115 Datum prosrcdatum;
1116 bool isnull;
1117 char *proc_source;
1118 char buf[32];
1120 /************************************************************
1121 * Allocate a new procedure description block
1122 ************************************************************/
1123 prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
1124 if (prodesc == NULL)
1125 ereport(ERROR,
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),
1143 0, 0, 0);
1144 if (!HeapTupleIsValid(langTup))
1146 free(prodesc->user_proname);
1147 free(prodesc->internal_proname);
1148 free(prodesc);
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;
1158 else
1159 interp = pltcl_norm_interp;
1161 /************************************************************
1162 * Get the required information for input conversion of the
1163 * return value.
1164 ************************************************************/
1165 if (!is_trigger)
1167 typeTup = SearchSysCache(TYPEOID,
1168 ObjectIdGetDatum(procStruct->prorettype),
1169 0, 0, 0);
1170 if (!HeapTupleIsValid(typeTup))
1172 free(prodesc->user_proname);
1173 free(prodesc->internal_proname);
1174 free(prodesc);
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)
1184 /* okay */ ;
1185 else if (procStruct->prorettype == TRIGGEROID)
1187 free(prodesc->user_proname);
1188 free(prodesc->internal_proname);
1189 free(prodesc);
1190 ereport(ERROR,
1191 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1192 errmsg("trigger functions can only be called as triggers")));
1194 else
1196 free(prodesc->user_proname);
1197 free(prodesc->internal_proname);
1198 free(prodesc);
1199 ereport(ERROR,
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);
1210 free(prodesc);
1211 ereport(ERROR,
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 ************************************************************/
1226 if (!is_trigger)
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]),
1234 0, 0, 0);
1235 if (!HeapTupleIsValid(typeTup))
1237 free(prodesc->user_proname);
1238 free(prodesc->internal_proname);
1239 free(prodesc);
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);
1250 free(prodesc);
1251 ereport(ERROR,
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);
1262 else
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);
1270 if (i > 0)
1271 strcat(proc_internal_args, " ");
1272 strcat(proc_internal_args, buf);
1274 ReleaseSysCache(typeTup);
1277 else
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
1286 * procedure
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);
1302 if (!is_trigger)
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",
1310 i + 1, i + 1);
1311 Tcl_DStringAppend(&proc_internal_body, buf, -1);
1315 else
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,
1323 "set i 0\n"
1324 "set v 0\n"
1325 "foreach v $args {\n"
1326 " incr i\n"
1327 " set $i $v\n"
1328 "}\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);
1337 if (isnull)
1338 elog(ERROR, "null prosrc");
1339 proc_source = TextDatumGetCString(prosrcdatum);
1340 UTF_BEGIN;
1341 Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
1342 UTF_END;
1343 pfree(proc_source);
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);
1358 free(prodesc);
1359 UTF_BEGIN;
1360 elog(ERROR, "could not create internal procedure \"%s\": %s",
1361 internal_proname, UTF_U2E(Tcl_GetStringResult(interp)));
1362 UTF_END;
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);
1375 return prodesc;
1379 /**********************************************************************
1380 * pltcl_elog() - elog() support for PLTcl
1381 **********************************************************************/
1382 static int
1383 pltcl_elog(ClientData cdata, Tcl_Interp *interp,
1384 int argc, CONST84 char *argv[])
1386 volatile int level;
1387 MemoryContext oldcontext;
1389 if (argc != 3)
1391 Tcl_SetResult(interp, "syntax error - 'elog level msg'", TCL_STATIC);
1392 return TCL_ERROR;
1395 if (strcmp(argv[1], "DEBUG") == 0)
1396 level = DEBUG2;
1397 else if (strcmp(argv[1], "LOG") == 0)
1398 level = LOG;
1399 else if (strcmp(argv[1], "INFO") == 0)
1400 level = INFO;
1401 else if (strcmp(argv[1], "NOTICE") == 0)
1402 level = NOTICE;
1403 else if (strcmp(argv[1], "WARNING") == 0)
1404 level = WARNING;
1405 else if (strcmp(argv[1], "ERROR") == 0)
1406 level = ERROR;
1407 else if (strcmp(argv[1], "FATAL") == 0)
1408 level = FATAL;
1409 else
1411 Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
1412 "'", NULL);
1413 return TCL_ERROR;
1416 if (level == ERROR)
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
1421 * handler.
1423 Tcl_SetResult(interp, (char *) argv[2], TCL_VOLATILE);
1424 return TCL_ERROR;
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;
1437 PG_TRY();
1439 UTF_BEGIN;
1440 elog(level, "%s", UTF_U2E(argv[2]));
1441 UTF_END;
1443 PG_CATCH();
1445 ErrorData *edata;
1447 /* Must reset elog.c's state */
1448 MemoryContextSwitchTo(oldcontext);
1449 edata = CopyErrorData();
1450 FlushErrorState();
1452 /* Pass the error message to Tcl */
1453 UTF_BEGIN;
1454 Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
1455 UTF_END;
1456 FreeErrorData(edata);
1458 return TCL_ERROR;
1460 PG_END_TRY();
1462 return TCL_OK;
1466 /**********************************************************************
1467 * pltcl_quote() - quote literal strings that are to
1468 * be used in SPI_execute query strings
1469 **********************************************************************/
1470 static int
1471 pltcl_quote(ClientData cdata, Tcl_Interp *interp,
1472 int argc, CONST84 char *argv[])
1474 char *tmp;
1475 const char *cp1;
1476 char *cp2;
1478 /************************************************************
1479 * Check call syntax
1480 ************************************************************/
1481 if (argc != 2)
1483 Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_STATIC);
1484 return TCL_ERROR;
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);
1492 cp1 = argv[1];
1493 cp2 = tmp;
1495 /************************************************************
1496 * Walk through string and double every quote and backslash
1497 ************************************************************/
1498 while (*cp1)
1500 if (*cp1 == '\'')
1501 *cp2++ = '\'';
1502 else
1504 if (*cp1 == '\\')
1505 *cp2++ = '\\';
1507 *cp2++ = *cp1++;
1510 /************************************************************
1511 * Terminate the string and set it as result
1512 ************************************************************/
1513 *cp2 = '\0';
1514 Tcl_SetResult(interp, tmp, TCL_VOLATILE);
1515 pfree(tmp);
1516 return TCL_OK;
1520 /**********************************************************************
1521 * pltcl_argisnull() - determine if a specific argument is NULL
1522 **********************************************************************/
1523 static int
1524 pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
1525 int argc, CONST84 char *argv[])
1527 int argno;
1528 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
1530 /************************************************************
1531 * Check call syntax
1532 ************************************************************/
1533 if (argc != 2)
1535 Tcl_SetResult(interp, "syntax error - 'argisnull argno'",
1536 TCL_STATIC);
1537 return TCL_ERROR;
1540 /************************************************************
1541 * Check that we're called as a normal function
1542 ************************************************************/
1543 if (fcinfo == NULL)
1545 Tcl_SetResult(interp, "argisnull cannot be used in triggers",
1546 TCL_STATIC);
1547 return TCL_ERROR;
1550 /************************************************************
1551 * Get the argument number
1552 ************************************************************/
1553 if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
1554 return TCL_ERROR;
1556 /************************************************************
1557 * Check that the argno is valid
1558 ************************************************************/
1559 argno--;
1560 if (argno < 0 || argno >= fcinfo->nargs)
1562 Tcl_SetResult(interp, "argno out of range", TCL_STATIC);
1563 return TCL_ERROR;
1566 /************************************************************
1567 * Get the requested NULL state
1568 ************************************************************/
1569 if (PG_ARGISNULL(argno))
1570 Tcl_SetResult(interp, "1", TCL_STATIC);
1571 else
1572 Tcl_SetResult(interp, "0", TCL_STATIC);
1574 return TCL_OK;
1578 /**********************************************************************
1579 * pltcl_returnnull() - Cause a NULL return from a function
1580 **********************************************************************/
1581 static int
1582 pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
1583 int argc, CONST84 char *argv[])
1585 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
1587 /************************************************************
1588 * Check call syntax
1589 ************************************************************/
1590 if (argc != 1)
1592 Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_STATIC);
1593 return TCL_ERROR;
1596 /************************************************************
1597 * Check that we're called as a normal function
1598 ************************************************************/
1599 if (fcinfo == NULL)
1601 Tcl_SetResult(interp, "return_null cannot be used in triggers",
1602 TCL_STATIC);
1603 return TCL_ERROR;
1606 /************************************************************
1607 * Set the NULL return flag and cause Tcl to return from the
1608 * procedure.
1609 ************************************************************/
1610 fcinfo->isnull = true;
1612 return TCL_RETURN;
1616 /*----------
1617 * Support for running SPI operations inside subtransactions
1619 * Intended usage pattern is:
1621 * MemoryContext oldcontext = CurrentMemoryContext;
1622 * ResourceOwner oldowner = CurrentResourceOwner;
1624 * ...
1625 * pltcl_subtrans_begin(oldcontext, oldowner);
1626 * PG_TRY();
1628 * do something risky;
1629 * pltcl_subtrans_commit(oldcontext, oldowner);
1631 * PG_CATCH();
1633 * pltcl_subtrans_abort(interp, oldcontext, oldowner);
1634 * return TCL_ERROR;
1636 * PG_END_TRY();
1637 * return TCL_OK;
1638 *----------
1640 static void
1641 pltcl_subtrans_begin(MemoryContext oldcontext, ResourceOwner oldowner)
1643 BeginInternalSubTransaction(NULL);
1645 /* Want to run inside function's memory context */
1646 MemoryContextSwitchTo(oldcontext);
1649 static void
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();
1664 static void
1665 pltcl_subtrans_abort(Tcl_Interp *interp,
1666 MemoryContext oldcontext, ResourceOwner oldowner)
1668 ErrorData *edata;
1670 /* Save error info */
1671 MemoryContextSwitchTo(oldcontext);
1672 edata = CopyErrorData();
1673 FlushErrorState();
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
1683 * connected state.
1685 SPI_restore_connection();
1687 /* Pass the error message to Tcl */
1688 UTF_BEGIN;
1689 Tcl_SetResult(interp, UTF_E2U(edata->message), TCL_VOLATILE);
1690 UTF_END;
1691 FreeErrorData(edata);
1695 /**********************************************************************
1696 * pltcl_SPI_execute() - The builtin SPI_execute command
1697 * for the Tcl interpreter
1698 **********************************************************************/
1699 static int
1700 pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp,
1701 int argc, CONST84 char *argv[])
1703 int my_rc;
1704 int spi_rc;
1705 int query_idx;
1706 int i;
1707 int count = 0;
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 "
1714 "?-count n? "
1715 "?-array name? query ?loop body?";
1717 /************************************************************
1718 * Check the call syntax and get the options
1719 ************************************************************/
1720 if (argc < 2)
1722 Tcl_SetResult(interp, usage, TCL_STATIC);
1723 return TCL_ERROR;
1726 i = 1;
1727 while (i < argc)
1729 if (strcmp(argv[i], "-array") == 0)
1731 if (++i >= argc)
1733 Tcl_SetResult(interp, usage, TCL_STATIC);
1734 return TCL_ERROR;
1736 arrayname = argv[i++];
1737 continue;
1740 if (strcmp(argv[i], "-count") == 0)
1742 if (++i >= argc)
1744 Tcl_SetResult(interp, usage, TCL_STATIC);
1745 return TCL_ERROR;
1747 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
1748 return TCL_ERROR;
1749 continue;
1752 break;
1755 query_idx = i;
1756 if (query_idx >= argc || query_idx + 2 < argc)
1758 Tcl_SetResult(interp, usage, TCL_STATIC);
1759 return TCL_ERROR;
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
1766 * errors sanely
1767 ************************************************************/
1769 pltcl_subtrans_begin(oldcontext, oldowner);
1771 PG_TRY();
1773 UTF_BEGIN;
1774 spi_rc = SPI_execute(UTF_U2E(argv[query_idx]),
1775 pltcl_current_prodesc->fn_readonly, count);
1776 UTF_END;
1778 my_rc = pltcl_process_SPI_result(interp,
1779 arrayname,
1780 loop_body,
1781 spi_rc,
1782 SPI_tuptable,
1783 SPI_processed);
1785 pltcl_subtrans_commit(oldcontext, oldowner);
1787 PG_CATCH();
1789 pltcl_subtrans_abort(interp, oldcontext, oldowner);
1790 return TCL_ERROR;
1792 PG_END_TRY();
1794 return my_rc;
1798 * Process the result from SPI_execute or SPI_execute_plan
1800 * Shared code between pltcl_SPI_execute and pltcl_SPI_execute_plan
1802 static int
1803 pltcl_process_SPI_result(Tcl_Interp *interp,
1804 CONST84 char *arrayname,
1805 CONST84 char *loop_body,
1806 int spi_rc,
1807 SPITupleTable *tuptable,
1808 int ntuples)
1810 int my_rc = TCL_OK;
1811 char buf[64];
1812 int i;
1813 int loop_rc;
1814 HeapTuple *tuples;
1815 TupleDesc tupdesc;
1817 switch (spi_rc)
1819 case SPI_OK_SELINTO:
1820 case SPI_OK_INSERT:
1821 case SPI_OK_DELETE:
1822 case SPI_OK_UPDATE:
1823 snprintf(buf, sizeof(buf), "%d", ntuples);
1824 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1825 break;
1827 case SPI_OK_UTILITY:
1828 case SPI_OK_REWRITTEN:
1829 if (tuptable == NULL)
1831 Tcl_SetResult(interp, "0", TCL_STATIC);
1832 break;
1834 /* FALL THRU for utility returning tuples */
1836 case SPI_OK_SELECT:
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)
1853 if (ntuples > 0)
1854 pltcl_set_tuple_values(interp, arrayname, 0,
1855 tuples[0], tupdesc);
1857 else
1860 * There is a loop body - process all tuples and evaluate the
1861 * body on each
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)
1871 continue;
1872 if (loop_rc == TCL_CONTINUE)
1873 continue;
1874 if (loop_rc == TCL_RETURN)
1876 my_rc = TCL_RETURN;
1877 break;
1879 if (loop_rc == TCL_BREAK)
1880 break;
1881 my_rc = TCL_ERROR;
1882 break;
1886 if (my_rc == TCL_OK)
1888 snprintf(buf, sizeof(buf), "%d", ntuples);
1889 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1891 break;
1893 default:
1894 Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ",
1895 SPI_result_code_string(spi_rc), NULL);
1896 my_rc = TCL_ERROR;
1897 break;
1900 SPI_freetuptable(tuptable);
1902 return my_rc;
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 **********************************************************************/
1914 static int
1915 pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
1916 int argc, CONST84 char *argv[])
1918 int nargs;
1919 CONST84 char **args;
1920 pltcl_query_desc *qdesc;
1921 void *plan;
1922 int i;
1923 Tcl_HashEntry *hashent;
1924 int hashnew;
1925 Tcl_HashTable *query_hash;
1926 MemoryContext oldcontext = CurrentMemoryContext;
1927 ResourceOwner oldowner = CurrentResourceOwner;
1929 /************************************************************
1930 * Check the call syntax
1931 ************************************************************/
1932 if (argc != 3)
1934 Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
1935 TCL_STATIC);
1936 return TCL_ERROR;
1939 /************************************************************
1940 * Split the argument type list
1941 ************************************************************/
1942 if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
1943 return TCL_ERROR;
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
1957 * errors sanely
1958 ************************************************************/
1960 pltcl_subtrans_begin(oldcontext, oldowner);
1962 PG_TRY();
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++)
1971 Oid typId,
1972 typInput,
1973 typIOParam;
1974 int32 typmod;
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 ************************************************************/
1988 UTF_BEGIN;
1989 plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
1990 UTF_END;
1992 if (plan == NULL)
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 */
2004 SPI_freeplan(plan);
2006 pltcl_subtrans_commit(oldcontext, oldowner);
2008 PG_CATCH();
2010 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2012 free(qdesc->argtypes);
2013 free(qdesc->arginfuncs);
2014 free(qdesc->argtypioparams);
2015 free(qdesc);
2016 ckfree((char *) args);
2018 return TCL_ERROR;
2020 PG_END_TRY();
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;
2028 else
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);
2038 return TCL_OK;
2042 /**********************************************************************
2043 * pltcl_SPI_execute_plan() - Execute a prepared plan
2044 **********************************************************************/
2045 static int
2046 pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
2047 int argc, CONST84 char *argv[])
2049 int my_rc;
2050 int spi_rc;
2051 int i;
2052 int j;
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;
2058 int count = 0;
2059 int callnargs;
2060 CONST84 char **callargs = NULL;
2061 Datum *argvalues;
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 ************************************************************/
2073 i = 1;
2074 while (i < argc)
2076 if (strcmp(argv[i], "-array") == 0)
2078 if (++i >= argc)
2080 Tcl_SetResult(interp, usage, TCL_STATIC);
2081 return TCL_ERROR;
2083 arrayname = argv[i++];
2084 continue;
2086 if (strcmp(argv[i], "-nulls") == 0)
2088 if (++i >= argc)
2090 Tcl_SetResult(interp, usage, TCL_STATIC);
2091 return TCL_ERROR;
2093 nulls = argv[i++];
2094 continue;
2096 if (strcmp(argv[i], "-count") == 0)
2098 if (++i >= argc)
2100 Tcl_SetResult(interp, usage, TCL_STATIC);
2101 return TCL_ERROR;
2103 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
2104 return TCL_ERROR;
2105 continue;
2108 break;
2111 /************************************************************
2112 * Get the prepared plan descriptor by its key
2113 ************************************************************/
2114 if (i >= argc)
2116 Tcl_SetResult(interp, usage, TCL_STATIC);
2117 return TCL_ERROR;
2120 if (interp == pltcl_norm_interp)
2121 query_hash = pltcl_norm_query_hash;
2122 else
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);
2129 return TCL_ERROR;
2131 qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
2132 i++;
2134 /************************************************************
2135 * If a nulls string is given, check for correct length
2136 ************************************************************/
2137 if (nulls != NULL)
2139 if (strlen(nulls) != qdesc->nargs)
2141 Tcl_SetResult(interp,
2142 "length of nulls string doesn't match # of arguments",
2143 TCL_STATIC);
2144 return TCL_ERROR;
2148 /************************************************************
2149 * If there was a argtype list on preparation, we need
2150 * an argument value list now
2151 ************************************************************/
2152 if (qdesc->nargs > 0)
2154 if (i >= argc)
2156 Tcl_SetResult(interp, "missing argument list", TCL_STATIC);
2157 return TCL_ERROR;
2160 /************************************************************
2161 * Split the argument values
2162 ************************************************************/
2163 if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
2164 return TCL_ERROR;
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",
2173 TCL_STATIC);
2174 ckfree((char *) callargs);
2175 return TCL_ERROR;
2178 else
2179 callnargs = 0;
2181 /************************************************************
2182 * Get loop body if present
2183 ************************************************************/
2184 if (i < argc)
2185 loop_body = argv[i++];
2187 if (i != argc)
2189 Tcl_SetResult(interp, usage, TCL_STATIC);
2190 return TCL_ERROR;
2193 /************************************************************
2194 * Execute the plan inside a sub-transaction, so we can cope with
2195 * errors sanely
2196 ************************************************************/
2198 pltcl_subtrans_begin(oldcontext, oldowner);
2200 PG_TRY();
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],
2213 NULL,
2214 qdesc->argtypioparams[j],
2215 -1);
2217 else
2219 UTF_BEGIN;
2220 argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j],
2221 (char *) UTF_U2E(callargs[j]),
2222 qdesc->argtypioparams[j],
2223 -1);
2224 UTF_END;
2228 if (callargs)
2229 ckfree((char *) callargs);
2230 callargs = NULL;
2232 /************************************************************
2233 * Execute the plan
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,
2239 arrayname,
2240 loop_body,
2241 spi_rc,
2242 SPI_tuptable,
2243 SPI_processed);
2245 pltcl_subtrans_commit(oldcontext, oldowner);
2247 PG_CATCH();
2249 pltcl_subtrans_abort(interp, oldcontext, oldowner);
2251 if (callargs)
2252 ckfree((char *) callargs);
2254 return TCL_ERROR;
2256 PG_END_TRY();
2258 return my_rc;
2262 /**********************************************************************
2263 * pltcl_SPI_lastoid() - return the last oid. To
2264 * be used after insert queries
2265 **********************************************************************/
2266 static int
2267 pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
2268 int argc, CONST84 char *argv[])
2270 char buf[64];
2272 snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
2273 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2274 return TCL_OK;
2278 /**********************************************************************
2279 * pltcl_set_tuple_values() - Set variables for all attributes
2280 * of a given tuple
2281 **********************************************************************/
2282 static void
2283 pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname,
2284 int tupno, HeapTuple tuple, TupleDesc tupdesc)
2286 int i;
2287 char *outputstr;
2288 char buf[64];
2289 Datum attr;
2290 bool isnull;
2292 CONST84 char *attname;
2293 HeapTuple typeTup;
2294 Oid typoutput;
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)
2306 arrptr = &attname;
2307 nameptr = &nullname;
2309 else
2311 arrptr = &arrayname;
2312 nameptr = &attname;
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)
2321 continue;
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),
2339 0, 0, 0);
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
2349 * If not, unset it
2351 * Hmmm - Null attributes will cause functions to
2352 * crash if they don't expect them - need something
2353 * smarter here.
2354 ************************************************************/
2355 if (!isnull && OidIsValid(typoutput))
2357 outputstr = OidOutputFunctionCall(typoutput, attr);
2358 UTF_BEGIN;
2359 Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
2360 UTF_END;
2361 pfree(outputstr);
2363 else
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 **********************************************************************/
2373 static void
2374 pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
2375 Tcl_DString *retval)
2377 int i;
2378 char *outputstr;
2379 Datum attr;
2380 bool isnull;
2382 char *attname;
2383 HeapTuple typeTup;
2384 Oid typoutput;
2386 for (i = 0; i < tupdesc->natts; i++)
2388 /* ignore dropped attributes */
2389 if (tupdesc->attrs[i]->attisdropped)
2390 continue;
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),
2408 0, 0, 0);
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
2418 * value to the list
2420 * Hmmm - Null attributes will cause functions to
2421 * crash if they don't expect them - need something
2422 * smarter here.
2423 ************************************************************/
2424 if (!isnull && OidIsValid(typoutput))
2426 outputstr = OidOutputFunctionCall(typoutput, attr);
2427 Tcl_DStringAppendElement(retval, attname);
2428 UTF_BEGIN;
2429 Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));
2430 UTF_END;
2431 pfree(outputstr);