31
31
* ENHANCEMENTS, OR MODIFICATIONS.
32
32
*
33
33
* 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 $
35
35
*
36
36
**********************************************************************/
37
37
49
49
#endif
50
50
51
51
#include "access/heapam.h"
52
+ #include "catalog/namespace.h"
52
53
#include "catalog/pg_language.h"
53
54
#include "catalog/pg_proc.h"
54
55
#include "catalog/pg_type.h"
55
56
#include "commands/trigger.h"
56
57
#include "executor/spi.h"
57
58
#include "fmgr.h"
59
+ #include "miscadmin.h"
58
60
#include "nodes/makefuncs.h"
59
61
#include "parser/parse_type.h"
60
62
#include "tcop/tcopprot.h"
61
63
#include "utils/builtins.h"
64
+ #include "utils/inval.h"
65
+ #include "utils/lsyscache.h"
62
66
#include "utils/syscache.h"
63
67
64
68
#if defined(UNICODE_CONVERSION ) && TCL_MAJOR_VERSION == 8 \
@@ -129,7 +133,8 @@ typedef struct pltcl_query_desc
129
133
* Global data
130
134
**********************************************************************/
131
135
static 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;
133
138
static int pltcl_call_level = 0 ;
134
139
static int pltcl_restart_in_progress = 0 ;
135
140
static Tcl_Interp * pltcl_hold_interp = NULL ;
@@ -143,9 +148,9 @@ static FunctionCallInfo pltcl_current_fcinfo = NULL;
143
148
/**********************************************************************
144
149
* Forward declarations
145
150
**********************************************************************/
146
- static void pltcl_init_all (void );
147
- static void pltcl_init_interp (Tcl_Interp * interp );
148
151
152
+ static void pltcl_init_interp (Tcl_Interp * interp );
153
+ static Tcl_Interp * pltcl_fetch_interp (bool pltrusted );
149
154
static void pltcl_init_load_unknown (Tcl_Interp * interp );
150
155
151
156
Datum pltcl_call_handler (PG_FUNCTION_ARGS );
@@ -249,38 +254,12 @@ pltcl_init(void)
249
254
pltcl_pm_init_done = true;
250
255
}
251
256
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
-
282
257
/**********************************************************************
283
258
* 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.
284
263
**********************************************************************/
285
264
static void
286
265
pltcl_init_interp (Tcl_Interp * interp )
@@ -307,6 +286,42 @@ pltcl_init_interp(Tcl_Interp *interp)
307
286
pltcl_SPI_lastoid , NULL , NULL );
308
287
}
309
288
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
+ }
310
325
311
326
/**********************************************************************
312
327
* pltcl_init_load_unknown() - Load the unknown procedure from
@@ -315,6 +330,12 @@ pltcl_init_interp(Tcl_Interp *interp)
315
330
static void
316
331
pltcl_init_load_unknown (Tcl_Interp * interp )
317
332
{
333
+ Oid relOid ;
334
+ Relation pmrel ;
335
+ char * pmrelname ,
336
+ * nspname ;
337
+ char * buf ;
338
+ int buflen ;
318
339
int spi_rc ;
319
340
int tcl_rc ;
320
341
Tcl_DString unknown_src ;
@@ -324,45 +345,87 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
324
345
325
346
/************************************************************
326
347
* 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.
327
355
************************************************************/
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 ))
334
365
return ;
335
366
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
+
336
393
/************************************************************
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
339
396
************************************************************/
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 );
341
402
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 );
345
404
if (spi_rc != SPI_OK_SELECT )
346
405
elog (ERROR , "select from pltcl_modules failed" );
347
406
407
+ pfree (buf );
408
+
348
409
/************************************************************
349
410
* If there's nothing, module unknown doesn't exist
350
411
************************************************************/
351
412
if (SPI_processed == 0 )
352
413
{
353
- Tcl_DStringFree (& unknown_src );
354
414
SPI_freetuptable (SPI_tuptable );
355
415
elog (WARNING , "module \"unknown\" not found in pltcl_modules" );
416
+ relation_close (pmrel , AccessShareLock );
356
417
return ;
357
418
}
358
419
359
420
/************************************************************
360
- * There is a module named unknown. Resemble the
421
+ * There is a module named unknown. Reassemble the
361
422
* source from the modsrc attributes and evaluate
362
423
* it in the Tcl interpreter
363
424
************************************************************/
364
425
fno = SPI_fnumber (SPI_tuptable -> tupdesc , "modsrc" );
365
426
427
+ Tcl_DStringInit (& unknown_src );
428
+
366
429
for (i = 0 ; i < SPI_processed ; i ++ )
367
430
{
368
431
part = SPI_getvalue (SPI_tuptable -> vals [i ],
@@ -376,8 +439,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
376
439
}
377
440
}
378
441
tcl_rc = Tcl_GlobalEval (interp , Tcl_DStringValue (& unknown_src ));
442
+
379
443
Tcl_DStringFree (& unknown_src );
380
444
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 );
381
455
}
382
456
383
457
@@ -398,9 +472,10 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
398
472
FunctionCallInfo save_fcinfo ;
399
473
400
474
/************************************************************
401
- * Initialize interpreters
475
+ * Initialize interpreters if not done previously
402
476
************************************************************/
403
- pltcl_init_all ();
477
+ if (!pltcl_pm_init_done )
478
+ pltcl_init ();
404
479
405
480
/************************************************************
406
481
* Connect to SPI manager
@@ -467,10 +542,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
467
542
/* Find or compile the function */
468
543
prodesc = compile_pltcl_function (fcinfo -> flinfo -> fn_oid , InvalidOid );
469
544
470
- if (prodesc -> lanpltrusted )
471
- interp = pltcl_safe_interp ;
472
- else
473
- interp = pltcl_norm_interp ;
545
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
474
546
475
547
/************************************************************
476
548
* Create the tcl command to call the internal
@@ -654,10 +726,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
654
726
prodesc = compile_pltcl_function (fcinfo -> flinfo -> fn_oid ,
655
727
RelationGetRelid (trigdata -> tg_relation ));
656
728
657
- if (prodesc -> lanpltrusted )
658
- interp = pltcl_safe_interp ;
659
- else
660
- interp = pltcl_norm_interp ;
729
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
661
730
662
731
tupdesc = trigdata -> tg_relation -> rd_att ;
663
732
@@ -1078,10 +1147,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
1078
1147
prodesc -> lanpltrusted = langStruct -> lanpltrusted ;
1079
1148
ReleaseSysCache (langTup );
1080
1149
1081
- if (prodesc -> lanpltrusted )
1082
- interp = pltcl_safe_interp ;
1083
- else
1084
- interp = pltcl_norm_interp ;
1150
+ interp = pltcl_fetch_interp (prodesc -> lanpltrusted );
1085
1151
1086
1152
/************************************************************
1087
1153
* Get the required information for input conversion of the
0 commit comments