@ -33,7 +33,7 @@
* ENHANCEMENTS , OR MODIFICATIONS .
* ENHANCEMENTS , OR MODIFICATIONS .
*
*
* IDENTIFICATION
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / pl / plperl / plperl . c , v 1.94 .2 .3 2006 / 01 / 17 17 : 33 : 22 tgl Exp $
* $ PostgreSQL : pgsql / src / pl / plperl / plperl . c , v 1.94 .2 .4 2006 / 01 / 28 03 : 28 : 19 neilc Exp $
*
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@ -98,22 +98,33 @@ typedef struct plperl_proc_desc
SV * reference ;
SV * reference ;
} plperl_proc_desc ;
} plperl_proc_desc ;
/*
* The information we cache for the duration of a single call to a
* function .
*/
typedef struct plperl_call_data
{
plperl_proc_desc * prodesc ;
FunctionCallInfo fcinfo ;
Tuplestorestate * tuple_store ;
TupleDesc ret_tdesc ;
AttInMetadata * attinmeta ;
MemoryContext tmp_cxt ;
} plperl_call_data ;
/**********************************************************************
/**********************************************************************
* Global data
* Global data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int plperl_firstcall = 1 ;
static bool plperl_firstcall = true ;
static bool plperl_safe_init_done = false ;
static bool plperl_safe_init_done = false ;
static PerlInterpreter * plperl_interp = NULL ;
static PerlInterpreter * plperl_interp = NULL ;
static HV * plperl_proc_hash = NULL ;
static HV * plperl_proc_hash = NULL ;
static bool plperl_use_strict = false ;
static bool plperl_use_strict = false ;
/* these are saved and restored by plperl_call_handler */
/* this is saved and restored by plperl_call_handler */
static plperl_proc_desc * plperl_current_prodesc = NULL ;
static plperl_call_data * current_call_data = NULL ;
static FunctionCallInfo plperl_current_caller_info ;
static Tuplestorestate * plperl_current_tuple_store ;
static TupleDesc plperl_current_tuple_desc ;
/**********************************************************************
/**********************************************************************
* Forward declarations
* Forward declarations
@ -171,7 +182,7 @@ plperl_init(void)
EmitWarningsOnPlaceholders ( " plperl " ) ;
EmitWarningsOnPlaceholders ( " plperl " ) ;
plperl_init_interp ( ) ;
plperl_init_interp ( ) ;
plperl_firstcall = 0 ;
plperl_firstcall = false ;
}
}
@ -302,7 +313,6 @@ plperl_safe_init(void)
plperl_safe_init_done = true ;
plperl_safe_init_done = true ;
}
}
/*
/*
* Perl likes to put a newline after its error messages ; clean up such
* Perl likes to put a newline after its error messages ; clean up such
*/
*/
@ -575,18 +585,11 @@ Datum
plperl_call_handler ( PG_FUNCTION_ARGS )
plperl_call_handler ( PG_FUNCTION_ARGS )
{
{
Datum retval ;
Datum retval ;
plperl_proc_desc * save_prodesc ;
plperl_call_data * save_call_data ;
FunctionCallInfo save_caller_info ;
Tuplestorestate * save_tuple_store ;
TupleDesc save_tuple_desc ;
plperl_init_all ( ) ;
plperl_init_all ( ) ;
save_prodesc = plperl_current_prodesc ;
save_call_data = current_call_data ;
save_caller_info = plperl_current_caller_info ;
save_tuple_store = plperl_current_tuple_store ;
save_tuple_desc = plperl_current_tuple_desc ;
PG_TRY ( ) ;
PG_TRY ( ) ;
{
{
if ( CALLED_AS_TRIGGER ( fcinfo ) )
if ( CALLED_AS_TRIGGER ( fcinfo ) )
@ -596,19 +599,12 @@ plperl_call_handler(PG_FUNCTION_ARGS)
}
}
PG_CATCH ( ) ;
PG_CATCH ( ) ;
{
{
plperl_current_prodesc = save_prodesc ;
current_call_data = save_call_data ;
plperl_current_caller_info = save_caller_info ;
plperl_current_tuple_store = save_tuple_store ;
plperl_current_tuple_desc = save_tuple_desc ;
PG_RE_THROW ( ) ;
PG_RE_THROW ( ) ;
}
}
PG_END_TRY ( ) ;
PG_END_TRY ( ) ;
plperl_current_prodesc = save_prodesc ;
current_call_data = save_call_data ;
plperl_current_caller_info = save_caller_info ;
plperl_current_tuple_store = save_tuple_store ;
plperl_current_tuple_desc = save_tuple_desc ;
return retval ;
return retval ;
}
}
@ -923,15 +919,18 @@ plperl_func_handler(PG_FUNCTION_ARGS)
ReturnSetInfo * rsi ;
ReturnSetInfo * rsi ;
SV * array_ret = NULL ;
SV * array_ret = NULL ;
/*
* Create the call_data beforing connecting to SPI , so that it is
* not allocated in the SPI memory context
*/
current_call_data = ( plperl_call_data * ) palloc0 ( sizeof ( plperl_call_data ) ) ;
current_call_data - > fcinfo = fcinfo ;
if ( SPI_connect ( ) ! = SPI_OK_CONNECT )
if ( SPI_connect ( ) ! = SPI_OK_CONNECT )
elog ( ERROR , " could not connect to SPI manager " ) ;
elog ( ERROR , " could not connect to SPI manager " ) ;
prodesc = compile_plperl_function ( fcinfo - > flinfo - > fn_oid , false ) ;
prodesc = compile_plperl_function ( fcinfo - > flinfo - > fn_oid , false ) ;
current_call_data - > prodesc = prodesc ;
plperl_current_prodesc = prodesc ;
plperl_current_caller_info = fcinfo ;
plperl_current_tuple_store = 0 ;
plperl_current_tuple_desc = 0 ;
rsi = ( ReturnSetInfo * ) fcinfo - > resultinfo ;
rsi = ( ReturnSetInfo * ) fcinfo - > resultinfo ;
@ -988,10 +987,10 @@ plperl_func_handler(PG_FUNCTION_ARGS)
}
}
rsi - > returnMode = SFRM_Materialize ;
rsi - > returnMode = SFRM_Materialize ;
if ( plperl_ current_tuple_store)
if ( current_call_data - > tuple_store )
{
{
rsi - > setResult = plperl_ current_tuple_store;
rsi - > setResult = current_call_data - > tuple_store ;
rsi - > setDesc = plperl_current_tuple_ desc;
rsi - > setDesc = current_call_data - > ret_t desc;
}
}
retval = ( Datum ) 0 ;
retval = ( Datum ) 0 ;
}
}
@ -1056,6 +1055,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
if ( array_ret = = NULL )
if ( array_ret = = NULL )
SvREFCNT_dec ( perlret ) ;
SvREFCNT_dec ( perlret ) ;
current_call_data = NULL ;
return retval ;
return retval ;
}
}
@ -1069,14 +1069,20 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
SV * svTD ;
SV * svTD ;
HV * hvTD ;
HV * hvTD ;
/*
* Create the call_data beforing connecting to SPI , so that it is
* not allocated in the SPI memory context
*/
current_call_data = ( plperl_call_data * ) palloc0 ( sizeof ( plperl_call_data ) ) ;
current_call_data - > fcinfo = fcinfo ;
/* Connect to SPI manager */
/* Connect to SPI manager */
if ( SPI_connect ( ) ! = SPI_OK_CONNECT )
if ( SPI_connect ( ) ! = SPI_OK_CONNECT )
elog ( ERROR , " could not connect to SPI manager " ) ;
elog ( ERROR , " could not connect to SPI manager " ) ;
/* Find or compile the function */
/* Find or compile the function */
prodesc = compile_plperl_function ( fcinfo - > flinfo - > fn_oid , true ) ;
prodesc = compile_plperl_function ( fcinfo - > flinfo - > fn_oid , true ) ;
current_call_data - > prodesc = prodesc ;
plperl_current_prodesc = prodesc ;
svTD = plperl_trigger_build_args ( fcinfo ) ;
svTD = plperl_trigger_build_args ( fcinfo ) ;
perlret = plperl_call_perl_trigger_func ( prodesc , fcinfo , svTD ) ;
perlret = plperl_call_perl_trigger_func ( prodesc , fcinfo , svTD ) ;
@ -1147,6 +1153,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
if ( perlret )
if ( perlret )
SvREFCNT_dec ( perlret ) ;
SvREFCNT_dec ( perlret ) ;
current_call_data = NULL ;
return retval ;
return retval ;
}
}
@ -1471,7 +1478,7 @@ plperl_spi_exec(char *query, int limit)
{
{
int spi_rv ;
int spi_rv ;
spi_rv = SPI_execute ( query , plperl_ current_prodesc- > fn_readonly ,
spi_rv = SPI_execute ( query , current_call_data - > prodesc - > fn_readonly ,
limit ) ;
limit ) ;
ret_hv = plperl_spi_execute_fetch_result ( SPI_tuptable , SPI_processed ,
ret_hv = plperl_spi_execute_fetch_result ( SPI_tuptable , SPI_processed ,
spi_rv ) ;
spi_rv ) ;
@ -1566,16 +1573,19 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
void
void
plperl_return_next ( SV * sv )
plperl_return_next ( SV * sv )
{
{
plperl_proc_desc * prodesc = plperl_current_prodesc ;
plperl_proc_desc * prodesc ;
FunctionCallInfo fcinfo = plperl_current_caller_info ;
FunctionCallInfo fcinfo ;
ReturnSetInfo * rsi = ( ReturnSetInfo * ) fcinfo - > resultinfo ;
ReturnSetInfo * rsi ;
MemoryContext cxt ;
MemoryContext old_ cxt;
HeapTuple tuple ;
HeapTuple tuple ;
TupleDesc tupdesc ;
if ( ! sv )
if ( ! sv )
return ;
return ;
prodesc = current_call_data - > prodesc ;
fcinfo = current_call_data - > fcinfo ;
rsi = ( ReturnSetInfo * ) fcinfo - > resultinfo ;
if ( ! prodesc - > fn_retisset )
if ( ! prodesc - > fn_retisset )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
@ -1588,28 +1598,68 @@ plperl_return_next(SV *sv)
errmsg ( " setof-composite-returning Perl function "
errmsg ( " setof-composite-returning Perl function "
" must call return_next with reference to hash " ) ) ) ;
" must call return_next with reference to hash " ) ) ) ;
cxt = MemoryContextSwitchTo ( rsi - > econtext - > ecxt_per_query_memory ) ;
if ( ! current_call_data - > ret_tdesc )
{
TupleDesc tupdesc ;
if ( ! plperl_current_tuple_store )
Assert ( ! current_call_data - > tuple_store ) ;
plperl_current_tuple_store =
Assert ( ! current_call_data - > attinmeta ) ;
tuplestore_begin_heap ( true , false , work_mem ) ;
/*
* This is the first call to return_next in the current
* PL / Perl function call , so memoize some lookups
*/
if ( prodesc - > fn_retistuple )
( void ) get_call_result_type ( fcinfo , NULL , & tupdesc ) ;
else
tupdesc = rsi - > expectedDesc ;
/*
* Make sure the tuple_store and ret_tdesc are sufficiently
* long - lived .
*/
old_cxt = MemoryContextSwitchTo ( rsi - > econtext - > ecxt_per_query_memory ) ;
current_call_data - > ret_tdesc = CreateTupleDescCopy ( tupdesc ) ;
current_call_data - > tuple_store =
tuplestore_begin_heap ( true , false , work_mem ) ;
if ( prodesc - > fn_retistuple )
if ( prodesc - > fn_retistuple )
{
{
TypeFuncClass rettype ;
current_call_data - > attinmeta =
AttInMetadata * attinmeta ;
TupleDescGetAttInMetadata ( current_call_data - > ret_tdesc ) ;
}
rettype = get_call_result_type ( fcinfo , NULL , & tupdesc ) ;
MemoryContextSwitchTo ( old_cxt ) ;
tupdesc = CreateTupleDescCopy ( tupdesc ) ;
attinmeta = TupleDescGetAttInMetadata ( tupdesc ) ;
tuple = plperl_build_tuple_result ( ( HV * ) SvRV ( sv ) , attinmeta ) ;
}
}
else
/*
* Producing the tuple we want to return requires making plenty of
* palloc ( ) allocations that are not cleaned up . Since this
* function can be called many times before the current memory
* context is reset , we need to do those allocations in a
* temporary context .
*/
if ( ! current_call_data - > tmp_cxt )
{
{
Datum ret ;
current_call_data - > tmp_cxt =
bool isNull ;
AllocSetContextCreate ( rsi - > econtext - > ecxt_per_tuple_memory ,
" PL/Perl return_next temporary cxt " ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_INITSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
}
tupdesc = CreateTupleDescCopy ( rsi - > expectedDesc ) ;
old_cxt = MemoryContextSwitchTo ( current_call_data - > tmp_cxt ) ;
if ( prodesc - > fn_retistuple )
{
tuple = plperl_build_tuple_result ( ( HV * ) SvRV ( sv ) ,
current_call_data - > attinmeta ) ;
}
else
{
Datum ret = ( Datum ) 0 ;
bool isNull = true ;
if ( SvOK ( sv ) & & SvTYPE ( sv ) ! = SVt_NULL )
if ( SvOK ( sv ) & & SvTYPE ( sv ) ! = SVt_NULL )
{
{
@ -1621,21 +1671,16 @@ plperl_return_next(SV *sv)
Int32GetDatum ( - 1 ) ) ;
Int32GetDatum ( - 1 ) ) ;
isNull = false ;
isNull = false ;
}
}
else
{
ret = ( Datum ) 0 ;
isNull = true ;
}
tuple = heap_form_tuple ( tup desc, & ret , & isNull ) ;
tuple = heap_form_tuple ( current_call_data - > ret_tdesc , & ret , & isNull ) ;
}
}
if ( ! plperl_current_tuple_desc )
/* Make sure to store the tuple in a long-lived memory context */
plperl_current_tuple_desc = tupdesc ;
MemoryContextSwitchTo ( rsi - > econtext - > ecxt_per_query_memory ) ;
tuplestore_puttuple ( current_call_data - > tuple_store , tuple ) ;
MemoryContextSwitchTo ( old_cxt ) ;
tuplestore_puttuple ( plperl_current_tuple_store , tuple ) ;
MemoryContextReset ( current_call_data - > tmp_cxt ) ;
heap_freetuple ( tuple ) ;
MemoryContextSwitchTo ( cxt ) ;
}
}