@ -98,17 +98,19 @@ typedef struct plperl_interp_desc
/**********************************************************************
* The information we cache about loaded procedures
*
* The refcount field counts the struct ' s reference from the hash table shown
* below , plus one reference for each function call level that is using the
* struct . We can release the struct , and the associated Perl sub , when the
* refcount goes to zero .
* The fn_refcount field counts the struct ' s reference from the hash table
* shown below , plus one reference for each function call level that is using
* the struct . We can release the struct , and the associated Perl sub , when
* the fn_refcount goes to zero . Releasing the struct itself is done by
* deleting the fn_cxt , which also gets rid of all subsidiary data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct plperl_proc_desc
{
char * proname ; /* user name of procedure */
MemoryContext fn_cxt ; /* memory context for this procedure */
unsigned long fn_refcount ; /* number of active references */
TransactionId fn_xmin ; /* xmin/TID of procedure's pg_proc tuple */
ItemPointerData fn_tid ;
int refcount ; /* reference count of this struct */
SV * reference ; /* CODE reference for Perl sub */
plperl_interp_desc * interp ; /* interpreter it's created in */
bool fn_readonly ; /* is function readonly (not volatile)? */
@ -122,18 +124,19 @@ typedef struct plperl_proc_desc
Oid result_oid ; /* Oid of result type */
FmgrInfo result_in_func ; /* I/O function and arg for result type */
Oid result_typioparam ;
/* Conversion info for function's argument types: */
/* Per-argument info for function's argument types: */
int nargs ;
FmgrInfo arg_out_func [ FUNC_MAX_ARGS ] ;
bool arg_is_rowtype [ FUNC_MAX_ARGS ] ;
Oid arg_arraytype [ FUNC_MAX_ARGS ] ; /* InvalidOid if not an array */
FmgrInfo * arg_out_func ; /* output fns for arg types */
bool * arg_is_rowtype ; /* is each arg composite? */
Oid * arg_arraytype ; /* InvalidOid if not an array */
} plperl_proc_desc ;
# define increment_prodesc_refcount(prodesc) \
( ( prodesc ) - > refcount + + )
( ( prodesc ) - > fn_ refcount+ + )
# define decrement_prodesc_refcount(prodesc) \
do { \
if ( - - ( ( prodesc ) - > refcount ) < = 0 ) \
Assert ( ( prodesc ) - > fn_refcount > 0 ) ; \
if ( - - ( ( prodesc ) - > fn_refcount ) = = 0 ) \
free_plperl_function ( prodesc ) ; \
} while ( 0 )
@ -353,23 +356,6 @@ hek2cstr(HE *he)
return ret ;
}
/*
* This routine is a crock , and so is everyplace that calls it . The problem
* is that the cached form of plperl functions / queries is allocated permanently
* ( mostly via malloc ( ) ) and never released until backend exit . Subsidiary
* data structures such as fmgr info records therefore must live forever
* as well . A better implementation would store all this stuff in a per -
* function memory context that could be reclaimed at need . In the meantime ,
* fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
* it might allocate , and whatever the eventual function might allocate using
* fn_mcxt , will live forever too .
*/
static void
perm_fmgr_info ( Oid functionId , FmgrInfo * finfo )
{
fmgr_info_cxt ( functionId , finfo , TopMemoryContext ) ;
}
/*
* _PG_init ( ) - library load - time initialization
@ -1433,6 +1419,10 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
SV * av ;
HV * hv ;
/*
* Currently we make no effort to cache any of the stuff we look up here ,
* which is bad .
*/
info = palloc0 ( sizeof ( plperl_array_info ) ) ;
/* get element type information, including output conversion function */
@ -1440,10 +1430,16 @@ plperl_ref_from_pg_array(Datum arg, Oid typid)
& typlen , & typbyval , & typalign ,
& typdelim , & typioparam , & typoutputfunc ) ;
if ( ( transform_funcid = get_transform_fromsql ( elementtype , current_call_data - > prodesc - > lang_oid , current_call_data - > prodesc - > trftypes ) ) )
perm_fmgr_info ( transform_funcid , & info - > transform_proc ) ;
/* Check for a transform function */
transform_funcid = get_transform_fromsql ( elementtype ,
current_call_data - > prodesc - > lang_oid ,
current_call_data - > prodesc - > trftypes ) ;
/* Look up transform or output function as appropriate */
if ( OidIsValid ( transform_funcid ) )
fmgr_info ( transform_funcid , & info - > transform_proc ) ;
else
perm_fmgr_info ( typoutputfunc , & info - > proc ) ;
fmgr_info ( typoutputfunc , & info - > proc ) ;
info - > elem_is_rowtype = type_is_rowtype ( elementtype ) ;
@ -1791,18 +1787,18 @@ plperl_call_handler(PG_FUNCTION_ARGS)
}
PG_CATCH ( ) ;
{
if ( this_call_data . prodesc )
decrement_prodesc_refcount ( this_call_data . prodesc ) ;
current_call_data = save_call_data ;
activate_interpreter ( oldinterp ) ;
if ( this_call_data . prodesc )
decrement_prodesc_refcount ( this_call_data . prodesc ) ;
PG_RE_THROW ( ) ;
}
PG_END_TRY ( ) ;
if ( this_call_data . prodesc )
decrement_prodesc_refcount ( this_call_data . prodesc ) ;
current_call_data = save_call_data ;
activate_interpreter ( oldinterp ) ;
if ( this_call_data . prodesc )
decrement_prodesc_refcount ( this_call_data . prodesc ) ;
return retval ;
}
@ -2616,7 +2612,7 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
static void
free_plperl_function ( plperl_proc_desc * prodesc )
{
Assert ( prodesc - > refcount < = 0 ) ;
Assert ( prodesc - > fn_refcount = = 0 ) ;
/* Release CODE reference, if we have one, from the appropriate interp */
if ( prodesc - > reference )
{
@ -2626,12 +2622,8 @@ free_plperl_function(plperl_proc_desc *prodesc)
SvREFCNT_dec ( prodesc - > reference ) ;
activate_interpreter ( oldinterp ) ;
}
/* Get rid of what we conveniently can of our own structs */
/* (FmgrInfo subsidiary info will get leaked ...) */
if ( prodesc - > proname )
free ( prodesc - > proname ) ;
list_free ( prodesc - > trftypes ) ;
free ( prodesc ) ;
/* Release all PG-owned data for this proc */
MemoryContextDelete ( prodesc - > fn_cxt ) ;
}
@ -2642,8 +2634,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
Form_pg_proc procStruct ;
plperl_proc_key proc_key ;
plperl_proc_ptr * proc_ptr ;
plperl_proc_desc * prodesc = NULL ;
int i ;
plperl_proc_desc * volatile prodesc = NULL ;
volatile MemoryContext proc_cxt = NULL ;
plperl_interp_desc * oldinterp = plperl_active_interp ;
ErrorContextCallback plperl_error_context ;
@ -2653,41 +2645,50 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
elog ( ERROR , " cache lookup failed for function %u " , fn_oid ) ;
procStruct = ( Form_pg_proc ) GETSTRUCT ( procTup ) ;
/* Set a callback for reporting compilation errors */
plperl_error_context . callback = plperl_compile_callback ;
plperl_error_context . previous = error_context_stack ;
plperl_error_context . arg = NameStr ( procStruct - > proname ) ;
error_context_stack = & plperl_error_context ;
/* Try to find function in plperl_proc_hash */
/*
* Try to find function in plperl_proc_hash . The reason for this
* overcomplicated - seeming lookup procedure is that we don ' t know whether
* it ' s plperl or plperlu , and don ' t want to spend a lookup in pg_language
* to find out .
*/
proc_key . proc_id = fn_oid ;
proc_key . is_trigger = is_trigger ;
proc_key . user_id = GetUserId ( ) ;
proc_ptr = hash_search ( plperl_proc_hash , & proc_key ,
HASH_FIND , NULL ) ;
if ( validate_plperl_function ( proc_ptr , procTup ) )
{
/* Found valid plperl entry */
ReleaseSysCache ( procTup ) ;
return proc_ptr - > proc_ptr ;
}
/* 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 ;
else
{
/* 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 ;
/* Found valid plperlu entry */
ReleaseSysCache ( procTup ) ;
return proc_ptr - > proc_ptr ;
}
/************************************************************
* If we haven ' t found it in the hashtable , we analyze
* the function ' s arguments and return type and store
* the in - / out - functions in the prodesc block and create
* a new hashtable entry for it .
*
* Then we load the procedure into the Perl interpreter .
* the in - / out - functions in the prodesc block ,
* then we load the procedure into the Perl interpreter ,
* and last we create a new hashtable entry for it .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ( prodesc = = NULL )
/* Set a callback for reporting compilation errors */
plperl_error_context . callback = plperl_compile_callback ;
plperl_error_context . previous = error_context_stack ;
plperl_error_context . arg = NameStr ( procStruct - > proname ) ;
error_context_stack = & plperl_error_context ;
PG_TRY ( ) ;
{
HeapTuple langTup ;
HeapTuple typeTup ;
@ -2697,42 +2698,42 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
Datum prosrcdatum ;
bool isnull ;
char * proc_source ;
MemoryContext oldcontext ;
/************************************************************
* Allocate a new procedure description block
* Allocate a context that will hold all PG data for the procedure .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
prodesc = ( plperl_proc_desc * ) malloc ( sizeof ( plperl_proc_desc ) ) ;
if ( prodesc = = NULL )
ereport ( ERROR ,
( errcode ( ERRCODE_OUT_OF_MEMORY ) ,
errmsg ( " out of memory " ) ) ) ;
/* Initialize all fields to 0 so free_plperl_function is safe */
MemSet ( prodesc , 0 , sizeof ( plperl_proc_desc ) ) ;
proc_cxt = AllocSetContextCreate ( TopMemoryContext ,
NameStr ( procStruct - > proname ) ,
ALLOCSET_SMALL_SIZES ) ;
prodesc - > proname = strdup ( NameStr ( procStruct - > proname ) ) ;
if ( prodesc - > proname = = NULL )
{
free_plperl_function ( prodesc ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_OUT_OF_MEMORY ) ,
errmsg ( " out of memory " ) ) ) ;
}
/************************************************************
* Allocate and fill a new procedure description block .
* struct prodesc and subsidiary data must all live in proc_cxt .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
oldcontext = MemoryContextSwitchTo ( proc_cxt ) ;
prodesc = ( plperl_proc_desc * ) palloc0 ( sizeof ( plperl_proc_desc ) ) ;
prodesc - > proname = pstrdup ( NameStr ( procStruct - > proname ) ) ;
prodesc - > fn_cxt = proc_cxt ;
prodesc - > fn_refcount = 0 ;
prodesc - > fn_xmin = HeapTupleHeaderGetRawXmin ( procTup - > t_data ) ;
prodesc - > fn_tid = procTup - > t_self ;
prodesc - > nargs = procStruct - > pronargs ;
prodesc - > arg_out_func = ( FmgrInfo * ) palloc0 ( prodesc - > nargs * sizeof ( FmgrInfo ) ) ;
prodesc - > arg_is_rowtype = ( bool * ) palloc0 ( prodesc - > nargs * sizeof ( bool ) ) ;
prodesc - > arg_arraytype = ( Oid * ) palloc0 ( prodesc - > nargs * sizeof ( Oid ) ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/* Remember if function is STABLE/IMMUTABLE */
prodesc - > fn_readonly =
( procStruct - > provolatile ! = PROVOLATILE_VOLATILE ) ;
{
MemoryContext oldcxt ;
protrftypes_datum = SysCacheGetAttr ( PROCOID , procTup ,
/* Fetch protrftypes */
protrftypes_datum = SysCacheGetAttr ( PROCOID , procTup ,
Anum_pg_proc_protrftypes , & isnull ) ;
oldcxt = MemoryContextSwitchTo ( TopMemoryContext ) ;
prodesc - > trftypes = isnull ? NIL : oid_array_to_list ( protrftypes_datum ) ;
MemoryContextSwitchTo ( oldcxt ) ;
}
MemoryContextSwitchTo ( proc_cxt ) ;
prodesc - > trftypes = isnull ? NIL : oid_array_to_list ( protrftypes_datum ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/************************************************************
* Lookup the pg_language tuple by Oid
@ -2740,11 +2741,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
langTup = SearchSysCache1 ( LANGOID ,
ObjectIdGetDatum ( procStruct - > prolang ) ) ;
if ( ! HeapTupleIsValid ( langTup ) )
{
free_plperl_function ( prodesc ) ;
elog ( ERROR , " cache lookup failed for language %u " ,
procStruct - > prolang ) ;
}
langStruct = ( Form_pg_language ) GETSTRUCT ( langTup ) ;
prodesc - > lang_oid = HeapTupleGetOid ( langTup ) ;
prodesc - > lanpltrusted = langStruct - > lanpltrusted ;
@ -2760,11 +2758,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
SearchSysCache1 ( TYPEOID ,
ObjectIdGetDatum ( procStruct - > prorettype ) ) ;
if ( ! HeapTupleIsValid ( typeTup ) )
{
free_plperl_function ( prodesc ) ;
elog ( ERROR , " cache lookup failed for type %u " ,
procStruct - > prorettype ) ;
}
typeStruct = ( Form_pg_type ) GETSTRUCT ( typeTup ) ;
/* Disallow pseudotype result, except VOID or RECORD */
@ -2775,21 +2770,15 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
/* okay */ ;
else if ( procStruct - > prorettype = = TRIGGEROID | |
procStruct - > prorettype = = EVTTRIGGEROID )
{
free_plperl_function ( prodesc ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " trigger functions can only be called "
" as triggers " ) ) ) ;
}
else
{
free_plperl_function ( prodesc ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " PL/Perl functions cannot return type %s " ,
format_type_be ( procStruct - > prorettype ) ) ) ) ;
}
}
prodesc - > result_oid = procStruct - > prorettype ;
@ -2800,7 +2789,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
prodesc - > fn_retisarray =
( typeStruct - > typlen = = - 1 & & typeStruct - > typelem ) ;
perm_fmgr_info ( typeStruct - > typinput , & ( prodesc - > result_in_func ) ) ;
fmgr_info_cxt ( typeStruct - > typinput ,
& ( prodesc - > result_in_func ) ,
proc_cxt ) ;
prodesc - > result_typioparam = getTypeIOParam ( typeTup ) ;
ReleaseSysCache ( typeTup ) ;
@ -2812,29 +2803,24 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
if ( ! is_trigger & & ! is_event_trigger )
{
prodesc - > nargs = procStruct - > pronargs ;
int i ;
for ( i = 0 ; i < prodesc - > nargs ; i + + )
{
typeTup = SearchSysCache1 ( TYPEOID ,
ObjectIdGetDatum ( procStruct - > proargtypes . values [ i ] ) ) ;
if ( ! HeapTupleIsValid ( typeTup ) )
{
free_plperl_function ( prodesc ) ;
elog ( ERROR , " cache lookup failed for type %u " ,
procStruct - > proargtypes . values [ i ] ) ;
}
typeStruct = ( Form_pg_type ) GETSTRUCT ( typeTup ) ;
/* Disallow pseudotype argument */
if ( typeStruct - > typtype = = TYPTYPE_PSEUDO & &
procStruct - > proargtypes . values [ i ] ! = RECORDOID )
{
free_plperl_function ( prodesc ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " PL/Perl functions cannot accept type %s " ,
format_type_be ( procStruct - > proargtypes . values [ i ] ) ) ) ) ;
}
if ( typeStruct - > typtype = = TYPTYPE_COMPOSITE | |
procStruct - > proargtypes . values [ i ] = = RECORDOID )
@ -2842,8 +2828,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
else
{
prodesc - > arg_is_rowtype [ i ] = false ;
perm_fmgr_info ( typeStruct - > typoutput ,
& ( prodesc - > arg_out_func [ i ] ) ) ;
fmgr_info_cxt ( typeStruct - > typoutput ,
& ( prodesc - > arg_out_func [ i ] ) ,
proc_cxt ) ;
}
/* Identify array attributes */
@ -2880,22 +2867,42 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
activate_interpreter ( oldinterp ) ;
pfree ( proc_source ) ;
if ( ! prodesc - > reference ) /* can this happen? */
{
free_plperl_function ( prodesc ) ;
elog ( ERROR , " could not create PL/Perl internal procedure " ) ;
}
/************************************************************
* OK , link the procedure into the correct hashtable entry
* OK , link the procedure into the correct hashtable entry .
* Note we assume that the hashtable entry either doesn ' t exist yet ,
* or we already cleared its proc_ptr during the validation attempts
* above . So no need to decrement an old refcount here .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
proc_key . user_id = prodesc - > lanpltrusted ? GetUserId ( ) : InvalidOid ;
proc_ptr = hash_search ( plperl_proc_hash , & proc_key ,
HASH_ENTER , NULL ) ;
/* We assume these two steps can't throw an error: */
proc_ptr - > proc_ptr = prodesc ;
increment_prodesc_refcount ( prodesc ) ;
}
PG_CATCH ( ) ;
{
/*
* If we got as far as creating a reference , we should be able to use
* free_plperl_function ( ) to clean up . If not , then at most we have
* some PG memory resources in proc_cxt , which we can just delete .
*/
if ( prodesc & & prodesc - > reference )
free_plperl_function ( prodesc ) ;
else if ( proc_cxt )
MemoryContextDelete ( proc_cxt ) ;
/* Be sure to restore the previous interpreter, too, for luck */
activate_interpreter ( oldinterp ) ;
PG_RE_THROW ( ) ;
}
PG_END_TRY ( ) ;
/* restore previous error callback */
error_context_stack = plperl_error_context . previous ;