3131 * ENHANCEMENTS, OR MODIFICATIONS.
3232 *
3333 * IDENTIFICATION
34- * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.79.2.2 2010/01/25 01:58:57 tgl Exp $
34+ * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.79.2.3 2010/05/13 18:29:54 tgl Exp $
3535 *
3636 **********************************************************************/
3737
4949#endif
5050
5151#include "access/heapam.h"
52+ #include "catalog/namespace.h"
5253#include "catalog/pg_language.h"
5354#include "catalog/pg_proc.h"
5455#include "catalog/pg_type.h"
5556#include "commands/trigger.h"
5657#include "executor/spi.h"
5758#include "fmgr.h"
59+ #include "miscadmin.h"
5860#include "nodes/makefuncs.h"
5961#include "parser/parse_type.h"
6062#include "tcop/tcopprot.h"
6163#include "utils/builtins.h"
64+ #include "utils/inval.h"
65+ #include "utils/lsyscache.h"
6266#include "utils/syscache.h"
6367
6468#if defined(UNICODE_CONVERSION ) && TCL_MAJOR_VERSION == 8 \
@@ -129,7 +133,8 @@ typedef struct pltcl_query_desc
129133 * Global data
130134 **********************************************************************/
131135static bool pltcl_pm_init_done = false;
132- static bool pltcl_be_init_done = false;
136+ static bool pltcl_be_norm_init_done = false;
137+ static bool pltcl_be_safe_init_done = false;
133138static int pltcl_call_level = 0 ;
134139static int pltcl_restart_in_progress = 0 ;
135140static Tcl_Interp * pltcl_hold_interp = NULL ;
@@ -143,9 +148,9 @@ static FunctionCallInfo pltcl_current_fcinfo = NULL;
143148/**********************************************************************
144149 * Forward declarations
145150 **********************************************************************/
146- static void pltcl_init_all (void );
147- static void pltcl_init_interp (Tcl_Interp * interp );
148151
152+ static void pltcl_init_interp (Tcl_Interp * interp );
153+ static Tcl_Interp * pltcl_fetch_interp (bool pltrusted );
149154static void pltcl_init_load_unknown (Tcl_Interp * interp );
150155
151156Datum pltcl_call_handler (PG_FUNCTION_ARGS );
@@ -249,38 +254,12 @@ pltcl_init(void)
249254 pltcl_pm_init_done = true;
250255}
251256
252- /**********************************************************************
253- * pltcl_init_all() - Initialize all
254- **********************************************************************/
255- static void
256- pltcl_init_all (void )
257- {
258- /************************************************************
259- * Execute postmaster-startup safe initialization
260- ************************************************************/
261- if (!pltcl_pm_init_done )
262- pltcl_init ();
263-
264- /************************************************************
265- * Any other initialization that must be done each time a new
266- * backend starts:
267- * - Try to load the unknown procedure from pltcl_modules
268- ************************************************************/
269- if (!pltcl_be_init_done )
270- {
271- if (SPI_connect () != SPI_OK_CONNECT )
272- elog (ERROR , "SPI_connect failed" );
273- pltcl_init_load_unknown (pltcl_norm_interp );
274- pltcl_init_load_unknown (pltcl_safe_interp );
275- if (SPI_finish () != SPI_OK_FINISH )
276- elog (ERROR , "SPI_finish failed" );
277- pltcl_be_init_done = true;
278- }
279- }
280-
281-
282257/**********************************************************************
283258 * pltcl_init_interp() - initialize a Tcl interpreter
259+ *
260+ * The work done here must be safe to do in the postmaster process,
261+ * in case the pltcl library is preloaded in the postmaster. Note
262+ * that this is applied separately to the "normal" and "safe" interpreters.
284263 **********************************************************************/
285264static void
286265pltcl_init_interp (Tcl_Interp * interp )
@@ -307,6 +286,42 @@ pltcl_init_interp(Tcl_Interp *interp)
307286 pltcl_SPI_lastoid , NULL , NULL );
308287}
309288
289+ /**********************************************************************
290+ * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
291+ *
292+ * This also takes care of any on-first-use initialization required.
293+ * The initialization work done here can't be done in the postmaster, and
294+ * hence is not safe to do at library load time, because it may invoke
295+ * arbitrary user-defined code.
296+ * Note: we assume caller has already connected to SPI.
297+ **********************************************************************/
298+ static Tcl_Interp *
299+ pltcl_fetch_interp (bool pltrusted )
300+ {
301+ Tcl_Interp * interp ;
302+
303+ /* On first use, we try to load the unknown procedure from pltcl_modules */
304+ if (pltrusted )
305+ {
306+ interp = pltcl_safe_interp ;
307+ if (!pltcl_be_safe_init_done )
308+ {
309+ pltcl_init_load_unknown (interp );
310+ pltcl_be_safe_init_done = true;
311+ }
312+ }
313+ else
314+ {
315+ interp = pltcl_norm_interp ;
316+ if (!pltcl_be_norm_init_done )
317+ {
318+ pltcl_init_load_unknown (interp );
319+ pltcl_be_norm_init_done = true;
320+ }
321+ }
322+
323+ return interp ;
324+ }
310325
311326/**********************************************************************
312327 * pltcl_init_load_unknown() - Load the unknown procedure from
@@ -315,6 +330,12 @@ pltcl_init_interp(Tcl_Interp *interp)
315330static void
316331pltcl_init_load_unknown (Tcl_Interp * interp )
317332{
333+ Oid relOid ;
334+ Relation pmrel ;
335+ char * pmrelname ,
336+ * nspname ;
337+ char * buf ;
338+ int buflen ;
318339 int spi_rc ;
319340 int tcl_rc ;
320341 Tcl_DString unknown_src ;
@@ -324,45 +345,87 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
324345
325346 /************************************************************
326347 * Check if table pltcl_modules exists
348+ *
349+ * We allow the table to be found anywhere in the search_path.
350+ * This is for backwards compatibility. To ensure that the table
351+ * is trustworthy, we require it to be owned by a superuser.
352+ *
353+ * this next bit of code is the same as try_relation_openrv(),
354+ * which only exists in 8.4 and up.
327355 ************************************************************/
328- spi_rc = SPI_exec ("select 1 from pg_catalog.pg_class "
329- "where relname = 'pltcl_modules'" , 1 );
330- SPI_freetuptable (SPI_tuptable );
331- if (spi_rc != SPI_OK_SELECT )
332- elog (ERROR , "select from pg_class failed" );
333- if (SPI_processed == 0 )
356+
357+ /* Check for shared-cache-inval messages */
358+ AcceptInvalidationMessages ();
359+
360+ /* Look up the appropriate relation using namespace search */
361+ relOid = RangeVarGetRelid (makeRangeVar (NULL , "pltcl_modules" ), true);
362+
363+ /* Drop out on not-found */
364+ if (!OidIsValid (relOid ))
334365 return ;
335366
367+ /* Let relation_open do the rest */
368+ pmrel = relation_open (relOid , AccessShareLock );
369+
370+ if (pmrel == NULL )
371+ return ;
372+ /* must be table or view, else ignore */
373+ if (!(pmrel -> rd_rel -> relkind == RELKIND_RELATION ||
374+ pmrel -> rd_rel -> relkind == RELKIND_VIEW ))
375+ {
376+ relation_close (pmrel , AccessShareLock );
377+ return ;
378+ }
379+ /* must be owned by superuser, else ignore */
380+ if (!superuser_arg (pmrel -> rd_rel -> relowner ))
381+ {
382+ relation_close (pmrel , AccessShareLock );
383+ return ;
384+ }
385+ /* get fully qualified table name for use in select command */
386+ nspname = get_namespace_name (RelationGetNamespace (pmrel ));
387+ if (!nspname )
388+ elog (ERROR , "cache lookup failed for namespace %u" ,
389+ RelationGetNamespace (pmrel ));
390+ pmrelname = quote_qualified_identifier (nspname ,
391+ RelationGetRelationName (pmrel ));
392+
336393 /************************************************************
337- * Read all the row's from it where modname = 'unknown' in
338- * the order of modseq
394+ * Read all the rows from it where modname = 'unknown',
395+ * in the order of modseq
339396 ************************************************************/
340- Tcl_DStringInit (& unknown_src );
397+ buflen = strlen (pmrelname ) + 100 ;
398+ buf = (char * ) palloc (buflen );
399+ snprintf (buf , buflen ,
400+ "select modsrc from %s where modname = 'unknown' order by modseq" ,
401+ pmrelname );
341402
342- spi_rc = SPI_exec ("select modseq, modsrc from pltcl_modules "
343- "where modname = 'unknown' "
344- "order by modseq" , 0 );
403+ spi_rc = SPI_exec (buf , 0 );
345404 if (spi_rc != SPI_OK_SELECT )
346405 elog (ERROR , "select from pltcl_modules failed" );
347406
407+ pfree (buf );
408+
348409 /************************************************************
349410 * If there's nothing, module unknown doesn't exist
350411 ************************************************************/
351412 if (SPI_processed == 0 )
352413 {
353- Tcl_DStringFree (& unknown_src );
354414 SPI_freetuptable (SPI_tuptable );
355415 elog (WARNING , "module \"unknown\" not found in pltcl_modules" );
416+ relation_close (pmrel , AccessShareLock );
356417 return ;
357418 }
358419
359420 /************************************************************
360- * There is a module named unknown. Resemble the
421+ * There is a module named unknown. Reassemble the
361422 * source from the modsrc attributes and evaluate
362423 * it in the Tcl interpreter
363424 ************************************************************/
364425 fno = SPI_fnumber (SPI_tuptable -> tupdesc , "modsrc" );
365426
427+ Tcl_DStringInit (& unknown_src );
428+
366429 for (i = 0 ; i < SPI_processed ; i ++ )
367430 {
368431 part = SPI_getvalue (SPI_tuptable -> vals [i ],
@@ -376,8 +439,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
376439 }
377440 }
378441 tcl_rc = Tcl_GlobalEval (interp , Tcl_DStringValue (& unknown_src ));
442+
379443 Tcl_DStringFree (& unknown_src );
380444 SPI_freetuptable (SPI_tuptable );
445+
446+ if (tcl_rc != TCL_OK )
447+ {
448+ UTF_BEGIN ;
449+ elog (ERROR , "could not load module \"unknown\": %s" ,
450+ UTF_U2E (Tcl_GetStringResult (interp )));
451+ UTF_END ;
452+ }
453+
454+ relation_close (pmrel , AccessShareLock );
381455}
382456
383457
@@ -398,9 +472,10 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
398472 FunctionCallInfo save_fcinfo ;
399473
400474 /************************************************************
401- * Initialize interpreters
475+ * Initialize interpreters if not done previously
402476 ************************************************************/
403- pltcl_init_all ();
477+ if (!pltcl_pm_init_done )
478+ pltcl_init ();
404479
405480 /************************************************************
406481 * Connect to SPI manager
@@ -467,10 +542,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
467542 /* Find or compile the function */
468543 prodesc = compile_pltcl_function (fcinfo -> flinfo -> fn_oid , InvalidOid );
469544
470- if (prodesc -> lanpltrusted )
471- interp = pltcl_safe_interp ;
472- else
473- interp = pltcl_norm_interp ;
545+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
474546
475547 /************************************************************
476548 * Create the tcl command to call the internal
@@ -654,10 +726,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
654726 prodesc = compile_pltcl_function (fcinfo -> flinfo -> fn_oid ,
655727 RelationGetRelid (trigdata -> tg_relation ));
656728
657- if (prodesc -> lanpltrusted )
658- interp = pltcl_safe_interp ;
659- else
660- interp = pltcl_norm_interp ;
729+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
661730
662731 tupdesc = trigdata -> tg_relation -> rd_att ;
663732
@@ -1078,10 +1147,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
10781147 prodesc -> lanpltrusted = langStruct -> lanpltrusted ;
10791148 ReleaseSysCache (langTup );
10801149
1081- if (prodesc -> lanpltrusted )
1082- interp = pltcl_safe_interp ;
1083- else
1084- interp = pltcl_norm_interp ;
1150+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
10851151
10861152 /************************************************************
10871153 * Get the required information for input conversion of the
0 commit comments