@ -45,8 +45,44 @@
/* defines PLPERL_SET_OPMASK */
# include "plperl_opmask.h"
EXTERN_C void boot_DynaLoader ( pTHX_ CV * cv ) ;
EXTERN_C void boot_SPI ( pTHX_ CV * cv ) ;
PG_MODULE_MAGIC ;
/**********************************************************************
* Information associated with a Perl interpreter . We have one interpreter
* that is used for all plperlu ( untrusted ) functions . For plperl ( trusted )
* functions , there is a separate interpreter for each effective SQL userid .
* ( This is needed to ensure that an unprivileged user can ' t inject Perl code
* that ' ll be executed with the privileges of some other SQL user . )
*
* The plperl_interp_desc structs are kept in a Postgres hash table indexed
* by userid OID , with OID 0 used for the single untrusted interpreter .
*
* We start out by creating a " held " interpreter , which we initialize
* only as far as we can do without deciding if it will be trusted or
* untrusted . Later , when we first need to run a plperl or plperlu
* function , we complete the initialization appropriately and move the
* PerlInterpreter pointer into the plperl_interp_hash hashtable . If after
* that we need more interpreters , we create them as needed if we can , or
* fail if the Perl build doesn ' t support multiple interpreters .
*
* The reason for all the dancing about with a held interpreter is to make
* it possible for people to preload a lot of Perl code at postmaster startup
* ( using plperl . on_init ) and then use that code in backends . Of course this
* will only work for the first interpreter created in any backend , but it ' s
* still useful with that restriction .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct plperl_interp_desc
{
Oid user_id ; /* Hash key (must be first!) */
PerlInterpreter * interp ; /* The interpreter */
HTAB * query_hash ; /* plperl_query_entry structs */
} plperl_interp_desc ;
/**********************************************************************
* The information we cache about loaded procedures
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -55,6 +91,7 @@ typedef struct plperl_proc_desc
char * proname ; /* user name of procedure */
TransactionId fn_xmin ;
ItemPointerData fn_tid ;
plperl_interp_desc * interp ; /* interpreter it's created in */
bool fn_readonly ;
bool lanpltrusted ;
bool fn_retistuple ; /* true, if function returns tuple */
@ -69,14 +106,35 @@ typedef struct plperl_proc_desc
SV * reference ;
} plperl_proc_desc ;
/* hash table entry for proc desc */
/**********************************************************************
* For speedy lookup , we maintain a hash table mapping from
* function OID + trigger flag + user OID to plperl_proc_desc pointers .
* The reason the plperl_proc_desc struct isn ' t directly part of the hash
* entry is to simplify recovery from errors during compile_plperl_function .
*
* Note : if the same function is called by multiple userIDs within a session ,
* there will be a separate plperl_proc_desc entry for each userID in the case
* of plperl functions , but only one entry for plperlu functions , because we
* set user_id = 0 for that case . If the user redeclares the same function
* from plperl to plperlu or vice versa , there might be multiple
* plperl_proc_ptr entries in the hashtable , but only one is valid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct plperl_proc_key
{
Oid proc_id ; /* Function OID */
/*
* is_trigger is really a bool , but declare as Oid to ensure this struct
* contains no padding
*/
Oid is_trigger ; /* is it a trigger function? */
Oid user_id ; /* User calling the function, or 0 */
} plperl_proc_key ;
typedef struct plperl_proc_entry
typedef struct plperl_proc_ptr
{
char proc_name [ NAMEDATALEN ] ; /* internal name, eg
* __PLPerl_proc_39987 */
plperl_proc_desc * proc_data ;
} plperl_proc_entry ;
plperl_proc_key proc_key ; /* Hash key (must be first!) */
plperl_proc_desc * proc_ptr ;
} plperl_proc_ptr ;
/*
* The information we cache for the duration of a single call to a
@ -97,7 +155,7 @@ typedef struct plperl_call_data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct plperl_query_desc
{
char qname [ sizeof ( long ) * 2 + 1 ] ;
char qname [ 24 ] ;
void * plan ;
int nargs ;
Oid * argtypes ;
@ -117,32 +175,18 @@ typedef struct plperl_query_entry
* Global data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef enum
{
INTERP_NONE ,
INTERP_HELD ,
INTERP_TRUSTED ,
INTERP_UNTRUSTED ,
INTERP_BOTH
} InterpState ;
static InterpState interp_state = INTERP_NONE ;
static bool can_run_two = false ;
static bool plperl_safe_init_done = false ;
static PerlInterpreter * plperl_trusted_interp = NULL ;
static PerlInterpreter * plperl_untrusted_interp = NULL ;
static PerlInterpreter * plperl_held_interp = NULL ;
static OP * ( * pp_require_orig ) ( pTHX ) = NULL ;
static OP * pp_require_safe ( pTHX ) ;
static bool trusted_context ;
static HTAB * plperl_interp_hash = NULL ;
static HTAB * plperl_proc_hash = NULL ;
static HTAB * plperl_query_hash = NULL ;
static char plperl_opmask [ MAXO ] ;
static void set_interp_require ( void ) ;
static plperl_interp_desc * plperl_active_interp = NULL ;
/* If we have an unassigned "held" interpreter, it's stored here */
static PerlInterpreter * plperl_held_interp = NULL ;
/* GUC variables */
static bool plperl_use_strict = false ;
static OP * ( * pp_require_orig ) ( pTHX ) = NULL ;
static char plperl_opmask [ MAXO ] ;
/* this is saved and restored by plperl_call_handler */
static plperl_call_data * current_call_data = NULL ;
@ -153,7 +197,8 @@ Datum plperl_call_handler(PG_FUNCTION_ARGS);
Datum plperl_validator ( PG_FUNCTION_ARGS ) ;
void _PG_init ( void ) ;
static void plperl_init_interp ( void ) ;
static PerlInterpreter * plperl_init_interp ( void ) ;
static void set_interp_require ( bool trusted ) ;
static Datum plperl_func_handler ( PG_FUNCTION_ARGS ) ;
static Datum plperl_trigger_handler ( PG_FUNCTION_ARGS ) ;
@ -162,13 +207,17 @@ static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
static SV * plperl_hash_from_tuple ( HeapTuple tuple , TupleDesc tupdesc ) ;
static void plperl_init_shared_libs ( pTHX ) ;
static void plperl_trusted_init ( void ) ;
static void plperl_untrusted_init ( void ) ;
static HV * plperl_spi_execute_fetch_result ( SPITupleTable * , int , int ) ;
static SV * newSVstring ( const char * str ) ;
static SV * * hv_store_string ( HV * hv , const char * key , SV * val ) ;
static SV * * hv_fetch_string ( HV * hv , const char * key ) ;
static SV * plperl_create_sub ( char * proname , char * s , bool truste d) ;
static void plperl_create_sub ( plperl_proc_desc * desc , char * s , Oid fn_oi d) ;
static SV * plperl_call_perl_func ( plperl_proc_desc * desc , FunctionCallInfo fcinfo ) ;
static char * strip_trailing_ws ( const char * msg ) ;
static OP * pp_require_safe ( pTHX ) ;
static void activate_interpreter ( plperl_interp_desc * interp_desc ) ;
# ifdef WIN32
static char * setlocale_perl ( int category , char * locale ) ;
@ -219,25 +268,36 @@ _PG_init(void)
EmitWarningsOnPlaceholders ( " plperl " ) ;
MemSet ( & hash_ctl , 0 , sizeof ( hash_ctl ) ) ;
hash_ctl . keysize = NAMEDATALEN ;
hash_ctl . entrysize = sizeof ( plperl_proc_entry ) ;
plperl_proc_hash = hash_create ( " PLPerl Procedures " ,
32 ,
/*
* Create hash tables .
*/
memset ( & hash_ctl , 0 , sizeof ( hash_ctl ) ) ;
hash_ctl . keysize = sizeof ( Oid ) ;
hash_ctl . entrysize = sizeof ( plperl_interp_desc ) ;
hash_ctl . hash = oid_hash ;
plperl_interp_hash = hash_create ( " PL/Perl interpreters " ,
8 ,
& hash_ctl ,
HASH_ELEM ) ;
HASH_ELEM | HASH_FUNCTION ) ;
hash_ctl . entrysize = sizeof ( plperl_query_entry ) ;
plperl_query_hash = hash_create ( " PLPerl Queries " ,
memset ( & hash_ctl , 0 , sizeof ( hash_ctl ) ) ;
hash_ctl . keysize = sizeof ( plperl_proc_key ) ;
hash_ctl . entrysize = sizeof ( plperl_proc_ptr ) ;
hash_ctl . hash = tag_hash ;
plperl_proc_hash = hash_create ( " PL/Perl procedures " ,
32 ,
& hash_ctl ,
HASH_ELEM ) ;
HASH_ELEM | HASH_FUNCTION ) ;
/*
* Save the default opmask .
*/
PLPERL_SET_OPMASK ( plperl_opmask ) ;
plperl_init_interp ( ) ;
/*
* Create the first Perl interpreter , but only partially initialize it .
*/
plperl_held_interp = plperl_init_interp ( ) ;
inited = true ;
}
@ -287,17 +347,10 @@ _PG_init(void)
" require strict; "
# define TEST_FOR_MULTI \
" use Config; " \
" $Config{usemultiplicity} eq 'define' or " \
" ($Config{usethreads} eq 'define' " \
" and $Config{useithreads} eq 'define') "
static void
set_interp_require ( voi d)
set_interp_require ( bool trusted )
{
if ( trusted_context )
if ( trusted )
{
PL_ppaddr [ OP_REQUIRE ] = pp_require_safe ;
PL_ppaddr [ OP_DOFILE ] = pp_require_safe ;
@ -309,92 +362,142 @@ set_interp_require(void)
}
}
/********************************************************************
*
* We start out by creating a " held " interpreter that we can use in
* trusted or untrusted mode ( but not both ) as the need arises . Later , we
* assign that interpreter if it is available to either the trusted or
* untrusted interpreter . If it has already been assigned , and we need to
* create the other interpreter , we do that if we can , or error out .
* We detect if it is safe to run two interpreters during the setup of the
* dummy interpreter .
/*
* Select and activate an appropriate Perl interpreter .
*/
static void
check_interp ( bool trusted )
select_perl_context ( bool trusted )
{
if ( interp_state = = INTERP_HELD )
{
Oid user_id ;
plperl_interp_desc * interp_desc ;
bool found ;
PerlInterpreter * interp = NULL ;
/* Find or create the interpreter hashtable entry for this userid */
if ( trusted )
{
plperl_trusted_interp = plperl_held_interp ;
interp_state = INTERP_TRUSTED ;
}
user_id = GetUserId ( ) ;
else
user_id = InvalidOid ;
interp_desc = hash_search ( plperl_interp_hash , & user_id ,
HASH_ENTER ,
& found ) ;
if ( ! found )
{
plperl_untrusted_interp = plperl_held_interp ;
interp_state = INTERP_UNTRUSTED ;
}
plperl_held_interp = NULL ;
trusted_context = trusted ;
set_interp_require ( ) ;
/* Initialize newly-created hashtable entry */
interp_desc - > interp = NULL ;
interp_desc - > query_hash = NULL ;
}
else if ( interp_state = = INTERP_BOTH | |
( trusted & & interp_state = = INTERP_TRUSTED ) | |
( ! trusted & & interp_state = = INTERP_UNTRUSTED ) )
{
if ( trusted_context ! = trusted )
/* Make sure we have a query_hash for this interpreter */
if ( interp_desc - > query_hash = = NULL )
{
if ( trusted )
PERL_SET_CONTEXT ( plperl_trusted_interp ) ;
else
PERL_SET_CONTEXT ( plperl_untrusted_interp ) ;
trusted_context = trusted ;
set_interp_require ( ) ;
HASHCTL hash_ctl ;
memset ( & hash_ctl , 0 , sizeof ( hash_ctl ) ) ;
hash_ctl . keysize = NAMEDATALEN ;
hash_ctl . entrysize = sizeof ( plperl_query_entry ) ;
interp_desc - > query_hash = hash_create ( " PL/Perl queries " ,
32 ,
& hash_ctl ,
HASH_ELEM ) ;
}
/*
* Quick exit if already have an interpreter
*/
if ( interp_desc - > interp )
{
activate_interpreter ( interp_desc ) ;
return ;
}
else if ( can_run_two )
/*
* adopt held interp if free , else create new one if possible
*/
if ( plperl_held_interp ! = NULL )
{
PERL_SET_CONTEXT ( plperl_held_interp ) ;
plperl_init_interp ( ) ;
/* first actual use of a perl interpreter */
interp = plperl_held_interp ;
/*
* Reset the plperl_held_interp pointer first ; if we fail during init
* we don ' t want to try again with the partially - initialized interp .
*/
plperl_held_interp = NULL ;
if ( trusted )
plperl_trusted_interp = plperl_held_interp ;
plperl_trusted_init ( ) ;
else
plperl_untrusted_interp = plperl_held_interp ;
interp_state = INTERP_BOTH ;
plperl_held_interp = NULL ;
trusted_context = trusted ;
set_interp_require ( ) ;
plperl_untrusted_init ( ) ;
}
else
{
# ifdef MULTIPLICITY
/*
* plperl_init_interp will change Perl ' s idea of the active
* interpreter . Reset plperl_active_interp temporarily , so that if we
* hit an error partway through here , we ' ll make sure to switch back
* to a non - broken interpreter before running any other Perl
* functions .
*/
plperl_active_interp = NULL ;
/* Now build the new interpreter */
interp = plperl_init_interp ( ) ;
if ( trusted )
plperl_trusted_init ( ) ;
else
plperl_untrusted_init ( ) ;
# else
elog ( ERROR ,
" cannot allocate second Perl interpreter on this platform " ) ;
" cannot allocate multiple Perl interpreters on this platform " ) ;
# endif
}
set_interp_require ( trusted ) ;
/* Fully initialized, so mark the hashtable entry valid */
interp_desc - > interp = interp ;
/* And mark this as the active interpreter */
plperl_active_interp = interp_desc ;
}
/*
* Restore previous interpreter selection , if two are active
* Make the specified interpreter the active one
*
* A call with NULL does nothing . This is so that " restoring " to a previously
* null state of plperl_active_interp doesn ' t result in useless thrashing .
*/
static void
restore_context ( bool old_context )
activate_interpreter ( plperl_interp_desc * interp_desc )
{
if ( interp_state = = INTERP_BOTH & & trusted_context ! = old_context )
if ( interp_desc & & plperl_active_interp ! = interp_desc )
{
if ( old_context )
PERL_SET_CONTEXT ( plperl_trusted_interp ) ;
else
PERL_SET_CONTEXT ( plperl_untrusted_interp ) ;
trusted_context = old_context ;
set_interp_require ( ) ;
Assert ( interp_desc - > interp ) ;
PERL_SET_CONTEXT ( interp_desc - > interp ) ;
/* trusted iff user_id isn't InvalidOid */
set_interp_require ( OidIsValid ( interp_desc - > user_id ) ) ;
plperl_active_interp = interp_desc ;
}
}
static void
/*
* Create a new Perl interpreter .
*
* We initialize the interpreter as far as we can without knowing whether
* it will become a trusted or untrusted interpreter ; in particular , the
* plperl . on_init code will get executed . Later , either plperl_trusted_init
* or plperl_untrusted_init must be called to complete the initialization .
*/
static PerlInterpreter *
plperl_init_interp ( void )
{
PerlInterpreter * plperl ;
static int perl_sys_init_done ;
static char * embedding [ 3 ] = {
" " , " -e " , PERLBOOT
} ;
@ -457,19 +560,23 @@ plperl_init_interp(void)
*/
# if defined(PERL_SYS_INIT3) && !defined(MYMALLOC)
/* only call this the first time through, as per perlembed man page */
if ( interp_state = = INTERP_NONE )
if ( ! perl_sys_init_done )
{
char * dummy_env [ 1 ] = { NULL } ;
PERL_SYS_INIT3 ( & nargs , ( char * * * ) & embedding , ( char * * * ) & dummy_env ) ;
perl_sys_init_done = 1 ;
/* quiet warning if PERL_SYS_INIT3 doesn't use the third argument */
dummy_env [ 0 ] = NULL ;
}
# endif
plperl_held_interp = perl_alloc ( ) ;
if ( ! plperl_held_interp )
plperl = perl_alloc ( ) ;
if ( ! plperl )
elog ( ERROR , " could not allocate Perl interpreter " ) ;
perl_construct ( plperl_held_interp ) ;
PERL_SET_CONTEXT ( plperl ) ;
perl_construct ( plperl ) ;
/*
* Record the original function for the ' require ' and ' dofile ' opcodes .
@ -484,18 +591,16 @@ plperl_init_interp(void)
PL_ppaddr [ OP_DOFILE ] = pp_require_orig ;
}
perl_parse ( plperl_held_interp , plperl_init_shared_libs ,
nargs , embedding , NULL ) ;
perl_run ( plperl_held_interp ) ;
if ( interp_state = = INTERP_NONE )
{
SV * res ;
if ( perl_parse ( plperl , plperl_init_shared_libs ,
nargs , embedding , NULL ) ! = 0 )
ereport ( ERROR ,
( errmsg ( " %s " , strip_trailing_ws ( SvPV_nolen ( ERRSV ) ) ) ,
errcontext ( " while parsing Perl initialization " ) ) ) ;
res = eval_pv ( TEST_FOR_MULTI , TRUE ) ;
can_run_two = SvIV ( res ) ;
interp_state = INTERP_HELD ;
}
if ( perl_run ( plperl ) ! = 0 )
ereport ( ERROR ,
( errmsg ( " %s " , strip_trailing_ws ( SvPV_nolen ( ERRSV ) ) ) ,
errcontext ( " while running Perl initialization " ) ) ) ;
# ifdef PLPERL_RESTORE_LOCALE
PLPERL_RESTORE_LOCALE ( LC_COLLATE , save_collate ) ;
@ -505,6 +610,7 @@ plperl_init_interp(void)
PLPERL_RESTORE_LOCALE ( LC_TIME , save_time ) ;
# endif
return plperl ;
}
@ -537,9 +643,11 @@ pp_require_safe(pTHX)
DIE ( aTHX_ " Unable to load %s into plperl " , name ) ;
}
/*
* Initialize the current Perl interpreter as a trusted interp
*/
static void
plperl_safe_init ( void )
plperl_trusted _init ( void )
{
HV * stash ;
SV * sv ;
@ -600,8 +708,18 @@ plperl_safe_init(void)
# ifdef PL_stashcache
hv_clear ( PL_stashcache ) ;
# endif
}
plperl_safe_init_done = true ;
/*
* Initialize the current Perl interpreter as an untrusted interp
*/
static void
plperl_untrusted_init ( void )
{
/*
* Nothing to do here
*/
}
@ -890,7 +1008,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
Datum retval ;
plperl_call_data * save_call_data = current_call_data ;
bool oldcontext = trusted_context ;
plperl_interp_desc * oldinterp = plperl_active_interp ;
PG_TRY ( ) ;
{
@ -902,13 +1020,13 @@ plperl_call_handler(PG_FUNCTION_ARGS)
PG_CATCH ( ) ;
{
current_call_data = save_call_data ;
restore_context ( oldcontext ) ;
activate_interpreter ( oldinterp ) ;
PG_RE_THROW ( ) ;
}
PG_END_TRY ( ) ;
current_call_data = save_call_data ;
restore_context ( oldcontext ) ;
activate_interpreter ( oldinterp ) ;
return retval ;
}
@ -987,19 +1105,16 @@ plperl_validator(PG_FUNCTION_ARGS)
* Uses mkfunc to create an anonymous sub whose text is
* supplied in s , and returns a reference to the closure .
*/
static SV *
plperl_create_sub ( char * proname , char * s , bool truste d)
static void
plperl_create_sub ( plperl_proc_desc * prodesc , char * s , Oid fn_oi d)
{
dSP ;
char subname [ NAMEDATALEN + 40 ] ;
SV * subref ;
int count ;
char * compile_sub ;
if ( trusted & & ! plperl_safe_init_done )
{
plperl_safe_init ( ) ;
SPAGAIN ;
}
sprintf ( subname , " %s__%u " , prodesc - > proname , fn_oid ) ;
ENTER ;
SAVETMPS ;
@ -1039,7 +1154,7 @@ plperl_create_sub(char *proname, char *s, bool trusted)
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " creation of Perl function \" %s \" failed: %s " ,
proname ,
prodesc - > pro name ,
strip_trailing_ws ( SvPV ( ERRSV , PL_na ) ) ) ) ) ;
}
@ -1066,7 +1181,7 @@ plperl_create_sub(char *proname, char *s, bool trusted)
FREETMPS ;
LEAVE ;
return subref ;
prodesc - > reference = subref ;
}
@ -1078,10 +1193,6 @@ plperl_create_sub(char *proname, char *s, bool trusted)
* and do the initialization behind perl ' s back .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
EXTERN_C void boot_DynaLoader ( pTHX_ CV * cv ) ;
EXTERN_C void boot_SPI ( pTHX_ CV * cv ) ;
static void
plperl_init_shared_libs ( pTHX )
{
@ -1277,7 +1388,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
" cannot accept a set " ) ) ) ;
}
check_interp ( prodesc - > lanpltrusted ) ;
activate_interpreter ( prodesc - > interp ) ;
perlret = plperl_call_perl_func ( prodesc , fcinfo ) ;
@ -1416,7 +1527,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
prodesc = compile_plperl_function ( fcinfo - > flinfo - > fn_oid , true ) ;
current_call_data - > prodesc = prodesc ;
check_interp ( prodesc - > lanpltrusted ) ;
activate_interpreter ( prodesc - > interp ) ;
svTD = plperl_trigger_build_args ( fcinfo ) ;
perlret = plperl_call_perl_trigger_func ( prodesc , fcinfo , svTD ) ;
@ -1493,17 +1604,54 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
}
static bool
validate_plperl_function ( plperl_proc_ptr * proc_ptr , HeapTuple procTup )
{
if ( proc_ptr & & proc_ptr - > proc_ptr )
{
plperl_proc_desc * prodesc = proc_ptr - > proc_ptr ;
bool uptodate ;
/************************************************************
* If it ' s present , must check whether it ' s still up to date .
* This is needed because CREATE OR REPLACE FUNCTION can modify the
* function ' s pg_proc entry without changing its OID .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
uptodate = ( prodesc - > fn_xmin = = HeapTupleHeaderGetXmin ( procTup - > t_data ) & &
ItemPointerEquals ( & prodesc - > fn_tid , & procTup - > t_self ) ) ;
if ( uptodate )
return true ;
/* Otherwise, unlink the obsoleted entry from the hashtable ... */
proc_ptr - > proc_ptr = NULL ;
/* ... and throw it away */
if ( prodesc - > reference )
{
plperl_interp_desc * oldinterp = plperl_active_interp ;
activate_interpreter ( prodesc - > interp ) ;
SvREFCNT_dec ( prodesc - > reference ) ;
activate_interpreter ( oldinterp ) ;
}
free ( prodesc - > proname ) ;
free ( prodesc ) ;
}
return false ;
}
static plperl_proc_desc *
compile_plperl_function ( Oid fn_oid , bool is_trigger )
{
HeapTuple procTup ;
Form_pg_proc procStruct ;
char internal_proname [ NAMEDATALEN ] ;
plperl_proc_key proc_key ;
plperl_proc_ptr * proc_ptr ;
plperl_proc_desc * prodesc = NULL ;
int i ;
plperl_proc_entry * hash_entry ;
bool found ;
bool oldcontext = trusted_context ;
plperl_interp_desc * oldinterp = plperl_active_interp ;
/* We'll need the pg_proc tuple in any case... */
procTup = SearchSysCache ( PROCOID ,
@ -1513,48 +1661,24 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
elog ( ERROR , " cache lookup failed for function %u " , fn_oid ) ;
procStruct = ( Form_pg_proc ) GETSTRUCT ( procTup ) ;
/************************************************************
* Build our internal proc name from the function ' s Oid
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ( ! is_trigger )
sprintf ( internal_proname , " __PLPerl_proc_%u " , fn_oid ) ;
else
sprintf ( internal_proname , " __PLPerl_proc_%u_trigger " , fn_oid ) ;
/* Try to find function in plperl_proc_hash */
proc_key . proc_id = fn_oid ;
proc_key . is_trigger = is_trigger ;
proc_key . user_id = GetUserId ( ) ;
/************************************************************
* Lookup the internal proc name in the hashtable
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
hash_entry = hash_search ( plperl_proc_hash , internal_proname ,
proc_ptr = hash_search ( plperl_proc_hash , & proc_key ,
HASH_FIND , NULL ) ;
if ( hash_entry )
{
bool uptodate ;
prodesc = hash_entry - > proc_data ;
/************************************************************
* If it ' s present , must check whether it ' s still up to date .
* This is needed because CREATE OR REPLACE FUNCTION can modify the
* function ' s pg_proc entry without changing its OID .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
uptodate = ( prodesc - > fn_xmin = = HeapTupleHeaderGetXmin ( procTup - > t_data ) & &
ItemPointerEquals ( & prodesc - > fn_tid , & procTup - > t_self ) ) ;
if ( ! uptodate )
{
hash_search ( plperl_proc_hash , internal_proname ,
HASH_REMOVE , NULL ) ;
if ( prodesc - > reference )
if ( validate_plperl_function ( proc_ptr , procTup ) )
prodesc = proc_ptr - > proc_ptr ;
else
{
check_interp ( prodesc - > lanpltrusted ) ;
SvREFCNT_dec ( prodesc - > reference ) ;
restore_context ( oldcontext ) ;
}
free ( prodesc - > proname ) ;
free ( prodesc ) ;
prodesc = NULL ;
}
/* If not found or obsolete, maybe it's plperlu */
proc_key . user_id = InvalidOid ;
proc_ptr = hash_search ( plperl_proc_hash , & proc_key ,
HASH_FIND , NULL ) ;
if ( validate_plperl_function ( proc_ptr , procTup ) )
prodesc = proc_ptr - > proc_ptr ;
}
/************************************************************
@ -1585,6 +1709,10 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
errmsg ( " out of memory " ) ) ) ;
MemSet ( prodesc , 0 , sizeof ( plperl_proc_desc ) ) ;
prodesc - > proname = strdup ( NameStr ( procStruct - > proname ) ) ;
if ( prodesc - > proname = = NULL )
ereport ( ERROR ,
( errcode ( ERRCODE_OUT_OF_MEMORY ) ,
errmsg ( " out of memory " ) ) ) ;
prodesc - > fn_xmin = HeapTupleHeaderGetXmin ( procTup - > t_data ) ;
prodesc - > fn_tid = procTup - > t_self ;
@ -1724,29 +1852,33 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
proc_source = TextDatumGetCString ( prosrcdatum ) ;
/************************************************************
* Create the procedure in the interpreter
* Create the procedure in the appropriate interpreter
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
check_interp ( prodesc - > lanpltrusted ) ;
select_perl_context ( prodesc - > lanpltrusted ) ;
prodesc - > reference = plperl_create_sub ( prodesc - > proname ,
proc_source ,
prodesc - > lanpltrusted ) ;
prodesc - > interp = plperl_active_interp ;
restore_context ( oldcontext ) ;
plperl_create_sub ( prodesc , proc_source , fn_oid ) ;
activate_interpreter ( oldinterp ) ;
pfree ( proc_source ) ;
if ( ! prodesc - > reference ) /* can this happen? */
{
free ( prodesc - > proname ) ;
free ( prodesc ) ;
elog ( ERROR , " could not create internal procedure \" %s \" " ,
internal_proname ) ;
elog ( ERROR , " could not create PL/Perl internal procedure " ) ;
}
hash_entry = hash_search ( plperl_proc_hash , internal_proname ,
HASH_ENTER , & found ) ;
hash_entry - > proc_data = prodesc ;
/************************************************************
* OK , link the procedure into the correct hashtable entry
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
proc_key . user_id = prodesc - > lanpltrusted ? GetUserId ( ) : InvalidOid ;
proc_ptr = hash_search ( plperl_proc_hash , & proc_key ,
HASH_ENTER , NULL ) ;
proc_ptr - > proc_ptr = prodesc ;
}
ReleaseSysCache ( procTup ) ;
@ -2330,7 +2462,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
* the key to the caller .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
hash_entry = hash_search ( plperl_query_hash , qdesc - > qname ,
hash_entry = hash_search ( plperl_active_interp - > query_hash , qdesc - > qname ,
HASH_ENTER , & found ) ;
hash_entry - > query_data = qdesc ;
@ -2367,7 +2499,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
* Fetch the saved plan descriptor , see if it ' s o . k .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
hash_entry = hash_search ( plperl_query_hash , query ,
hash_entry = hash_search ( plperl_active_interp - > query_hash , query ,
HASH_FIND , NULL ) ;
if ( hash_entry = = NULL )
elog ( ERROR , " spi_exec_prepared: Invalid prepared query passed " ) ;
@ -2375,7 +2507,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv)
qdesc = hash_entry - > query_data ;
if ( qdesc = = NULL )
elog ( ERROR , " spi_exec_prepared: panic - plperl_ query_hash value vanished " ) ;
elog ( ERROR , " spi_exec_prepared: panic - plperl query_hash value vanished " ) ;
if ( qdesc - > nargs ! = argc )
elog ( ERROR , " spi_exec_prepared: expected %d argument(s), %d passed " ,
@ -2508,7 +2640,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
/************************************************************
* Fetch the saved plan descriptor , see if it ' s o . k .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
hash_entry = hash_search ( plperl_query_hash , query ,
hash_entry = hash_search ( plperl_active_interp - > query_hash , query ,
HASH_FIND , NULL ) ;
if ( hash_entry = = NULL )
elog ( ERROR , " spi_exec_prepared: Invalid prepared query passed " ) ;
@ -2516,7 +2648,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv)
qdesc = hash_entry - > query_data ;
if ( qdesc = = NULL )
elog ( ERROR , " spi_query_prepared: panic - plperl_ query_hash value vanished " ) ;
elog ( ERROR , " spi_query_prepared: panic - plperl query_hash value vanished " ) ;
if ( qdesc - > nargs ! = argc )
elog ( ERROR , " spi_query_prepared: expected %d argument(s), %d passed " ,
@ -2622,7 +2754,7 @@ plperl_spi_freeplan(char *query)
plperl_query_desc * qdesc ;
plperl_query_entry * hash_entry ;
hash_entry = hash_search ( plperl_query_hash , query ,
hash_entry = hash_search ( plperl_active_interp - > query_hash , query ,
HASH_FIND , NULL ) ;
if ( hash_entry = = NULL )
elog ( ERROR , " spi_exec_prepared: Invalid prepared query passed " ) ;
@ -2630,13 +2762,13 @@ plperl_spi_freeplan(char *query)
qdesc = hash_entry - > query_data ;
if ( qdesc = = NULL )
elog ( ERROR , " spi_exec_freeplan: panic - plperl_ query_hash value vanished " ) ;
elog ( ERROR , " spi_exec_freeplan: panic - plperl query_hash value vanished " ) ;
/*
* free all memory before SPI_freeplan , so if it dies , nothing will be
* left over
*/
hash_search ( plperl_query_hash , query ,
hash_search ( plperl_active_interp - > query_hash , query ,
HASH_REMOVE , NULL ) ;
plan = qdesc - > plan ;