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 ,
442451 HASH_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+
444471 pltcl_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{
453480 Tcl_Interp * interp ;
454481 char interpname [32 ];
@@ -462,7 +489,6 @@ pltcl_init_interp(pltcl_interp_desc *interp_desc, bool pltrusted)
462489 if ((interp = Tcl_CreateSlave (pltcl_hold_interp , interpname ,
463490 pltrusted ? 1 : 0 )) == NULL )
464491 elog (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)
490516 pltcl_SPI_execute_plan , NULL , NULL );
491517 Tcl_CreateObjCommand (interp , "spi_lastoid" ,
492518 pltcl_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{
504549 Oid user_id ;
505550 pltcl_interp_desc * interp_desc ;
@@ -515,12 +560,117 @@ pltcl_fetch_interp(bool pltrusted)
515560 HASH_ENTER ,
516561 & found );
517562 if (!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
520569 return 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 );
13231474 interp = prodesc -> interp_desc -> interp ;
13241475
13251476 /************************************************************
0 commit comments