1 /**********************************************************************
2 * pltcl.c - PostgreSQL support for Tcl as
3 * procedural language (PL)
5 * This software is copyrighted by Jan Wieck - Hamburg.
7 * The author hereby grants permission to use, copy, modify,
8 * distribute, and license this software and its documentation
9 * for any purpose, provided that existing copyright notices are
10 * retained in all copies and that this notice is included
11 * verbatim in any distributions. No written agreement, license,
12 * or royalty fee is required for any of the authorized uses.
13 * Modifications to this software may be copyrighted by their
14 * author and need not follow the licensing terms described
15 * here, provided that the new terms are clearly indicated on
16 * the first page of each file where they apply.
18 * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
19 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
20 * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
21 * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
22 * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
25 * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
28 * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
29 * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
30 * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
31 * ENHANCEMENTS, OR MODIFICATIONS.
34 * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.65 2002/10/14 04:20:52 momjian Exp $
36 **********************************************************************/
46 #include "access/heapam.h"
47 #include "catalog/pg_language.h"
48 #include "catalog/pg_proc.h"
49 #include "catalog/pg_type.h"
50 #include "commands/trigger.h"
51 #include "executor/spi.h"
53 #include "nodes/makefuncs.h"
54 #include "parser/parse_type.h"
55 #include "tcop/tcopprot.h"
56 #include "utils/builtins.h"
57 #include "utils/syscache.h"
59 #if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
60 && TCL_MINOR_VERSION > 0
62 #include "mb/pg_wchar.h"
64 static unsigned char *
65 utf_u2e(unsigned char *src)
67 return pg_do_encoding_conversion(src, strlen(src), PG_UTF8, GetDatabaseEncoding());
70 static unsigned char *
71 utf_e2u(unsigned char *src)
73 return pg_do_encoding_conversion(src, strlen(src), GetDatabaseEncoding(), PG_UTF8);
77 #define UTF_BEGIN do { \
78 unsigned char *_pltcl_utf_src; \
79 unsigned char *_pltcl_utf_dst
80 #define UTF_END if (_pltcl_utf_src!=_pltcl_utf_dst) \
81 pfree(_pltcl_utf_dst); } while (0)
82 #define UTF_U2E(x) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
83 #define UTF_E2U(x) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
87 #define UTF_U2E(x) (x)
88 #define UTF_E2U(x) (x)
89 #endif /* PLTCL_UTF */
91 /**********************************************************************
92 * The information we cache about loaded procedures
93 **********************************************************************/
94 typedef struct pltcl_proc_desc
97 TransactionId fn_xmin;
100 FmgrInfo result_in_func;
103 FmgrInfo arg_out_func[FUNC_MAX_ARGS];
104 Oid arg_out_elem[FUNC_MAX_ARGS];
105 int arg_is_rel[FUNC_MAX_ARGS];
109 /**********************************************************************
110 * The information we cache about prepared and saved plans
111 **********************************************************************/
112 typedef struct pltcl_query_desc
118 FmgrInfo *arginfuncs;
123 /**********************************************************************
125 **********************************************************************/
126 static int pltcl_firstcall = 1;
127 static int pltcl_call_level = 0;
128 static int pltcl_restart_in_progress = 0;
129 static Tcl_Interp *pltcl_hold_interp = NULL;
130 static Tcl_Interp *pltcl_norm_interp = NULL;
131 static Tcl_Interp *pltcl_safe_interp = NULL;
132 static Tcl_HashTable *pltcl_proc_hash = NULL;
133 static Tcl_HashTable *pltcl_norm_query_hash = NULL;
134 static Tcl_HashTable *pltcl_safe_query_hash = NULL;
135 static FunctionCallInfo pltcl_current_fcinfo = NULL;
137 /**********************************************************************
138 * Forward declarations
139 **********************************************************************/
140 static void pltcl_init_all(void);
141 static void pltcl_init_interp(Tcl_Interp *interp);
143 static void pltcl_init_load_unknown(Tcl_Interp *interp);
145 Datum pltcl_call_handler(PG_FUNCTION_ARGS);
146 Datum pltclu_call_handler(PG_FUNCTION_ARGS);
148 static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
150 static HeapTuple pltcl_trigger_handler(PG_FUNCTION_ARGS);
152 static pltcl_proc_desc *compile_pltcl_function(Oid fn_oid, bool is_trigger);
154 static int pltcl_elog(ClientData cdata, Tcl_Interp *interp,
155 int argc, char *argv[]);
156 static int pltcl_quote(ClientData cdata, Tcl_Interp *interp,
157 int argc, char *argv[]);
158 static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
159 int argc, char *argv[]);
160 static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
161 int argc, char *argv[]);
163 static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
164 int argc, char *argv[]);
165 static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
166 int argc, char *argv[]);
167 static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
168 int argc, char *argv[]);
170 static void pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname,
171 int tupno, HeapTuple tuple, TupleDesc tupdesc);
172 static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
173 Tcl_DString *retval);
174 static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
175 int argc, char *argv[]);
178 * This routine is a crock, and so is everyplace that calls it. The problem
179 * is that the cached form of pltcl functions/queries is allocated permanently
180 * (mostly via malloc()) and never released until backend exit. Subsidiary
181 * data structures such as fmgr info records therefore must live forever
182 * as well. A better implementation would store all this stuff in a per-
183 * function memory context that could be reclaimed at need. In the meantime,
184 * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
185 * it might allocate, and whatever the eventual function might allocate using
186 * fn_mcxt, will live forever too.
189 perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
191 fmgr_info_cxt(functionId, finfo, TopMemoryContext);
194 /**********************************************************************
195 * pltcl_init_all() - Initialize all
196 **********************************************************************/
200 /************************************************************
201 * Do initialization only once
202 ************************************************************/
203 if (!pltcl_firstcall)
206 /************************************************************
207 * Create the dummy hold interpreter to prevent close of
208 * stdout and stderr on DeleteInterp
209 ************************************************************/
210 if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
212 elog(ERROR, "pltcl: internal error - cannot create 'hold' "
216 /************************************************************
217 * Create the two interpreters
218 ************************************************************/
219 if ((pltcl_norm_interp =
220 Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
223 "pltcl: internal error - cannot create 'normal' interpreter");
225 pltcl_init_interp(pltcl_norm_interp);
227 if ((pltcl_safe_interp =
228 Tcl_CreateSlave(pltcl_hold_interp, "safe", 1)) == NULL)
231 "pltcl: internal error - cannot create 'safe' interpreter");
233 pltcl_init_interp(pltcl_safe_interp);
235 /************************************************************
236 * Initialize the proc and query hash tables
237 ************************************************************/
238 pltcl_proc_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
239 pltcl_norm_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
240 pltcl_safe_query_hash = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable));
241 Tcl_InitHashTable(pltcl_proc_hash, TCL_STRING_KEYS);
242 Tcl_InitHashTable(pltcl_norm_query_hash, TCL_STRING_KEYS);
243 Tcl_InitHashTable(pltcl_safe_query_hash, TCL_STRING_KEYS);
250 /**********************************************************************
251 * pltcl_init_interp() - initialize a Tcl interpreter
252 **********************************************************************/
254 pltcl_init_interp(Tcl_Interp *interp)
256 /************************************************************
257 * Install the commands for SPI support in the interpreter
258 ************************************************************/
259 Tcl_CreateCommand(interp, "elog",
260 pltcl_elog, NULL, NULL);
261 Tcl_CreateCommand(interp, "quote",
262 pltcl_quote, NULL, NULL);
263 Tcl_CreateCommand(interp, "argisnull",
264 pltcl_argisnull, NULL, NULL);
265 Tcl_CreateCommand(interp, "return_null",
266 pltcl_returnnull, NULL, NULL);
268 Tcl_CreateCommand(interp, "spi_exec",
269 pltcl_SPI_exec, NULL, NULL);
270 Tcl_CreateCommand(interp, "spi_prepare",
271 pltcl_SPI_prepare, NULL, NULL);
272 Tcl_CreateCommand(interp, "spi_execp",
273 pltcl_SPI_execp, NULL, NULL);
274 Tcl_CreateCommand(interp, "spi_lastoid",
275 pltcl_SPI_lastoid, NULL, NULL);
277 /************************************************************
278 * Try to load the unknown procedure from pltcl_modules
279 ************************************************************/
280 if (SPI_connect() != SPI_OK_CONNECT)
281 elog(ERROR, "pltcl_init_interp(): SPI_connect failed");
282 pltcl_init_load_unknown(interp);
283 if (SPI_finish() != SPI_OK_FINISH)
284 elog(ERROR, "pltcl_init_interp(): SPI_finish failed");
288 /**********************************************************************
289 * pltcl_init_load_unknown() - Load the unknown procedure from
290 * table pltcl_modules (if it exists)
291 **********************************************************************/
293 pltcl_init_load_unknown(Tcl_Interp *interp)
297 Tcl_DString unknown_src;
302 /************************************************************
303 * Check if table pltcl_modules exists
304 ************************************************************/
305 spi_rc = SPI_exec("select 1 from pg_class "
306 "where relname = 'pltcl_modules'", 1);
307 SPI_freetuptable(SPI_tuptable);
308 if (spi_rc != SPI_OK_SELECT)
309 elog(ERROR, "pltcl_init_load_unknown(): select from pg_class failed");
310 if (SPI_processed == 0)
313 /************************************************************
314 * Read all the row's from it where modname = 'unknown' in
315 * the order of modseq
316 ************************************************************/
317 Tcl_DStringInit(&unknown_src);
319 spi_rc = SPI_exec("select modseq, modsrc from pltcl_modules "
320 "where modname = 'unknown' "
321 "order by modseq", 0);
322 if (spi_rc != SPI_OK_SELECT)
324 elog(ERROR, "pltcl_init_load_unknown(): select from pltcl_modules "
328 /************************************************************
329 * If there's nothing, module unknown doesn't exist
330 ************************************************************/
331 if (SPI_processed == 0)
333 Tcl_DStringFree(&unknown_src);
334 SPI_freetuptable(SPI_tuptable);
335 elog(WARNING, "pltcl: Module unknown not found in pltcl_modules");
339 /************************************************************
340 * There is a module named unknown. Resemble the
341 * source from the modsrc attributes and evaluate
342 * it in the Tcl interpreter
343 ************************************************************/
344 fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
346 for (i = 0; i < SPI_processed; i++)
348 part = SPI_getvalue(SPI_tuptable->vals[i],
349 SPI_tuptable->tupdesc, fno);
353 Tcl_DStringAppend(&unknown_src, UTF_E2U(part), -1);
358 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
359 Tcl_DStringFree(&unknown_src);
360 SPI_freetuptable(SPI_tuptable);
364 /**********************************************************************
365 * pltcl_call_handler - This is the only visible function
366 * of the PL interpreter. The PostgreSQL
367 * function manager and trigger manager
368 * call this function for execution of
370 **********************************************************************/
371 PG_FUNCTION_INFO_V1(pltcl_call_handler);
373 /* keep non-static */
375 pltcl_call_handler(PG_FUNCTION_ARGS)
378 FunctionCallInfo save_fcinfo;
380 /************************************************************
381 * Initialize interpreters on first call
382 ************************************************************/
386 /************************************************************
387 * Connect to SPI manager
388 ************************************************************/
389 if (SPI_connect() != SPI_OK_CONNECT)
390 elog(ERROR, "pltcl: cannot connect to SPI manager");
391 /************************************************************
392 * Keep track about the nesting of Tcl-SPI-Tcl-... calls
393 ************************************************************/
396 /************************************************************
397 * Determine if called as function or trigger and
398 * call appropriate subhandler
399 ************************************************************/
400 save_fcinfo = pltcl_current_fcinfo;
402 if (CALLED_AS_TRIGGER(fcinfo))
404 pltcl_current_fcinfo = NULL;
405 retval = PointerGetDatum(pltcl_trigger_handler(fcinfo));
409 pltcl_current_fcinfo = fcinfo;
410 retval = pltcl_func_handler(fcinfo);
413 pltcl_current_fcinfo = save_fcinfo;
422 * Alternate handler for unsafe functions
424 PG_FUNCTION_INFO_V1(pltclu_call_handler);
426 /* keep non-static */
428 pltclu_call_handler(PG_FUNCTION_ARGS)
430 return pltcl_call_handler(fcinfo);
433 /**********************************************************************
434 * pltcl_func_handler() - Handler for regular function calls
435 **********************************************************************/
437 pltcl_func_handler(PG_FUNCTION_ARGS)
439 pltcl_proc_desc *prodesc;
440 Tcl_Interp *volatile interp;
442 Tcl_DString list_tmp;
446 sigjmp_buf save_restart;
448 /* Find or compile the function */
449 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, false);
451 if (prodesc->lanpltrusted)
452 interp = pltcl_safe_interp;
454 interp = pltcl_norm_interp;
456 /************************************************************
457 * Create the tcl command to call the internal
458 * proc in the Tcl interpreter
459 ************************************************************/
460 Tcl_DStringInit(&tcl_cmd);
461 Tcl_DStringInit(&list_tmp);
462 Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
464 /************************************************************
465 * Catch elog(ERROR) during build of the Tcl command
466 ************************************************************/
467 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
468 if (sigsetjmp(Warn_restart, 1) != 0)
470 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
471 Tcl_DStringFree(&tcl_cmd);
472 Tcl_DStringFree(&list_tmp);
473 pltcl_restart_in_progress = 1;
474 if (--pltcl_call_level == 0)
475 pltcl_restart_in_progress = 0;
476 siglongjmp(Warn_restart, 1);
479 /************************************************************
480 * Add all call arguments to the command
481 ************************************************************/
482 for (i = 0; i < prodesc->nargs; i++)
484 if (prodesc->arg_is_rel[i])
486 /**************************************************
487 * For tuple values, add a list for 'array set ...'
488 **************************************************/
489 TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i];
491 Assert(slot != NULL && !fcinfo->argnull[i]);
492 Tcl_DStringInit(&list_tmp);
493 pltcl_build_tuple_argument(slot->val,
494 slot->ttc_tupleDescriptor,
496 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&list_tmp));
497 Tcl_DStringFree(&list_tmp);
498 Tcl_DStringInit(&list_tmp);
502 /**************************************************
503 * Single values are added as string element
504 * of their external representation
505 **************************************************/
506 if (fcinfo->argnull[i])
507 Tcl_DStringAppendElement(&tcl_cmd, "");
512 tmp = DatumGetCString(FunctionCall3(&prodesc->arg_out_func[i],
514 ObjectIdGetDatum(prodesc->arg_out_elem[i]),
517 Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp));
523 Tcl_DStringFree(&list_tmp);
524 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
526 /************************************************************
527 * Call the Tcl function
528 ************************************************************/
529 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
530 Tcl_DStringFree(&tcl_cmd);
532 /************************************************************
533 * Check the return code from Tcl and handle
534 * our special restart mechanism to get rid
535 * of all nested call levels on transaction
537 ************************************************************/
538 if (tcl_rc != TCL_OK || pltcl_restart_in_progress)
540 if (!pltcl_restart_in_progress)
542 pltcl_restart_in_progress = 1;
543 if (--pltcl_call_level == 0)
544 pltcl_restart_in_progress = 0;
546 elog(ERROR, "pltcl: %s\n%s", interp->result,
547 UTF_U2E(Tcl_GetVar(interp, "errorInfo",
551 if (--pltcl_call_level == 0)
552 pltcl_restart_in_progress = 0;
553 siglongjmp(Warn_restart, 1);
556 /************************************************************
557 * Convert the result value from the Tcl interpreter
558 * into its PostgreSQL data format and return it.
559 * Again, the function call could fire an elog and we
560 * have to count for the current interpreter level we are
561 * on. The save_restart from above is still good.
562 ************************************************************/
563 if (sigsetjmp(Warn_restart, 1) != 0)
565 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
566 pltcl_restart_in_progress = 1;
567 if (--pltcl_call_level == 0)
568 pltcl_restart_in_progress = 0;
569 siglongjmp(Warn_restart, 1);
572 /************************************************************
573 * Disconnect from SPI manager and then create the return
574 * values datum (if the input function does a palloc for it
575 * this must not be allocated in the SPI memory context
576 * because SPI_finish would free it). But don't try to call
577 * the result_in_func if we've been told to return a NULL;
578 * the contents of interp->result may not be a valid value of
579 * the result type in that case.
580 ************************************************************/
581 if (SPI_finish() != SPI_OK_FINISH)
582 elog(ERROR, "pltcl: SPI_finish() failed");
589 retval = FunctionCall3(&prodesc->result_in_func,
590 PointerGetDatum(UTF_U2E(interp->result)),
591 ObjectIdGetDatum(prodesc->result_in_elem),
596 /************************************************************
597 * Finally we may restore normal error handling.
598 ************************************************************/
599 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
605 /**********************************************************************
606 * pltcl_trigger_handler() - Handler for trigger calls
607 **********************************************************************/
609 pltcl_trigger_handler(PG_FUNCTION_ARGS)
611 pltcl_proc_desc *prodesc;
612 Tcl_Interp *volatile interp;
613 TriggerData *trigdata = (TriggerData *) fcinfo->context;
616 volatile HeapTuple rettup;
618 Tcl_DString tcl_trigtup;
619 Tcl_DString tcl_newtup;
630 sigjmp_buf save_restart;
632 /* Find or compile the function */
633 prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, true);
635 if (prodesc->lanpltrusted)
636 interp = pltcl_safe_interp;
638 interp = pltcl_norm_interp;
640 tupdesc = trigdata->tg_relation->rd_att;
642 /************************************************************
643 * Create the tcl command to call the internal
644 * proc in the interpreter
645 ************************************************************/
646 Tcl_DStringInit(&tcl_cmd);
647 Tcl_DStringInit(&tcl_trigtup);
648 Tcl_DStringInit(&tcl_newtup);
650 /************************************************************
651 * We call external functions below - care for elog(ERROR)
652 ************************************************************/
653 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
654 if (sigsetjmp(Warn_restart, 1) != 0)
656 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
657 Tcl_DStringFree(&tcl_cmd);
658 Tcl_DStringFree(&tcl_trigtup);
659 Tcl_DStringFree(&tcl_newtup);
660 pltcl_restart_in_progress = 1;
661 if (--pltcl_call_level == 0)
662 pltcl_restart_in_progress = 0;
663 siglongjmp(Warn_restart, 1);
666 /* The procedure name */
667 Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
669 /* The trigger name for argument TG_name */
670 Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgname);
672 /* The oid of the trigger relation for argument TG_relid */
673 stroid = DatumGetCString(DirectFunctionCall1(oidout,
674 ObjectIdGetDatum(trigdata->tg_relation->rd_id)));
675 Tcl_DStringAppendElement(&tcl_cmd, stroid);
678 /* A list of attribute names for argument TG_relatts */
679 Tcl_DStringAppendElement(&tcl_trigtup, "");
680 for (i = 0; i < tupdesc->natts; i++)
681 Tcl_DStringAppendElement(&tcl_trigtup,
682 NameStr(tupdesc->attrs[i]->attname));
683 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
684 Tcl_DStringFree(&tcl_trigtup);
685 Tcl_DStringInit(&tcl_trigtup);
687 /* The when part of the event for TG_when */
688 if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
689 Tcl_DStringAppendElement(&tcl_cmd, "BEFORE");
690 else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
691 Tcl_DStringAppendElement(&tcl_cmd, "AFTER");
693 Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN");
695 /* The level part of the event for TG_level */
696 if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
697 Tcl_DStringAppendElement(&tcl_cmd, "ROW");
698 else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
699 Tcl_DStringAppendElement(&tcl_cmd, "STATEMENT");
701 Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN");
703 /* Build the data list for the trigtuple */
704 pltcl_build_tuple_argument(trigdata->tg_trigtuple,
705 tupdesc, &tcl_trigtup);
708 * Now the command part of the event for TG_op and data for NEW and
711 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
713 Tcl_DStringAppendElement(&tcl_cmd, "INSERT");
715 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
716 Tcl_DStringAppendElement(&tcl_cmd, "");
718 rettup = trigdata->tg_trigtuple;
720 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
722 Tcl_DStringAppendElement(&tcl_cmd, "DELETE");
724 Tcl_DStringAppendElement(&tcl_cmd, "");
725 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
727 rettup = trigdata->tg_trigtuple;
729 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
731 Tcl_DStringAppendElement(&tcl_cmd, "UPDATE");
733 pltcl_build_tuple_argument(trigdata->tg_newtuple,
734 tupdesc, &tcl_newtup);
736 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_newtup));
737 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
739 rettup = trigdata->tg_newtuple;
743 Tcl_DStringAppendElement(&tcl_cmd, "UNKNOWN");
745 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
746 Tcl_DStringAppendElement(&tcl_cmd, Tcl_DStringValue(&tcl_trigtup));
748 rettup = trigdata->tg_trigtuple;
751 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
752 Tcl_DStringFree(&tcl_trigtup);
753 Tcl_DStringFree(&tcl_newtup);
755 /************************************************************
756 * Finally append the arguments from CREATE TRIGGER
757 ************************************************************/
758 for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
759 Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
761 /************************************************************
762 * Call the Tcl function
763 ************************************************************/
764 tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
765 Tcl_DStringFree(&tcl_cmd);
767 /************************************************************
768 * Check the return code from Tcl and handle
769 * our special restart mechanism to get rid
770 * of all nested call levels on transaction
772 ************************************************************/
773 if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress)
775 if (!pltcl_restart_in_progress)
777 pltcl_restart_in_progress = 1;
778 if (--pltcl_call_level == 0)
779 pltcl_restart_in_progress = 0;
781 elog(ERROR, "pltcl: %s\n%s", interp->result,
782 UTF_U2E(Tcl_GetVar(interp, "errorInfo",
786 if (--pltcl_call_level == 0)
787 pltcl_restart_in_progress = 0;
788 siglongjmp(Warn_restart, 1);
797 elog(ERROR, "pltcl: unsupported TCL return code %d", tcl_rc);
800 /************************************************************
801 * The return value from the procedure might be one of
802 * the magic strings OK or SKIP or a list from array get
803 ************************************************************/
804 if (SPI_finish() != SPI_OK_FINISH)
805 elog(ERROR, "pltcl: SPI_finish() failed");
807 if (strcmp(interp->result, "OK") == 0)
809 if (strcmp(interp->result, "SKIP") == 0)
810 return (HeapTuple) NULL;
812 /************************************************************
813 * Convert the result value from the Tcl interpreter
814 * and setup structures for SPI_modifytuple();
815 ************************************************************/
816 if (Tcl_SplitList(interp, interp->result,
817 &ret_numvals, &ret_values) != TCL_OK)
819 elog(WARNING, "pltcl: cannot split return value from trigger");
820 elog(ERROR, "pltcl: %s", interp->result);
823 if (ret_numvals % 2 != 0)
825 ckfree((char *) ret_values);
826 elog(ERROR, "pltcl: invalid return list from trigger - must have even # of elements");
829 modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
830 modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
831 for (i = 0; i < tupdesc->natts; i++)
834 modvalues[i] = (Datum) NULL;
837 modnulls = palloc(tupdesc->natts + 1);
838 memset(modnulls, 'n', tupdesc->natts);
839 modnulls[tupdesc->natts] = '\0';
841 /************************************************************
842 * Care for possible elog(ERROR)'s below
843 ************************************************************/
844 if (sigsetjmp(Warn_restart, 1) != 0)
846 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
847 ckfree((char *) ret_values);
848 pltcl_restart_in_progress = 1;
849 if (--pltcl_call_level == 0)
850 pltcl_restart_in_progress = 0;
851 siglongjmp(Warn_restart, 1);
855 while (i < ret_numvals)
863 /************************************************************
864 * Ignore pseudo elements with a dot name
865 ************************************************************/
866 if (*(ret_values[i]) == '.')
872 /************************************************************
873 * Get the attribute number
874 ************************************************************/
875 attnum = SPI_fnumber(tupdesc, ret_values[i++]);
876 if (attnum == SPI_ERROR_NOATTRIBUTE)
877 elog(ERROR, "pltcl: invalid attribute '%s'", ret_values[--i]);
879 elog(ERROR, "pltcl: cannot set system attribute '%s'", ret_values[--i]);
881 /************************************************************
882 * Lookup the attribute type in the syscache
883 * for the input function
884 ************************************************************/
885 typeTup = SearchSysCache(TYPEOID,
886 ObjectIdGetDatum(tupdesc->attrs[attnum - 1]->atttypid),
888 if (!HeapTupleIsValid(typeTup))
890 elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %u failed",
892 tupdesc->attrs[attnum - 1]->atttypid);
894 typinput = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
895 typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
896 ReleaseSysCache(typeTup);
898 /************************************************************
899 * Set the attribute to NOT NULL and convert the contents
900 ************************************************************/
901 modnulls[attnum - 1] = ' ';
902 fmgr_info(typinput, &finfo);
904 modvalues[attnum - 1] =
905 FunctionCall3(&finfo,
906 CStringGetDatum(UTF_U2E(ret_values[i++])),
907 ObjectIdGetDatum(typelem),
908 Int32GetDatum(tupdesc->attrs[attnum - 1]->atttypmod));
912 rettup = SPI_modifytuple(trigdata->tg_relation, rettup, tupdesc->natts,
913 modattrs, modvalues, modnulls);
920 elog(ERROR, "pltcl: SPI_modifytuple() failed - RC = %d\n", SPI_result);
922 ckfree((char *) ret_values);
923 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
929 /**********************************************************************
930 * compile_pltcl_function - compile (or hopefully just look up) function
931 **********************************************************************/
932 static pltcl_proc_desc *
933 compile_pltcl_function(Oid fn_oid, bool is_trigger)
936 Form_pg_proc procStruct;
937 char internal_proname[64];
938 Tcl_HashEntry *hashent;
939 pltcl_proc_desc *prodesc = NULL;
945 /* We'll need the pg_proc tuple in any case... */
946 procTup = SearchSysCache(PROCOID,
947 ObjectIdGetDatum(fn_oid),
949 if (!HeapTupleIsValid(procTup))
950 elog(ERROR, "pltcl: cache lookup for proc %u failed", fn_oid);
951 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
953 /************************************************************
954 * Build our internal proc name from the functions Oid
955 ************************************************************/
957 snprintf(internal_proname, sizeof(internal_proname),
958 "__PLTcl_proc_%u", fn_oid);
960 snprintf(internal_proname, sizeof(internal_proname),
961 "__PLTcl_proc_%u_trigger", fn_oid);
963 /************************************************************
964 * Lookup the internal proc name in the hashtable
965 ************************************************************/
966 hashent = Tcl_FindHashEntry(pltcl_proc_hash, internal_proname);
968 /************************************************************
969 * If it's present, must check whether it's still up to date.
970 * This is needed because CREATE OR REPLACE FUNCTION can modify the
971 * function's pg_proc entry without changing its OID.
972 ************************************************************/
977 prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
979 uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
980 prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
984 Tcl_DeleteHashEntry(hashent);
989 /************************************************************
990 * If we haven't found it in the hashtable, we analyze
991 * the functions arguments and returntype and store
992 * the in-/out-functions in the prodesc block and create
993 * a new hashtable entry for it.
995 * Then we load the procedure into the Tcl interpreter.
996 ************************************************************/
1001 Form_pg_language langStruct;
1002 Form_pg_type typeStruct;
1003 Tcl_DString proc_internal_def;
1004 Tcl_DString proc_internal_body;
1005 char proc_internal_args[4096];
1009 /************************************************************
1010 * Allocate a new procedure description block
1011 ************************************************************/
1012 prodesc = (pltcl_proc_desc *) malloc(sizeof(pltcl_proc_desc));
1013 if (prodesc == NULL)
1014 elog(ERROR, "pltcl: out of memory");
1015 MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
1016 prodesc->proname = strdup(internal_proname);
1017 prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
1018 prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
1020 /************************************************************
1021 * Lookup the pg_language tuple by Oid
1022 ************************************************************/
1023 langTup = SearchSysCache(LANGOID,
1024 ObjectIdGetDatum(procStruct->prolang),
1026 if (!HeapTupleIsValid(langTup))
1028 free(prodesc->proname);
1030 elog(ERROR, "pltcl: cache lookup for language %u failed",
1031 procStruct->prolang);
1033 langStruct = (Form_pg_language) GETSTRUCT(langTup);
1034 prodesc->lanpltrusted = langStruct->lanpltrusted;
1035 ReleaseSysCache(langTup);
1037 if (prodesc->lanpltrusted)
1038 interp = pltcl_safe_interp;
1040 interp = pltcl_norm_interp;
1042 /************************************************************
1043 * Get the required information for input conversion of the
1045 ************************************************************/
1048 typeTup = SearchSysCache(TYPEOID,
1049 ObjectIdGetDatum(procStruct->prorettype),
1051 if (!HeapTupleIsValid(typeTup))
1053 free(prodesc->proname);
1055 elog(ERROR, "pltcl: cache lookup for return type %u failed",
1056 procStruct->prorettype);
1058 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1060 /* Disallow pseudotype result, except VOID */
1061 if (typeStruct->typtype == 'p')
1063 if (procStruct->prorettype == VOIDOID)
1065 else if (procStruct->prorettype == TRIGGEROID)
1067 free(prodesc->proname);
1069 elog(ERROR, "pltcl functions cannot return type %s"
1070 "\n\texcept when used as triggers",
1071 format_type_be(procStruct->prorettype));
1075 free(prodesc->proname);
1077 elog(ERROR, "pltcl functions cannot return type %s",
1078 format_type_be(procStruct->prorettype));
1082 if (typeStruct->typrelid != InvalidOid)
1084 free(prodesc->proname);
1086 elog(ERROR, "pltcl: return types of tuples not supported yet");
1089 perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func));
1090 prodesc->result_in_elem = typeStruct->typelem;
1092 ReleaseSysCache(typeTup);
1095 /************************************************************
1096 * Get the required information for output conversion
1097 * of all procedure arguments
1098 ************************************************************/
1101 prodesc->nargs = procStruct->pronargs;
1102 proc_internal_args[0] = '\0';
1103 for (i = 0; i < prodesc->nargs; i++)
1105 typeTup = SearchSysCache(TYPEOID,
1106 ObjectIdGetDatum(procStruct->proargtypes[i]),
1108 if (!HeapTupleIsValid(typeTup))
1110 free(prodesc->proname);
1112 elog(ERROR, "pltcl: cache lookup for argument type %u failed",
1113 procStruct->proargtypes[i]);
1115 typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1117 /* Disallow pseudotype argument */
1118 if (typeStruct->typtype == 'p')
1120 free(prodesc->proname);
1122 elog(ERROR, "pltcl functions cannot take type %s",
1123 format_type_be(procStruct->proargtypes[i]));
1126 if (typeStruct->typrelid != InvalidOid)
1128 prodesc->arg_is_rel[i] = 1;
1130 strcat(proc_internal_args, " ");
1131 snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
1132 strcat(proc_internal_args, buf);
1133 ReleaseSysCache(typeTup);
1137 prodesc->arg_is_rel[i] = 0;
1139 perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i]));
1140 prodesc->arg_out_elem[i] = typeStruct->typelem;
1143 strcat(proc_internal_args, " ");
1144 snprintf(buf, sizeof(buf), "%d", i + 1);
1145 strcat(proc_internal_args, buf);
1147 ReleaseSysCache(typeTup);
1152 /* trigger procedure has fixed args */
1153 strcpy(proc_internal_args,
1154 "TG_name TG_relid TG_relatts TG_when TG_level TG_op __PLTcl_Tup_NEW __PLTcl_Tup_OLD args");
1157 /************************************************************
1158 * Create the tcl command to define the internal
1160 ************************************************************/
1161 Tcl_DStringInit(&proc_internal_def);
1162 Tcl_DStringInit(&proc_internal_body);
1163 Tcl_DStringAppendElement(&proc_internal_def, "proc");
1164 Tcl_DStringAppendElement(&proc_internal_def, internal_proname);
1165 Tcl_DStringAppendElement(&proc_internal_def, proc_internal_args);
1167 /************************************************************
1168 * prefix procedure body with
1169 * upvar #0 <internal_procname> GD
1170 * and with appropriate setting of arguments
1171 ************************************************************/
1172 Tcl_DStringAppend(&proc_internal_body, "upvar #0 ", -1);
1173 Tcl_DStringAppend(&proc_internal_body, internal_proname, -1);
1174 Tcl_DStringAppend(&proc_internal_body, " GD\n", -1);
1177 for (i = 0; i < prodesc->nargs; i++)
1179 if (!prodesc->arg_is_rel[i])
1181 snprintf(buf, sizeof(buf), "array set %d $__PLTcl_Tup_%d\n",
1183 Tcl_DStringAppend(&proc_internal_body, buf, -1);
1188 Tcl_DStringAppend(&proc_internal_body,
1189 "array set NEW $__PLTcl_Tup_NEW\n", -1);
1190 Tcl_DStringAppend(&proc_internal_body,
1191 "array set OLD $__PLTcl_Tup_OLD\n", -1);
1193 Tcl_DStringAppend(&proc_internal_body,
1196 "foreach v $args {\n"
1200 "unset i v\n\n", -1);
1203 /************************************************************
1204 * Add user's function definition to proc body
1205 ************************************************************/
1206 proc_source = DatumGetCString(DirectFunctionCall1(textout,
1207 PointerGetDatum(&procStruct->prosrc)));
1209 Tcl_DStringAppend(&proc_internal_body, UTF_E2U(proc_source), -1);
1212 Tcl_DStringAppendElement(&proc_internal_def,
1213 Tcl_DStringValue(&proc_internal_body));
1214 Tcl_DStringFree(&proc_internal_body);
1216 /************************************************************
1217 * Create the procedure in the interpreter
1218 ************************************************************/
1219 tcl_rc = Tcl_GlobalEval(interp,
1220 Tcl_DStringValue(&proc_internal_def));
1221 Tcl_DStringFree(&proc_internal_def);
1222 if (tcl_rc != TCL_OK)
1224 free(prodesc->proname);
1226 elog(ERROR, "pltcl: cannot create internal procedure %s - %s",
1227 internal_proname, interp->result);
1230 /************************************************************
1231 * Add the proc description block to the hashtable
1232 ************************************************************/
1233 hashent = Tcl_CreateHashEntry(pltcl_proc_hash,
1234 prodesc->proname, &hashnew);
1235 Tcl_SetHashValue(hashent, (ClientData) prodesc);
1238 ReleaseSysCache(procTup);
1244 /**********************************************************************
1245 * pltcl_elog() - elog() support for PLTcl
1246 **********************************************************************/
1248 pltcl_elog(ClientData cdata, Tcl_Interp *interp,
1249 int argc, char *argv[])
1252 sigjmp_buf save_restart;
1254 /************************************************************
1255 * Suppress messages during the restart process
1256 ************************************************************/
1257 if (pltcl_restart_in_progress)
1260 /************************************************************
1261 * Catch the restart longjmp and begin a controlled
1262 * return though all interpreter levels if it happens
1263 ************************************************************/
1264 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
1265 if (sigsetjmp(Warn_restart, 1) != 0)
1267 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1268 pltcl_restart_in_progress = 1;
1274 Tcl_SetResult(interp, "syntax error - 'elog level msg'",
1279 if (strcmp(argv[1], "DEBUG") == 0)
1281 else if (strcmp(argv[1], "LOG") == 0)
1283 else if (strcmp(argv[1], "INFO") == 0)
1285 else if (strcmp(argv[1], "NOTICE") == 0)
1287 else if (strcmp(argv[1], "WARNING") == 0)
1289 else if (strcmp(argv[1], "ERROR") == 0)
1291 else if (strcmp(argv[1], "FATAL") == 0)
1295 Tcl_AppendResult(interp, "Unknown elog level '", argv[1],
1297 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1301 /************************************************************
1302 * Call elog(), restore the original restart address
1303 * and return to the caller (if not catched)
1304 ************************************************************/
1306 elog(level, UTF_U2E(argv[2]));
1308 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1313 /**********************************************************************
1314 * pltcl_quote() - quote literal strings that are to
1315 * be used in SPI_exec query strings
1316 **********************************************************************/
1318 pltcl_quote(ClientData cdata, Tcl_Interp *interp,
1319 int argc, char *argv[])
1325 /************************************************************
1327 ************************************************************/
1330 Tcl_SetResult(interp, "syntax error - 'quote string'", TCL_VOLATILE);
1334 /************************************************************
1335 * Allocate space for the maximum the string can
1336 * grow to and initialize pointers
1337 ************************************************************/
1338 tmp = palloc(strlen(argv[1]) * 2 + 1);
1342 /************************************************************
1343 * Walk through string and double every quote and backslash
1344 ************************************************************/
1357 /************************************************************
1358 * Terminate the string and set it as result
1359 ************************************************************/
1361 Tcl_SetResult(interp, tmp, TCL_VOLATILE);
1367 /**********************************************************************
1368 * pltcl_argisnull() - determine if a specific argument is NULL
1369 **********************************************************************/
1371 pltcl_argisnull(ClientData cdata, Tcl_Interp *interp,
1372 int argc, char *argv[])
1375 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
1377 /************************************************************
1379 ************************************************************/
1382 Tcl_SetResult(interp, "syntax error - 'argisnull argno'", TCL_VOLATILE);
1386 /************************************************************
1387 * Check that we're called as a normal function
1388 ************************************************************/
1391 Tcl_SetResult(interp, "argisnull cannot be used in triggers",
1396 /************************************************************
1397 * Get the argument number
1398 ************************************************************/
1399 if (Tcl_GetInt(interp, argv[1], &argno) != TCL_OK)
1402 /************************************************************
1403 * Check that the argno is valid
1404 ************************************************************/
1406 if (argno < 0 || argno >= fcinfo->nargs)
1408 Tcl_SetResult(interp, "argno out of range", TCL_VOLATILE);
1412 /************************************************************
1413 * Get the requested NULL state
1414 ************************************************************/
1415 if (PG_ARGISNULL(argno))
1416 Tcl_SetResult(interp, "1", TCL_VOLATILE);
1418 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1424 /**********************************************************************
1425 * pltcl_returnnull() - Cause a NULL return from a function
1426 **********************************************************************/
1428 pltcl_returnnull(ClientData cdata, Tcl_Interp *interp,
1429 int argc, char *argv[])
1431 FunctionCallInfo fcinfo = pltcl_current_fcinfo;
1433 /************************************************************
1435 ************************************************************/
1438 Tcl_SetResult(interp, "syntax error - 'return_null'", TCL_VOLATILE);
1442 /************************************************************
1443 * Check that we're called as a normal function
1444 ************************************************************/
1447 Tcl_SetResult(interp, "return_null cannot be used in triggers",
1452 /************************************************************
1453 * Set the NULL return flag and cause Tcl to return from the
1455 ************************************************************/
1456 fcinfo->isnull = true;
1462 /**********************************************************************
1463 * pltcl_SPI_exec() - The builtin SPI_exec command
1464 * for the Tcl interpreter
1465 **********************************************************************/
1467 pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
1468 int argc, char *argv[])
1473 char *volatile arrayname = NULL;
1474 volatile int query_idx;
1478 HeapTuple *volatile tuples;
1479 volatile TupleDesc tupdesc = NULL;
1480 SPITupleTable *tuptable;
1481 sigjmp_buf save_restart;
1483 char *usage = "syntax error - 'SPI_exec "
1485 "?-array name? query ?loop body?";
1487 /************************************************************
1488 * Don't do anything if we are already in restart mode
1489 ************************************************************/
1490 if (pltcl_restart_in_progress)
1493 /************************************************************
1494 * Check the call syntax and get the count option
1495 ************************************************************/
1498 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1505 if (strcmp(argv[i], "-array") == 0)
1509 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1512 arrayname = argv[i++];
1516 if (strcmp(argv[i], "-count") == 0)
1520 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1523 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
1532 if (query_idx >= argc)
1534 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1538 /************************************************************
1539 * Prepare to start a controlled return through all
1540 * interpreter levels on transaction abort
1541 ************************************************************/
1542 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
1543 if (sigsetjmp(Warn_restart, 1) != 0)
1545 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1546 pltcl_restart_in_progress = 1;
1547 Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
1551 /************************************************************
1552 * Execute the query and handle return codes
1553 ************************************************************/
1555 spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count);
1557 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1561 case SPI_OK_UTILITY:
1562 Tcl_SetResult(interp, "0", TCL_VOLATILE);
1563 SPI_freetuptable(SPI_tuptable);
1566 case SPI_OK_SELINTO:
1570 snprintf(buf, sizeof(buf), "%d", SPI_processed);
1571 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1572 SPI_freetuptable(SPI_tuptable);
1578 case SPI_ERROR_ARGUMENT:
1579 Tcl_SetResult(interp,
1580 "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT",
1584 case SPI_ERROR_UNCONNECTED:
1585 Tcl_SetResult(interp,
1586 "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED",
1590 case SPI_ERROR_COPY:
1591 Tcl_SetResult(interp,
1592 "pltcl: SPI_exec() failed - SPI_ERROR_COPY",
1596 case SPI_ERROR_CURSOR:
1597 Tcl_SetResult(interp,
1598 "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR",
1602 case SPI_ERROR_TRANSACTION:
1603 Tcl_SetResult(interp,
1604 "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION",
1608 case SPI_ERROR_OPUNKNOWN:
1609 Tcl_SetResult(interp,
1610 "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN",
1615 snprintf(buf, sizeof(buf), "%d", spi_rc);
1616 Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
1617 "unknown RC ", buf, NULL);
1621 /************************************************************
1622 * Only SELECT queries fall through to here - remember the
1624 ************************************************************/
1626 ntuples = SPI_processed;
1629 tuples = SPI_tuptable->vals;
1630 tupdesc = SPI_tuptable->tupdesc;
1633 /************************************************************
1634 * Again prepare for elog(ERROR)
1635 ************************************************************/
1636 if (sigsetjmp(Warn_restart, 1) != 0)
1638 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1639 pltcl_restart_in_progress = 1;
1640 Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
1644 /************************************************************
1645 * If there is no loop body given, just set the variables
1646 * from the first tuple (if any) and return the number of
1648 ************************************************************/
1649 if (argc == query_idx + 1)
1652 pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
1653 snprintf(buf, sizeof(buf), "%d", ntuples);
1654 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1655 SPI_freetuptable(SPI_tuptable);
1656 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1660 tuptable = SPI_tuptable;
1662 /************************************************************
1663 * There is a loop body - process all tuples and evaluate
1665 ************************************************************/
1667 for (i = 0; i < ntuples; i++)
1669 pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
1671 loop_rc = Tcl_Eval(interp, argv[query_idx]);
1673 if (loop_rc == TCL_OK)
1675 if (loop_rc == TCL_CONTINUE)
1677 if (loop_rc == TCL_RETURN)
1679 SPI_freetuptable(tuptable);
1680 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1683 if (loop_rc == TCL_BREAK)
1685 SPI_freetuptable(tuptable);
1686 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1690 SPI_freetuptable(tuptable);
1692 /************************************************************
1693 * Finally return the number of tuples
1694 ************************************************************/
1695 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1696 snprintf(buf, sizeof(buf), "%d", ntuples);
1697 Tcl_SetResult(interp, buf, TCL_VOLATILE);
1702 /**********************************************************************
1703 * pltcl_SPI_prepare() - Builtin support for prepared plans
1704 * The Tcl command SPI_prepare
1705 * always saves the plan using
1706 * SPI_saveplan and returns a key for
1707 * access. There is no chance to prepare
1708 * and not save the plan currently.
1709 **********************************************************************/
1711 pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
1712 int argc, char *argv[])
1716 pltcl_query_desc *qdesc;
1720 Tcl_HashEntry *hashent;
1722 sigjmp_buf save_restart;
1723 Tcl_HashTable *query_hash;
1725 /************************************************************
1726 * Don't do anything if we are already in restart mode
1727 ************************************************************/
1728 if (pltcl_restart_in_progress)
1731 /************************************************************
1732 * Check the call syntax
1733 ************************************************************/
1736 Tcl_SetResult(interp, "syntax error - 'SPI_prepare query argtypes'",
1741 /************************************************************
1742 * Split the argument type list
1743 ************************************************************/
1744 if (Tcl_SplitList(interp, argv[2], &nargs, &args) != TCL_OK)
1747 /************************************************************
1748 * Allocate the new querydesc structure
1749 ************************************************************/
1750 qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
1751 snprintf(qdesc->qname, sizeof(qdesc->qname), "%lx", (long) qdesc);
1752 qdesc->nargs = nargs;
1753 qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
1754 qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
1755 qdesc->argtypelems = (Oid *) malloc(nargs * sizeof(Oid));
1757 /************************************************************
1758 * Prepare to start a controlled return through all
1759 * interpreter levels on transaction abort
1760 ************************************************************/
1761 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
1762 if (sigsetjmp(Warn_restart, 1) != 0)
1764 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1765 pltcl_restart_in_progress = 1;
1766 free(qdesc->argtypes);
1767 free(qdesc->arginfuncs);
1768 free(qdesc->argtypelems);
1770 ckfree((char *) args);
1774 /************************************************************
1775 * Lookup the argument types by name in the system cache
1776 * and remember the required information for input conversion
1777 ************************************************************/
1778 for (i = 0; i < nargs; i++)
1780 /* XXX should extend this to allow qualified type names */
1781 typeTup = typenameType(makeTypeName(args[i]));
1782 qdesc->argtypes[i] = HeapTupleGetOid(typeTup);
1783 perm_fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput,
1784 &(qdesc->arginfuncs[i]));
1785 qdesc->argtypelems[i] = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
1786 ReleaseSysCache(typeTup);
1789 /************************************************************
1790 * Prepare the plan and check for errors
1791 ************************************************************/
1793 plan = SPI_prepare(UTF_U2E(argv[1]), nargs, qdesc->argtypes);
1801 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1805 case SPI_ERROR_ARGUMENT:
1806 reason = "SPI_ERROR_ARGUMENT";
1809 case SPI_ERROR_UNCONNECTED:
1810 reason = "SPI_ERROR_UNCONNECTED";
1813 case SPI_ERROR_COPY:
1814 reason = "SPI_ERROR_COPY";
1817 case SPI_ERROR_CURSOR:
1818 reason = "SPI_ERROR_CURSOR";
1821 case SPI_ERROR_TRANSACTION:
1822 reason = "SPI_ERROR_TRANSACTION";
1825 case SPI_ERROR_OPUNKNOWN:
1826 reason = "SPI_ERROR_OPUNKNOWN";
1830 snprintf(buf, sizeof(buf), "unknown RC %d", SPI_result);
1836 elog(ERROR, "pltcl: SPI_prepare() failed - %s", reason);
1839 /************************************************************
1841 ************************************************************/
1842 qdesc->plan = SPI_saveplan(plan);
1843 if (qdesc->plan == NULL)
1848 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1852 case SPI_ERROR_ARGUMENT:
1853 reason = "SPI_ERROR_ARGUMENT";
1856 case SPI_ERROR_UNCONNECTED:
1857 reason = "SPI_ERROR_UNCONNECTED";
1861 snprintf(buf, sizeof(buf), "unknown RC %d", SPI_result);
1867 elog(ERROR, "pltcl: SPI_saveplan() failed - %s", reason);
1870 /************************************************************
1871 * Insert a hashtable entry for the plan and return
1872 * the key to the caller
1873 ************************************************************/
1874 if (interp == pltcl_norm_interp)
1875 query_hash = pltcl_norm_query_hash;
1877 query_hash = pltcl_safe_query_hash;
1879 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
1880 hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
1881 Tcl_SetHashValue(hashent, (ClientData) qdesc);
1883 Tcl_SetResult(interp, qdesc->qname, TCL_VOLATILE);
1888 /**********************************************************************
1889 * pltcl_SPI_execp() - Execute a prepared plan
1890 **********************************************************************/
1892 pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
1893 int argc, char *argv[])
1900 Tcl_HashEntry *hashent;
1901 pltcl_query_desc *qdesc;
1902 Datum *volatile argvalues = NULL;
1903 char *volatile nulls = NULL;
1904 char *volatile arrayname = NULL;
1907 static char **callargs = NULL;
1910 HeapTuple *volatile tuples = NULL;
1911 volatile TupleDesc tupdesc = NULL;
1912 SPITupleTable *tuptable;
1913 sigjmp_buf save_restart;
1914 Tcl_HashTable *query_hash;
1916 char *usage = "syntax error - 'SPI_execp "
1917 "?-nulls string? ?-count n? "
1918 "?-array name? query ?args? ?loop body?";
1920 /************************************************************
1921 * Tidy up from an earlier abort
1922 ************************************************************/
1923 if (callargs != NULL)
1925 ckfree((char *) callargs);
1929 /************************************************************
1930 * Don't do anything if we are already in restart mode
1931 ************************************************************/
1932 if (pltcl_restart_in_progress)
1935 /************************************************************
1936 * Get the options and check syntax
1937 ************************************************************/
1941 if (strcmp(argv[i], "-array") == 0)
1945 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1948 arrayname = argv[i++];
1951 if (strcmp(argv[i], "-nulls") == 0)
1955 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1961 if (strcmp(argv[i], "-count") == 0)
1965 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1968 if (Tcl_GetInt(interp, argv[i++], &count) != TCL_OK)
1976 /************************************************************
1977 * Check minimum call arguments
1978 ************************************************************/
1981 Tcl_SetResult(interp, usage, TCL_VOLATILE);
1985 /************************************************************
1986 * Get the prepared plan descriptor by its key
1987 ************************************************************/
1988 if (interp == pltcl_norm_interp)
1989 query_hash = pltcl_norm_query_hash;
1991 query_hash = pltcl_safe_query_hash;
1993 hashent = Tcl_FindHashEntry(query_hash, argv[i++]);
1994 if (hashent == NULL)
1996 Tcl_AppendResult(interp, "invalid queryid '", argv[--i], "'", NULL);
1999 qdesc = (pltcl_query_desc *) Tcl_GetHashValue(hashent);
2001 /************************************************************
2002 * If a nulls string is given, check for correct length
2003 ************************************************************/
2006 if (strlen(nulls) != qdesc->nargs)
2008 Tcl_SetResult(interp,
2009 "length of nulls string doesn't match # of arguments",
2015 /************************************************************
2016 * If there was a argtype list on preparation, we need
2017 * an argument value list now
2018 ************************************************************/
2019 if (qdesc->nargs > 0)
2023 Tcl_SetResult(interp, "missing argument list", TCL_VOLATILE);
2027 /************************************************************
2028 * Split the argument values
2029 ************************************************************/
2030 if (Tcl_SplitList(interp, argv[i++], &callnargs, &callargs) != TCL_OK)
2033 /************************************************************
2034 * Check that the # of arguments matches
2035 ************************************************************/
2036 if (callnargs != qdesc->nargs)
2038 Tcl_SetResult(interp,
2039 "argument list length doesn't match # of arguments for query",
2041 if (callargs != NULL)
2043 ckfree((char *) callargs);
2049 /************************************************************
2050 * Prepare to start a controlled return through all
2051 * interpreter levels on transaction abort during the
2052 * parse of the arguments
2053 ************************************************************/
2054 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
2055 if (sigsetjmp(Warn_restart, 1) != 0)
2057 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2058 ckfree((char *) callargs);
2060 pltcl_restart_in_progress = 1;
2061 Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
2065 /************************************************************
2066 * Setup the value array for the SPI_execp() using
2067 * the type specific input functions
2068 ************************************************************/
2069 argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
2071 for (j = 0; j < callnargs; j++)
2073 if (nulls && nulls[j] == 'n')
2075 /* don't try to convert the input for a null */
2076 argvalues[j] = (Datum) 0;
2082 FunctionCall3(&qdesc->arginfuncs[j],
2083 CStringGetDatum(UTF_U2E(callargs[j])),
2084 ObjectIdGetDatum(qdesc->argtypelems[j]),
2090 /************************************************************
2091 * Free the splitted argument value list
2092 ************************************************************/
2093 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2094 ckfree((char *) callargs);
2100 /************************************************************
2101 * Remember the index of the last processed call
2102 * argument - a loop body for SELECT might follow
2103 ************************************************************/
2106 /************************************************************
2107 * Prepare to start a controlled return through all
2108 * interpreter levels on transaction abort
2109 ************************************************************/
2110 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
2111 if (sigsetjmp(Warn_restart, 1) != 0)
2113 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2114 pltcl_restart_in_progress = 1;
2115 Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
2119 /************************************************************
2121 ************************************************************/
2122 spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count);
2123 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2125 /************************************************************
2126 * Check the return code from SPI_execp()
2127 ************************************************************/
2130 case SPI_OK_UTILITY:
2131 Tcl_SetResult(interp, "0", TCL_VOLATILE);
2132 SPI_freetuptable(SPI_tuptable);
2135 case SPI_OK_SELINTO:
2139 snprintf(buf, sizeof(buf), "%d", SPI_processed);
2140 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2141 SPI_freetuptable(SPI_tuptable);
2147 case SPI_ERROR_ARGUMENT:
2148 Tcl_SetResult(interp,
2149 "pltcl: SPI_exec() failed - SPI_ERROR_ARGUMENT",
2153 case SPI_ERROR_UNCONNECTED:
2154 Tcl_SetResult(interp,
2155 "pltcl: SPI_exec() failed - SPI_ERROR_UNCONNECTED",
2159 case SPI_ERROR_COPY:
2160 Tcl_SetResult(interp,
2161 "pltcl: SPI_exec() failed - SPI_ERROR_COPY",
2165 case SPI_ERROR_CURSOR:
2166 Tcl_SetResult(interp,
2167 "pltcl: SPI_exec() failed - SPI_ERROR_CURSOR",
2171 case SPI_ERROR_TRANSACTION:
2172 Tcl_SetResult(interp,
2173 "pltcl: SPI_exec() failed - SPI_ERROR_TRANSACTION",
2177 case SPI_ERROR_OPUNKNOWN:
2178 Tcl_SetResult(interp,
2179 "pltcl: SPI_exec() failed - SPI_ERROR_OPUNKNOWN",
2184 snprintf(buf, sizeof(buf), "%d", spi_rc);
2185 Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
2186 "unknown RC ", buf, NULL);
2190 /************************************************************
2191 * Only SELECT queries fall through to here - remember the
2193 ************************************************************/
2195 ntuples = SPI_processed;
2198 tuples = SPI_tuptable->vals;
2199 tupdesc = SPI_tuptable->tupdesc;
2202 /************************************************************
2203 * Prepare to start a controlled return through all
2204 * interpreter levels on transaction abort during
2205 * the ouput conversions of the results
2206 ************************************************************/
2207 memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
2208 if (sigsetjmp(Warn_restart, 1) != 0)
2210 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2211 pltcl_restart_in_progress = 1;
2212 Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
2216 /************************************************************
2217 * If there is no loop body given, just set the variables
2218 * from the first tuple (if any) and return the number of
2220 ************************************************************/
2221 if (loop_body >= argc)
2224 pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
2225 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2226 snprintf(buf, sizeof(buf), "%d", ntuples);
2227 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2228 SPI_freetuptable(SPI_tuptable);
2232 tuptable = SPI_tuptable;
2234 /************************************************************
2235 * There is a loop body - process all tuples and evaluate
2237 ************************************************************/
2238 for (i = 0; i < ntuples; i++)
2240 pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
2242 loop_rc = Tcl_Eval(interp, argv[loop_body]);
2244 if (loop_rc == TCL_OK)
2246 if (loop_rc == TCL_CONTINUE)
2248 if (loop_rc == TCL_RETURN)
2250 SPI_freetuptable(tuptable);
2251 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2254 if (loop_rc == TCL_BREAK)
2256 SPI_freetuptable(tuptable);
2257 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2261 SPI_freetuptable(tuptable);
2263 /************************************************************
2264 * Finally return the number of tuples
2265 ************************************************************/
2266 memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
2267 snprintf(buf, sizeof(buf), "%d", ntuples);
2268 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2273 /**********************************************************************
2274 * pltcl_SPI_lastoid() - return the last oid. To
2275 * be used after insert queries
2276 **********************************************************************/
2278 pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
2279 int argc, char *argv[])
2283 snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
2284 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2289 /**********************************************************************
2290 * pltcl_set_tuple_values() - Set variables for all attributes
2292 **********************************************************************/
2294 pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname,
2295 int tupno, HeapTuple tuple, TupleDesc tupdesc)
2310 char *nullname = NULL;
2312 /************************************************************
2313 * Prepare pointers for Tcl_SetVar2() below and in array
2314 * mode set the .tupno element
2315 ************************************************************/
2316 if (arrayname == NULL)
2319 nameptr = &nullname;
2323 arrptr = &arrayname;
2325 snprintf(buf, sizeof(buf), "%d", tupno);
2326 Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
2329 for (i = 0; i < tupdesc->natts; i++)
2331 /************************************************************
2332 * Get the attribute name
2333 ************************************************************/
2334 attname = NameStr(tupdesc->attrs[i]->attname);
2336 /************************************************************
2337 * Get the attributes value
2338 ************************************************************/
2339 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
2341 /************************************************************
2342 * Lookup the attribute type in the syscache
2343 * for the output function
2344 ************************************************************/
2345 typeTup = SearchSysCache(TYPEOID,
2346 ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
2348 if (!HeapTupleIsValid(typeTup))
2350 elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %u failed",
2351 attname, tupdesc->attrs[i]->atttypid);
2354 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
2355 typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
2356 ReleaseSysCache(typeTup);
2358 /************************************************************
2359 * If there is a value, set the variable
2362 * Hmmm - Null attributes will cause functions to
2363 * crash if they don't expect them - need something
2365 ************************************************************/
2366 if (!isnull && OidIsValid(typoutput))
2368 outputstr = DatumGetCString(OidFunctionCall3(typoutput,
2370 ObjectIdGetDatum(typelem),
2371 Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
2373 Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0);
2378 Tcl_UnsetVar2(interp, *arrptr, *nameptr, 0);
2383 /**********************************************************************
2384 * pltcl_build_tuple_argument() - Build a string usable for 'array set'
2385 * from all attributes of a given tuple
2386 **********************************************************************/
2388 pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
2389 Tcl_DString *retval)
2401 for (i = 0; i < tupdesc->natts; i++)
2403 /************************************************************
2404 * Get the attribute name
2405 ************************************************************/
2406 attname = NameStr(tupdesc->attrs[i]->attname);
2408 /************************************************************
2409 * Get the attributes value
2410 ************************************************************/
2411 attr = heap_getattr(tuple, i + 1, tupdesc, &isnull);
2413 /************************************************************
2414 * Lookup the attribute type in the syscache
2415 * for the output function
2416 ************************************************************/
2417 typeTup = SearchSysCache(TYPEOID,
2418 ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
2420 if (!HeapTupleIsValid(typeTup))
2422 elog(ERROR, "pltcl: Cache lookup for attribute '%s' type %u failed",
2423 attname, tupdesc->attrs[i]->atttypid);
2426 typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
2427 typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
2428 ReleaseSysCache(typeTup);
2430 /************************************************************
2431 * If there is a value, append the attribute name and the
2434 * Hmmm - Null attributes will cause functions to
2435 * crash if they don't expect them - need something
2437 ************************************************************/
2438 if (!isnull && OidIsValid(typoutput))
2440 outputstr = DatumGetCString(OidFunctionCall3(typoutput,
2442 ObjectIdGetDatum(typelem),
2443 Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
2444 Tcl_DStringAppendElement(retval, attname);
2446 Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));