@ -2,7 +2,7 @@
* pltcl . c - PostgreSQL support for Tcl as
* procedural language ( PL )
*
* $ PostgreSQL : pgsql / src / pl / tcl / pltcl . c , v 1.117 .2 .2 2010 / 01 / 25 01 : 58 : 25 tgl Exp $
* $ PostgreSQL : pgsql / src / pl / tcl / pltcl . c , v 1.117 .2 .3 2010 / 05 / 13 18 : 29 : 25 tgl Exp $
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -19,15 +19,18 @@
# endif
# include "access/heapam.h"
# include "catalog/namespace.h"
# include "catalog/pg_language.h"
# include "catalog/pg_proc.h"
# include "commands/trigger.h"
# include "executor/spi.h"
# include "fmgr.h"
# include "miscadmin.h"
# include "nodes/makefuncs.h"
# include "parser/parse_type.h"
# include "tcop/tcopprot.h"
# include "utils/builtins.h"
# include "utils/inval.h"
# include "utils/lsyscache.h"
# include "utils/memutils.h"
# include "utils/syscache.h"
@ -113,7 +116,8 @@ typedef struct pltcl_query_desc
* Global data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static bool pltcl_pm_init_done = false ;
static bool pltcl_be_init_done = false ;
static bool pltcl_be_norm_init_done = false ;
static bool pltcl_be_safe_init_done = false ;
static Tcl_Interp * pltcl_hold_interp = NULL ;
static Tcl_Interp * pltcl_norm_interp = NULL ;
static Tcl_Interp * pltcl_safe_interp = NULL ;
@ -132,8 +136,8 @@ Datum pltcl_call_handler(PG_FUNCTION_ARGS);
Datum pltclu_call_handler ( PG_FUNCTION_ARGS ) ;
void _PG_init ( void ) ;
static void pltcl_init_all ( void ) ;
static void pltcl_init_interp ( Tcl_Interp * interp ) ;
static Tcl_Interp * pltcl_fetch_interp ( bool pltrusted ) ;
static void pltcl_init_load_unknown ( Tcl_Interp * interp ) ;
static Datum pltcl_func_handler ( PG_FUNCTION_ARGS ) ;
@ -325,33 +329,12 @@ _PG_init(void)
pltcl_pm_init_done = true ;
}
/**********************************************************************
* pltcl_init_all ( ) - Initialize all
*
* This does initialization that can ' t be done in the postmaster , and
* hence is not safe to do at library load time .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void
pltcl_init_all ( void )
{
/************************************************************
* Try to load the unknown procedure from pltcl_modules
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ( ! pltcl_be_init_done )
{
if ( SPI_connect ( ) ! = SPI_OK_CONNECT )
elog ( ERROR , " SPI_connect failed " ) ;
pltcl_init_load_unknown ( pltcl_norm_interp ) ;
pltcl_init_load_unknown ( pltcl_safe_interp ) ;
if ( SPI_finish ( ) ! = SPI_OK_FINISH )
elog ( ERROR , " SPI_finish failed " ) ;
pltcl_be_init_done = true ;
}
}
/**********************************************************************
* pltcl_init_interp ( ) - initialize a Tcl interpreter
*
* The work done here must be safe to do in the postmaster process ,
* in case the pltcl library is preloaded in the postmaster . Note
* that this is applied separately to the " normal " and " safe " interpreters .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void
pltcl_init_interp ( Tcl_Interp * interp )
@ -378,6 +361,42 @@ pltcl_init_interp(Tcl_Interp *interp)
pltcl_SPI_lastoid , NULL , NULL ) ;
}
/**********************************************************************
* pltcl_fetch_interp ( ) - fetch the Tcl interpreter to use for a function
*
* This also takes care of any on - first - use initialization required .
* The initialization work done here can ' t be done in the postmaster , and
* hence is not safe to do at library load time , because it may invoke
* arbitrary user - defined code .
* Note : we assume caller has already connected to SPI .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Tcl_Interp *
pltcl_fetch_interp ( bool pltrusted )
{
Tcl_Interp * interp ;
/* On first use, we try to load the unknown procedure from pltcl_modules */
if ( pltrusted )
{
interp = pltcl_safe_interp ;
if ( ! pltcl_be_safe_init_done )
{
pltcl_init_load_unknown ( interp ) ;
pltcl_be_safe_init_done = true ;
}
}
else
{
interp = pltcl_norm_interp ;
if ( ! pltcl_be_norm_init_done )
{
pltcl_init_load_unknown ( interp ) ;
pltcl_be_norm_init_done = true ;
}
}
return interp ;
}
/**********************************************************************
* pltcl_init_load_unknown ( ) - Load the unknown procedure from
@ -386,6 +405,12 @@ pltcl_init_interp(Tcl_Interp *interp)
static void
pltcl_init_load_unknown ( Tcl_Interp * interp )
{
Oid relOid ;
Relation pmrel ;
char * pmrelname ,
* nspname ;
char * buf ;
int buflen ;
int spi_rc ;
int tcl_rc ;
Tcl_DString unknown_src ;
@ -395,47 +420,87 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
/************************************************************
* Check if table pltcl_modules exists
*
* We allow the table to be found anywhere in the search_path .
* This is for backwards compatibility . To ensure that the table
* is trustworthy , we require it to be owned by a superuser .
*
* this next bit of code is the same as try_relation_openrv ( ) ,
* which only exists in 8.4 and up .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
spi_rc = SPI_execute ( " select 1 from pg_catalog.pg_class "
" where relname = 'pltcl_modules' " ,
false , 1 ) ;
SPI_freetuptable ( SPI_tuptable ) ;
if ( spi_rc ! = SPI_OK_SELECT )
elog ( ERROR , " select from pg_class failed " ) ;
if ( SPI_processed = = 0 )
/* Check for shared-cache-inval messages */
AcceptInvalidationMessages ( ) ;
/* Look up the appropriate relation using namespace search */
relOid = RangeVarGetRelid ( makeRangeVar ( NULL , " pltcl_modules " ) , true ) ;
/* Drop out on not-found */
if ( ! OidIsValid ( relOid ) )
return ;
/* Let relation_open do the rest */
pmrel = relation_open ( relOid , AccessShareLock ) ;
if ( pmrel = = NULL )
return ;
/* must be table or view, else ignore */
if ( ! ( pmrel - > rd_rel - > relkind = = RELKIND_RELATION | |
pmrel - > rd_rel - > relkind = = RELKIND_VIEW ) )
{
relation_close ( pmrel , AccessShareLock ) ;
return ;
}
/* must be owned by superuser, else ignore */
if ( ! superuser_arg ( pmrel - > rd_rel - > relowner ) )
{
relation_close ( pmrel , AccessShareLock ) ;
return ;
}
/* get fully qualified table name for use in select command */
nspname = get_namespace_name ( RelationGetNamespace ( pmrel ) ) ;
if ( ! nspname )
elog ( ERROR , " cache lookup failed for namespace %u " ,
RelationGetNamespace ( pmrel ) ) ;
pmrelname = quote_qualified_identifier ( nspname ,
RelationGetRelationName ( pmrel ) ) ;
/************************************************************
* Read all the row ' s from it where modname = ' unknown ' in
* the order of modseq
* Read all the rows from it where modname = ' unknown ' ,
* in the order of modseq
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Tcl_DStringInit ( & unknown_src ) ;
buflen = strlen ( pmrelname ) + 100 ;
buf = ( char * ) palloc ( buflen ) ;
snprintf ( buf , buflen ,
" select modsrc from %s where modname = 'unknown' order by modseq " ,
pmrelname ) ;
spi_rc = SPI_execute ( " select modseq, modsrc from pltcl_modules "
" where modname = 'unknown' "
" order by modseq " ,
false , 0 ) ;
spi_rc = SPI_execute ( buf , false , 0 ) ;
if ( spi_rc ! = SPI_OK_SELECT )
elog ( ERROR , " select from pltcl_modules failed " ) ;
pfree ( buf ) ;
/************************************************************
* If there ' s nothing , module unknown doesn ' t exist
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ( SPI_processed = = 0 )
{
Tcl_DStringFree ( & unknown_src ) ;
SPI_freetuptable ( SPI_tuptable ) ;
elog ( WARNING , " module \" unknown \" not found in pltcl_modules " ) ;
relation_close ( pmrel , AccessShareLock ) ;
return ;
}
/************************************************************
* There is a module named unknown . Resemble the
* There is a module named unknown . Reas semble the
* source from the modsrc attributes and evaluate
* it in the Tcl interpreter
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
fno = SPI_fnumber ( SPI_tuptable - > tupdesc , " modsrc " ) ;
Tcl_DStringInit ( & unknown_src ) ;
for ( i = 0 ; i < SPI_processed ; i + + )
{
part = SPI_getvalue ( SPI_tuptable - > vals [ i ] ,
@ -449,8 +514,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
}
}
tcl_rc = Tcl_GlobalEval ( interp , Tcl_DStringValue ( & unknown_src ) ) ;
Tcl_DStringFree ( & unknown_src ) ;
SPI_freetuptable ( SPI_tuptable ) ;
if ( tcl_rc ! = TCL_OK )
{
UTF_BEGIN ;
elog ( ERROR , " could not load module \" unknown \" : %s " ,
UTF_U2E ( Tcl_GetStringResult ( interp ) ) ) ;
UTF_END ;
}
relation_close ( pmrel , AccessShareLock ) ;
}
@ -471,11 +547,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
FunctionCallInfo save_fcinfo ;
pltcl_proc_desc * save_prodesc ;
/*
* Initialize interpreters if first time through
*/
pltcl_init_all ( ) ;
/*
* Ensure that static pointers are saved / restored properly
*/
@ -549,10 +620,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
pltcl_current_prodesc = prodesc ;
if ( prodesc - > lanpltrusted )
interp = pltcl_safe_interp ;
else
interp = pltcl_norm_interp ;
interp = pltcl_fetch_interp ( prodesc - > lanpltrusted ) ;
/************************************************************
* Create the tcl command to call the internal
@ -710,10 +778,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
pltcl_current_prodesc = prodesc ;
if ( prodesc - > lanpltrusted )
interp = pltcl_safe_interp ;
else
interp = pltcl_norm_interp ;
interp = pltcl_fetch_interp ( prodesc - > lanpltrusted ) ;
tupdesc = trigdata - > tg_relation - > rd_att ;
@ -1145,10 +1210,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
prodesc - > lanpltrusted = langStruct - > lanpltrusted ;
ReleaseSysCache ( langTup ) ;
if ( prodesc - > lanpltrusted )
interp = pltcl_safe_interp ;
else
interp = pltcl_norm_interp ;
interp = pltcl_fetch_interp ( prodesc - > lanpltrusted ) ;
/************************************************************
* Get the required information for input conversion of the