2
2
* pltcl.c - PostgreSQL support for Tcl as
3
3
* procedural language (PL)
4
4
*
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 $
6
6
*
7
7
**********************************************************************/
8
8
@@ -120,7 +120,8 @@ typedef struct pltcl_query_desc
120
120
* Global data
121
121
**********************************************************************/
122
122
static 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;
124
125
static Tcl_Interp * pltcl_hold_interp = NULL ;
125
126
static Tcl_Interp * pltcl_norm_interp = NULL ;
126
127
static Tcl_Interp * pltcl_safe_interp = NULL ;
@@ -139,8 +140,8 @@ Datum pltcl_call_handler(PG_FUNCTION_ARGS);
139
140
Datum pltclu_call_handler (PG_FUNCTION_ARGS );
140
141
void _PG_init (void );
141
142
142
- static void pltcl_init_all (void );
143
143
static void pltcl_init_interp (Tcl_Interp * interp );
144
+ static Tcl_Interp * pltcl_fetch_interp (bool pltrusted );
144
145
static void pltcl_init_load_unknown (Tcl_Interp * interp );
145
146
146
147
static Datum pltcl_func_handler (PG_FUNCTION_ARGS );
@@ -334,33 +335,12 @@ _PG_init(void)
334
335
pltcl_pm_init_done = true;
335
336
}
336
337
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
-
362
338
/**********************************************************************
363
339
* 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.
364
344
**********************************************************************/
365
345
static void
366
346
pltcl_init_interp (Tcl_Interp * interp )
@@ -387,6 +367,42 @@ pltcl_init_interp(Tcl_Interp *interp)
387
367
pltcl_SPI_lastoid , NULL , NULL );
388
368
}
389
369
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
+ }
390
406
391
407
/**********************************************************************
392
408
* pltcl_init_load_unknown() - Load the unknown procedure from
@@ -395,6 +411,11 @@ pltcl_init_interp(Tcl_Interp *interp)
395
411
static void
396
412
pltcl_init_load_unknown (Tcl_Interp * interp )
397
413
{
414
+ Relation pmrel ;
415
+ char * pmrelname ,
416
+ * nspname ;
417
+ char * buf ;
418
+ int buflen ;
398
419
int spi_rc ;
399
420
int tcl_rc ;
400
421
Tcl_DString unknown_src ;
@@ -404,47 +425,72 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
404
425
405
426
/************************************************************
406
427
* 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.
407
432
************************************************************/
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 );
415
448
return ;
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 ));
416
457
417
458
/************************************************************
418
- * Read all the row's from it where modname = 'unknown' in
419
- * the order of modseq
459
+ * Read all the rows from it where modname = 'unknown',
460
+ * in the order of modseq
420
461
************************************************************/
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 );
422
467
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 );
427
469
if (spi_rc != SPI_OK_SELECT )
428
470
elog (ERROR , "select from pltcl_modules failed" );
429
471
472
+ pfree (buf );
473
+
430
474
/************************************************************
431
475
* If there's nothing, module unknown doesn't exist
432
476
************************************************************/
433
477
if (SPI_processed == 0 )
434
478
{
435
- Tcl_DStringFree (& unknown_src );
436
479
SPI_freetuptable (SPI_tuptable );
437
480
elog (WARNING , "module \"unknown\" not found in pltcl_modules" );
481
+ relation_close (pmrel , AccessShareLock );
438
482
return ;
439
483
}
440
484
441
485
/************************************************************
442
- * There is a module named unknown. Resemble the
486
+ * There is a module named unknown. Reassemble the
443
487
* source from the modsrc attributes and evaluate
444
488
* it in the Tcl interpreter
445
489
************************************************************/
446
490
fno = SPI_fnumber (SPI_tuptable -> tupdesc , "modsrc" );
447
491
492
+ Tcl_DStringInit (& unknown_src );
493
+
448
494
for (i = 0 ; i < SPI_processed ; i ++ )
449
495
{
450
496
part = SPI_getvalue (SPI_tuptable -> vals [i ],
@@ -458,8 +504,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
458
504
}
459
505
}
460
506
tcl_rc = Tcl_GlobalEval (interp , Tcl_DStringValue (& unknown_src ));
507
+
461
508
Tcl_DStringFree (& unknown_src );
462
509
SPI_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 );
463
520
}
464
521
465
522
@@ -480,11 +537,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
480
537
FunctionCallInfo save_fcinfo ;
481
538
pltcl_proc_desc * save_prodesc ;
482
539
483
- /*
484
- * Initialize interpreters if first time through
485
- */
486
- pltcl_init_all ();
487
-
488
540
/*
489
541
* Ensure that static pointers are saved/restored properly
490
542
*/
@@ -558,10 +610,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
558
610
559
611
pltcl_current_prodesc = prodesc ;
560
612
561
- if (prodesc -> lanpltrusted )
562
- interp = pltcl_safe_interp ;
563
- else
564
- interp = pltcl_norm_interp ;
613
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
565
614
566
615
/************************************************************
567
616
* Create the tcl command to call the internal
@@ -719,10 +768,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
719
768
720
769
pltcl_current_prodesc = prodesc ;
721
770
722
- if (prodesc -> lanpltrusted )
723
- interp = pltcl_safe_interp ;
724
- else
725
- interp = pltcl_norm_interp ;
771
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
726
772
727
773
tupdesc = trigdata -> tg_relation -> rd_att ;
728
774
@@ -1152,10 +1198,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
1152
1198
prodesc -> lanpltrusted = langStruct -> lanpltrusted ;
1153
1199
ReleaseSysCache (langTup );
1154
1200
1155
- if (prodesc -> lanpltrusted )
1156
- interp = pltcl_safe_interp ;
1157
- else
1158
- interp = pltcl_norm_interp ;
1201
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
1159
1202
1160
1203
/************************************************************
1161
1204
* Get the required information for input conversion of the
0 commit comments