22 * pltcl.c- PostgreSQL support for Tcl as
33 * procedural language (PL)
44 *
5- * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.132 2010/02/26 02:01:37 momjian Exp $
5+ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.133 2010/05/13 18:29:12 tgl Exp $
66 *
77 **********************************************************************/
88
@@ -120,7 +120,8 @@ typedef struct pltcl_query_desc
120120 * Global data
121121 **********************************************************************/
122122static bool pltcl_pm_init_done = false;
123- static bool pltcl_be_init_done = false;
123+ static bool pltcl_be_norm_init_done = false;
124+ static bool pltcl_be_safe_init_done = false;
124125static Tcl_Interp * pltcl_hold_interp = NULL ;
125126static Tcl_Interp * pltcl_norm_interp = NULL ;
126127static Tcl_Interp * pltcl_safe_interp = NULL ;
@@ -139,8 +140,8 @@ Datumpltcl_call_handler(PG_FUNCTION_ARGS);
139140Datum pltclu_call_handler (PG_FUNCTION_ARGS );
140141void _PG_init (void );
141142
142- static void pltcl_init_all (void );
143143static void pltcl_init_interp (Tcl_Interp * interp );
144+ static Tcl_Interp * pltcl_fetch_interp (bool pltrusted );
144145static void pltcl_init_load_unknown (Tcl_Interp * interp );
145146
146147static Datum pltcl_func_handler (PG_FUNCTION_ARGS );
@@ -334,33 +335,12 @@ _PG_init(void)
334335pltcl_pm_init_done = true;
335336}
336337
337- /**********************************************************************
338- * pltcl_init_all()- Initialize all
339- *
340- * This does initialization that can't be done in the postmaster, and
341- * hence is not safe to do at library load time.
342- **********************************************************************/
343- static void
344- pltcl_init_all (void )
345- {
346- /************************************************************
347- * Try to load the unknown procedure from pltcl_modules
348- ************************************************************/
349- if (!pltcl_be_init_done )
350- {
351- if (SPI_connect ()!= SPI_OK_CONNECT )
352- elog (ERROR ,"SPI_connect failed" );
353- pltcl_init_load_unknown (pltcl_norm_interp );
354- pltcl_init_load_unknown (pltcl_safe_interp );
355- if (SPI_finish ()!= SPI_OK_FINISH )
356- elog (ERROR ,"SPI_finish failed" );
357- pltcl_be_init_done = true;
358- }
359- }
360-
361-
362338/**********************************************************************
363339 * pltcl_init_interp() - initialize a Tcl interpreter
340+ *
341+ * The work done here must be safe to do in the postmaster process,
342+ * in case the pltcl library is preloaded in the postmaster. Note
343+ * that this is applied separately to the "normal" and "safe" interpreters.
364344 **********************************************************************/
365345static void
366346pltcl_init_interp (Tcl_Interp * interp )
@@ -387,6 +367,42 @@ pltcl_init_interp(Tcl_Interp *interp)
387367pltcl_SPI_lastoid ,NULL ,NULL );
388368}
389369
370+ /**********************************************************************
371+ * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
372+ *
373+ * This also takes care of any on-first-use initialization required.
374+ * The initialization work done here can't be done in the postmaster, and
375+ * hence is not safe to do at library load time, because it may invoke
376+ * arbitrary user-defined code.
377+ * Note: we assume caller has already connected to SPI.
378+ **********************************************************************/
379+ static Tcl_Interp *
380+ pltcl_fetch_interp (bool pltrusted )
381+ {
382+ Tcl_Interp * interp ;
383+
384+ /* On first use, we try to load the unknown procedure from pltcl_modules */
385+ if (pltrusted )
386+ {
387+ interp = pltcl_safe_interp ;
388+ if (!pltcl_be_safe_init_done )
389+ {
390+ pltcl_init_load_unknown (interp );
391+ pltcl_be_safe_init_done = true;
392+ }
393+ }
394+ else
395+ {
396+ interp = pltcl_norm_interp ;
397+ if (!pltcl_be_norm_init_done )
398+ {
399+ pltcl_init_load_unknown (interp );
400+ pltcl_be_norm_init_done = true;
401+ }
402+ }
403+
404+ return interp ;
405+ }
390406
391407/**********************************************************************
392408 * pltcl_init_load_unknown()- Load the unknown procedure from
@@ -395,6 +411,11 @@ pltcl_init_interp(Tcl_Interp *interp)
395411static void
396412pltcl_init_load_unknown (Tcl_Interp * interp )
397413{
414+ Relation pmrel ;
415+ char * pmrelname ,
416+ * nspname ;
417+ char * buf ;
418+ int buflen ;
398419int spi_rc ;
399420int tcl_rc ;
400421Tcl_DString unknown_src ;
@@ -404,47 +425,72 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
404425
405426/************************************************************
406427 * Check if table pltcl_modules exists
428+ *
429+ * We allow the table to be found anywhere in the search_path.
430+ * This is for backwards compatibility. To ensure that the table
431+ * is trustworthy, we require it to be owned by a superuser.
407432 ************************************************************/
408- spi_rc = SPI_execute ("select 1 from pg_catalog.pg_class "
409- "where relname = 'pltcl_modules'" ,
410- false,1 );
411- SPI_freetuptable (SPI_tuptable );
412- if (spi_rc != SPI_OK_SELECT )
413- elog (ERROR ,"select from pg_class failed" );
414- if (SPI_processed == 0 )
433+ pmrel = try_relation_openrv (makeRangeVar (NULL ,"pltcl_modules" ,-1 ),
434+ AccessShareLock );
435+ if (pmrel == NULL )
436+ return ;
437+ /* must be table or view, else ignore */
438+ if (!(pmrel -> rd_rel -> relkind == RELKIND_RELATION ||
439+ pmrel -> rd_rel -> relkind == RELKIND_VIEW ))
440+ {
441+ relation_close (pmrel ,AccessShareLock );
442+ return ;
443+ }
444+ /* must be owned by superuser, else ignore */
445+ if (!superuser_arg (pmrel -> rd_rel -> relowner ))
446+ {
447+ relation_close (pmrel ,AccessShareLock );
415448return ;
449+ }
450+ /* get fully qualified table name for use in select command */
451+ nspname = get_namespace_name (RelationGetNamespace (pmrel ));
452+ if (!nspname )
453+ elog (ERROR ,"cache lookup failed for namespace %u" ,
454+ RelationGetNamespace (pmrel ));
455+ pmrelname = quote_qualified_identifier (nspname ,
456+ RelationGetRelationName (pmrel ));
416457
417458/************************************************************
418- * Read all therow's from it where modname = 'unknown' in
419- * the order of modseq
459+ * Read all therows from it where modname = 'unknown',
460+ *in the order of modseq
420461 ************************************************************/
421- Tcl_DStringInit (& unknown_src );
462+ buflen = strlen (pmrelname )+ 100 ;
463+ buf = (char * )palloc (buflen );
464+ snprintf (buf ,buflen ,
465+ "select modsrc from %s where modname = 'unknown' order by modseq" ,
466+ pmrelname );
422467
423- spi_rc = SPI_execute ("select modseq, modsrc from pltcl_modules "
424- "where modname = 'unknown' "
425- "order by modseq" ,
426- false,0 );
468+ spi_rc = SPI_execute (buf , false,0 );
427469if (spi_rc != SPI_OK_SELECT )
428470elog (ERROR ,"select from pltcl_modules failed" );
429471
472+ pfree (buf );
473+
430474/************************************************************
431475 * If there's nothing, module unknown doesn't exist
432476 ************************************************************/
433477if (SPI_processed == 0 )
434478{
435- Tcl_DStringFree (& unknown_src );
436479SPI_freetuptable (SPI_tuptable );
437480elog (WARNING ,"module \"unknown\" not found in pltcl_modules" );
481+ relation_close (pmrel ,AccessShareLock );
438482return ;
439483}
440484
441485/************************************************************
442- * There is a module named unknown.Resemble the
486+ * There is a module named unknown.Reassemble the
443487 * source from the modsrc attributes and evaluate
444488 * it in the Tcl interpreter
445489 ************************************************************/
446490fno = SPI_fnumber (SPI_tuptable -> tupdesc ,"modsrc" );
447491
492+ Tcl_DStringInit (& unknown_src );
493+
448494for (i = 0 ;i < SPI_processed ;i ++ )
449495{
450496part = SPI_getvalue (SPI_tuptable -> vals [i ],
@@ -458,8 +504,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
458504}
459505}
460506tcl_rc = Tcl_GlobalEval (interp ,Tcl_DStringValue (& unknown_src ));
507+
461508Tcl_DStringFree (& unknown_src );
462509SPI_freetuptable (SPI_tuptable );
510+
511+ if (tcl_rc != TCL_OK )
512+ {
513+ UTF_BEGIN ;
514+ elog (ERROR ,"could not load module \"unknown\": %s" ,
515+ UTF_U2E (Tcl_GetStringResult (interp )));
516+ UTF_END ;
517+ }
518+
519+ relation_close (pmrel ,AccessShareLock );
463520}
464521
465522
@@ -480,11 +537,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
480537FunctionCallInfo save_fcinfo ;
481538pltcl_proc_desc * save_prodesc ;
482539
483- /*
484- * Initialize interpreters if first time through
485- */
486- pltcl_init_all ();
487-
488540/*
489541 * Ensure that static pointers are saved/restored properly
490542 */
@@ -558,10 +610,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
558610
559611pltcl_current_prodesc = prodesc ;
560612
561- if (prodesc -> lanpltrusted )
562- interp = pltcl_safe_interp ;
563- else
564- interp = pltcl_norm_interp ;
613+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
565614
566615/************************************************************
567616 * Create the tcl command to call the internal
@@ -719,10 +768,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
719768
720769pltcl_current_prodesc = prodesc ;
721770
722- if (prodesc -> lanpltrusted )
723- interp = pltcl_safe_interp ;
724- else
725- interp = pltcl_norm_interp ;
771+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
726772
727773tupdesc = trigdata -> tg_relation -> rd_att ;
728774
@@ -1152,10 +1198,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
11521198prodesc -> lanpltrusted = langStruct -> lanpltrusted ;
11531199ReleaseSysCache (langTup );
11541200
1155- if (prodesc -> lanpltrusted )
1156- interp = pltcl_safe_interp ;
1157- else
1158- interp = pltcl_norm_interp ;
1201+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
11591202
11601203/************************************************************
11611204 * Get the required information for input conversion of the