1515
1616#include "access/htup_details.h"
1717#include "access/xact.h"
18+ #include "catalog/objectaccess.h"
1819#include "catalog/pg_proc.h"
1920#include "catalog/pg_type.h"
2021#include "commands/event_trigger.h"
2526#include "mb/pg_wchar.h"
2627#include "miscadmin.h"
2728#include "nodes/makefuncs.h"
29+ #include "parser/parse_func.h"
2830#include "parser/parse_type.h"
31+ #include "pgstat.h"
2932#include "tcop/tcopprot.h"
3033#include "utils/builtins.h"
3134#include "utils/lsyscache.h"
3235#include "utils/memutils.h"
36+ #include "utils/regproc.h"
3337#include "utils/rel.h"
3438#include "utils/syscache.h"
3539#include "utils/typcache.h"
@@ -226,6 +230,8 @@ typedef struct pltcl_call_state
226230/**********************************************************************
227231 * Global data
228232 **********************************************************************/
233+ static char * pltcl_start_proc = NULL ;
234+ static char * pltclu_start_proc = NULL ;
229235static bool pltcl_pm_init_done = false;
230236static Tcl_Interp * pltcl_hold_interp = NULL ;
231237static HTAB * pltcl_interp_htab = NULL ;
@@ -253,8 +259,11 @@ static const TclExceptionNameMap exception_name_map[] = {
253259 **********************************************************************/
254260void _PG_init (void );
255261
256- static void pltcl_init_interp (pltcl_interp_desc * interp_desc ,bool pltrusted );
257- static pltcl_interp_desc * pltcl_fetch_interp (bool pltrusted );
262+ static void pltcl_init_interp (pltcl_interp_desc * interp_desc ,
263+ Oid prolang ,bool pltrusted );
264+ static pltcl_interp_desc * pltcl_fetch_interp (Oid prolang ,bool pltrusted );
265+ static void call_pltcl_start_proc (Oid prolang ,bool pltrusted );
266+ static void start_proc_error_callback (void * arg );
258267
259268static Datum pltcl_handler (PG_FUNCTION_ARGS ,bool pltrusted );
260269
@@ -441,14 +450,32 @@ _PG_init(void)
441450& hash_ctl ,
442451HASH_ELEM |HASH_BLOBS );
443452
453+ /************************************************************
454+ * Define PL/Tcl's custom GUCs
455+ ************************************************************/
456+ DefineCustomStringVariable ("pltcl.start_proc" ,
457+ gettext_noop ("PL/Tcl function to call once when pltcl is first used." ),
458+ NULL ,
459+ & pltcl_start_proc ,
460+ NULL ,
461+ PGC_SUSET ,0 ,
462+ NULL ,NULL ,NULL );
463+ DefineCustomStringVariable ("pltclu.start_proc" ,
464+ gettext_noop ("PL/TclU function to call once when pltclu is first used." ),
465+ NULL ,
466+ & pltclu_start_proc ,
467+ NULL ,
468+ PGC_SUSET ,0 ,
469+ NULL ,NULL ,NULL );
470+
444471pltcl_pm_init_done = true;
445472}
446473
447474/**********************************************************************
448475 * pltcl_init_interp() - initialize a new Tcl interpreter
449476 **********************************************************************/
450477static void
451- pltcl_init_interp (pltcl_interp_desc * interp_desc ,bool pltrusted )
478+ pltcl_init_interp (pltcl_interp_desc * interp_desc ,Oid prolang , bool pltrusted )
452479{
453480Tcl_Interp * interp ;
454481char interpname [32 ];
@@ -462,7 +489,6 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
462489if ((interp = Tcl_CreateSlave (pltcl_hold_interp ,interpname ,
463490pltrusted ?1 :0 ))== NULL )
464491elog (ERROR ,"could not create slave Tcl interpreter" );
465- interp_desc -> interp = interp ;
466492
467493/************************************************************
468494 * Initialize the query hash table associated with interpreter
@@ -490,16 +516,35 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
490516pltcl_SPI_execute_plan ,NULL ,NULL );
491517Tcl_CreateObjCommand (interp ,"spi_lastoid" ,
492518pltcl_SPI_lastoid ,NULL ,NULL );
519+
520+ /************************************************************
521+ * Call the appropriate start_proc, if there is one.
522+ *
523+ * We must set interp_desc->interp before the call, else the start_proc
524+ * won't find the interpreter it's supposed to use. But, if the
525+ * start_proc fails, we want to abandon use of the interpreter.
526+ ************************************************************/
527+ PG_TRY ();
528+ {
529+ interp_desc -> interp = interp ;
530+ call_pltcl_start_proc (prolang ,pltrusted );
531+ }
532+ PG_CATCH ();
533+ {
534+ interp_desc -> interp = NULL ;
535+ Tcl_DeleteInterp (interp );
536+ PG_RE_THROW ();
537+ }
538+ PG_END_TRY ();
493539}
494540
495541/**********************************************************************
496542 * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
497543 *
498544 * This also takes care of any on-first-use initialization required.
499- * Note: we assume caller has already connected to SPI.
500545 **********************************************************************/
501546static pltcl_interp_desc *
502- pltcl_fetch_interp (bool pltrusted )
547+ pltcl_fetch_interp (Oid prolang , bool pltrusted )
503548{
504549Oid user_id ;
505550pltcl_interp_desc * interp_desc ;
@@ -515,12 +560,117 @@ pltcl_fetch_interp(bool pltrusted)
515560HASH_ENTER ,
516561& found );
517562if (!found )
518- pltcl_init_interp (interp_desc ,pltrusted );
563+ interp_desc -> interp = NULL ;
564+
565+ /* If we haven't yet successfully made an interpreter, try to do that */
566+ if (!interp_desc -> interp )
567+ pltcl_init_interp (interp_desc ,prolang ,pltrusted );
519568
520569return interp_desc ;
521570}
522571
523572
573+ /**********************************************************************
574+ * call_pltcl_start_proc() - Call user-defined initialization proc, if any
575+ **********************************************************************/
576+ static void
577+ call_pltcl_start_proc (Oid prolang ,bool pltrusted )
578+ {
579+ char * start_proc ;
580+ const char * gucname ;
581+ ErrorContextCallback errcallback ;
582+ List * namelist ;
583+ Oid fargtypes [1 ];/* dummy */
584+ Oid procOid ;
585+ HeapTuple procTup ;
586+ Form_pg_proc procStruct ;
587+ AclResult aclresult ;
588+ FmgrInfo finfo ;
589+ FunctionCallInfoData fcinfo ;
590+ PgStat_FunctionCallUsage fcusage ;
591+
592+ /* select appropriate GUC */
593+ start_proc = pltrusted ?pltcl_start_proc :pltclu_start_proc ;
594+ gucname = pltrusted ?"pltcl.start_proc" :"pltclu.start_proc" ;
595+
596+ /* Nothing to do if it's empty or unset */
597+ if (start_proc == NULL || start_proc [0 ]== '\0' )
598+ return ;
599+
600+ /* Set up errcontext callback to make errors more helpful */
601+ errcallback .callback = start_proc_error_callback ;
602+ errcallback .arg = (void * )gucname ;
603+ errcallback .previous = error_context_stack ;
604+ error_context_stack = & errcallback ;
605+
606+ /* Parse possibly-qualified identifier and look up the function */
607+ namelist = stringToQualifiedNameList (start_proc );
608+ procOid = LookupFuncName (namelist ,0 ,fargtypes , false);
609+
610+ /* Current user must have permission to call function */
611+ aclresult = pg_proc_aclcheck (procOid ,GetUserId (),ACL_EXECUTE );
612+ if (aclresult != ACLCHECK_OK )
613+ aclcheck_error (aclresult ,ACL_KIND_PROC ,start_proc );
614+
615+ /* Get the function's pg_proc entry */
616+ procTup = SearchSysCache1 (PROCOID ,ObjectIdGetDatum (procOid ));
617+ if (!HeapTupleIsValid (procTup ))
618+ elog (ERROR ,"cache lookup failed for function %u" ,procOid );
619+ procStruct = (Form_pg_proc )GETSTRUCT (procTup );
620+
621+ /* It must be same language as the function we're currently calling */
622+ if (procStruct -> prolang != prolang )
623+ ereport (ERROR ,
624+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
625+ errmsg ("function \"%s\" is in the wrong language" ,
626+ start_proc )));
627+
628+ /*
629+ * It must not be SECURITY DEFINER, either. This together with the
630+ * language match check ensures that the function will execute in the same
631+ * Tcl interpreter we just finished initializing.
632+ */
633+ if (procStruct -> prosecdef )
634+ ereport (ERROR ,
635+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
636+ errmsg ("function \"%s\" must not be SECURITY DEFINER" ,
637+ start_proc )));
638+
639+ /* A-OK */
640+ ReleaseSysCache (procTup );
641+
642+ /*
643+ * Call the function using the normal SQL function call mechanism. We
644+ * could perhaps cheat and jump directly to pltcl_handler(), but it seems
645+ * better to do it this way so that the call is exposed to, eg, call
646+ * statistics collection.
647+ */
648+ InvokeFunctionExecuteHook (procOid );
649+ fmgr_info (procOid ,& finfo );
650+ InitFunctionCallInfoData (fcinfo ,& finfo ,
651+ 0 ,
652+ InvalidOid ,NULL ,NULL );
653+ pgstat_init_function_usage (& fcinfo ,& fcusage );
654+ (void )FunctionCallInvoke (& fcinfo );
655+ pgstat_end_function_usage (& fcusage , true);
656+
657+ /* Pop the error context stack */
658+ error_context_stack = errcallback .previous ;
659+ }
660+
661+ /*
662+ * Error context callback for errors occurring during start_proc processing.
663+ */
664+ static void
665+ start_proc_error_callback (void * arg )
666+ {
667+ const char * gucname = (const char * )arg ;
668+
669+ /* translator: %s is "pltcl.start_proc" or "pltclu.start_proc" */
670+ errcontext ("processing %s parameter" ,gucname );
671+ }
672+
673+
524674/**********************************************************************
525675 * pltcl_call_handler- This is the only visible function
526676 * of the PL interpreter. The PostgreSQL
@@ -1319,7 +1469,8 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
13191469/************************************************************
13201470 * Identify the interpreter to use for the function
13211471 ************************************************************/
1322- prodesc -> interp_desc = pltcl_fetch_interp (prodesc -> lanpltrusted );
1472+ prodesc -> interp_desc = pltcl_fetch_interp (procStruct -> prolang ,
1473+ prodesc -> lanpltrusted );
13231474interp = prodesc -> interp_desc -> interp ;
13241475
13251476/************************************************************