@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / pl / plpgsql / src / pl_exec . c , v 1.248 2009 / 08 / 06 20 : 44 : 31 tgl Exp $
* $ PostgreSQL : pgsql / src / pl / plpgsql / src / pl_exec . c , v 1.249 2009 / 11 / 04 22 : 26 : 07 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -26,6 +26,7 @@
# include "lib/stringinfo.h"
# include "miscadmin.h"
# include "nodes/nodeFuncs.h"
# include "parser/parse_node.h"
# include "parser/scansup.h"
# include "storage/proc.h"
# include "tcop/tcopprot.h"
@ -154,10 +155,11 @@ static void exec_assign_value(PLpgSQL_execstate *estate,
Datum value , Oid valtype , bool * isNull ) ;
static void exec_eval_datum ( PLpgSQL_execstate * estate ,
PLpgSQL_datum * datum ,
Oid expectedtypeid ,
Oid * typeid ,
Datum * value ,
bool * isnull ) ;
static Oid exec_get_datum_type ( PLpgSQL_execstate * estate ,
PLpgSQL_datum * datum ) ;
static int exec_eval_integer ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr ,
bool * isNull ) ;
@ -172,8 +174,11 @@ static int exec_run_select(PLpgSQL_execstate *estate,
PLpgSQL_expr * expr , long maxtuples , Portal * portalP ) ;
static int exec_for_query ( PLpgSQL_execstate * estate , PLpgSQL_stmt_forq * stmt ,
Portal portal , bool prefetch_ok ) ;
static void eval_expr_params ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr , Datum * * p_values , char * * p_nulls ) ;
static ParamListInfo setup_param_list ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr ) ;
static void plpgsql_parser_setup ( ParseState * pstate , PLpgSQL_expr * expr ) ;
static Node * plpgsql_param_ref ( ParseState * pstate , ParamRef * pref ) ;
static void plpgsql_param_fetch ( ParamListInfo params , int paramid ) ;
static void exec_move_row ( PLpgSQL_execstate * estate ,
PLpgSQL_rec * rec ,
PLpgSQL_row * row ,
@ -514,12 +519,20 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
/*
* Put the OLD and NEW tuples into record variables
*
* We make the tupdescs available in both records even though only one
* may have a value . This allows parsing of record references to succeed
* in functions that are used for multiple trigger types . For example ,
* we might have a test like " if (TG_OP = 'INSERT' and NEW.foo = 'xyz') " ,
* which should parse regardless of the current trigger type .
*/
rec_new = ( PLpgSQL_rec * ) ( estate . datums [ func - > new_varno ] ) ;
rec_new - > freetup = false ;
rec_new - > tupdesc = trigdata - > tg_relation - > rd_att ;
rec_new - > freetupdesc = false ;
rec_old = ( PLpgSQL_rec * ) ( estate . datums [ func - > old_varno ] ) ;
rec_old - > freetup = false ;
rec_old - > tupdesc = trigdata - > tg_relation - > rd_att ;
rec_old - > freetupdesc = false ;
if ( TRIGGER_FIRED_FOR_STATEMENT ( trigdata - > tg_event ) )
@ -528,30 +541,22 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
* Per - statement triggers don ' t use OLD / NEW variables
*/
rec_new - > tup = NULL ;
rec_new - > tupdesc = NULL ;
rec_old - > tup = NULL ;
rec_old - > tupdesc = NULL ;
}
else if ( TRIGGER_FIRED_BY_INSERT ( trigdata - > tg_event ) )
{
rec_new - > tup = trigdata - > tg_trigtuple ;
rec_new - > tupdesc = trigdata - > tg_relation - > rd_att ;
rec_old - > tup = NULL ;
rec_old - > tupdesc = NULL ;
}
else if ( TRIGGER_FIRED_BY_UPDATE ( trigdata - > tg_event ) )
{
rec_new - > tup = trigdata - > tg_newtuple ;
rec_new - > tupdesc = trigdata - > tg_relation - > rd_att ;
rec_old - > tup = trigdata - > tg_trigtuple ;
rec_old - > tupdesc = trigdata - > tg_relation - > rd_att ;
}
else if ( TRIGGER_FIRED_BY_DELETE ( trigdata - > tg_event ) )
{
rec_new - > tup = NULL ;
rec_new - > tupdesc = NULL ;
rec_old - > tup = trigdata - > tg_trigtuple ;
rec_old - > tupdesc = trigdata - > tg_relation - > rd_att ;
}
else
elog ( ERROR , " unrecognized trigger action: not INSERT, DELETE, or UPDATE " ) ;
@ -631,19 +636,36 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
var - > isnull = false ;
var - > freeval = false ;
/*
* Store the trigger argument values into the special execution state
* variables
*/
estate . err_text = gettext_noop ( " while storing call arguments into local variables " ) ;
estate . trig_nargs = trigdata - > tg_trigger - > tgnargs ;
if ( estate . trig_nargs = = 0 )
estate . trig_argv = NULL ;
var = ( PLpgSQL_var * ) ( estate . datums [ func - > tg_argv_varno ] ) ;
if ( trigdata - > tg_trigger - > tgnargs > 0 )
{
/*
* For historical reasons , tg_argv [ ] subscripts start at zero not one .
* So we can ' t use construct_array ( ) .
*/
int nelems = trigdata - > tg_trigger - > tgnargs ;
Datum * elems ;
int dims [ 1 ] ;
int lbs [ 1 ] ;
elems = palloc ( sizeof ( Datum ) * nelems ) ;
for ( i = 0 ; i < nelems ; i + + )
elems [ i ] = CStringGetTextDatum ( trigdata - > tg_trigger - > tgargs [ i ] ) ;
dims [ 0 ] = nelems ;
lbs [ 0 ] = 0 ;
var - > value = PointerGetDatum ( construct_md_array ( elems , NULL ,
1 , dims , lbs ,
TEXTOID ,
- 1 , false , ' i ' ) ) ;
var - > isnull = false ;
var - > freeval = true ;
}
else
{
estate . trig_argv = palloc ( sizeof ( Datum ) * estate . trig_nargs ) ;
for ( i = 0 ; i < trigdata - > tg_trigger - > tgnargs ; i + + )
estate . trig_argv [ i ] = CStringGetTextDatum ( trigdata - > tg_trigger - > tgargs [ i ] ) ;
var - > value = ( Datum ) 0 ;
var - > isnull = true ;
va r - > freeval = false ;
}
estate . err_text = gettext_noop ( " during function entry " ) ;
@ -756,10 +778,6 @@ plpgsql_exec_error_callback(void *arg)
{
PLpgSQL_execstate * estate = ( PLpgSQL_execstate * ) arg ;
/* safety check, shouldn't happen */
if ( estate - > err_func = = NULL )
return ;
/* if we are doing RAISE, don't report its location */
if ( estate - > err_text = = raise_skip_msg )
return ;
@ -784,7 +802,7 @@ plpgsql_exec_error_callback(void *arg)
* local variable initialization "
*/
errcontext ( " PL/pgSQL function \" %s \" line %d %s " ,
estate - > err_ func- > fn_name ,
estate - > func - > fn_name ,
estate - > err_stmt - > lineno ,
_ ( estate - > err_text ) ) ;
}
@ -795,7 +813,7 @@ plpgsql_exec_error_callback(void *arg)
* arguments into local variables "
*/
errcontext ( " PL/pgSQL function \" %s \" %s " ,
estate - > err_ func- > fn_name ,
estate - > func - > fn_name ,
_ ( estate - > err_text ) ) ;
}
}
@ -803,13 +821,13 @@ plpgsql_exec_error_callback(void *arg)
{
/* translator: last %s is a plpgsql statement type name */
errcontext ( " PL/pgSQL function \" %s \" line %d at %s " ,
estate - > err_ func- > fn_name ,
estate - > func - > fn_name ,
estate - > err_stmt - > lineno ,
plpgsql_stmt_typename ( estate - > err_stmt ) ) ;
}
else
errcontext ( " PL/pgSQL function \" %s \" " ,
estate - > err_ func- > fn_name ) ;
estate - > func - > fn_name ) ;
}
@ -856,7 +874,6 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
case PLPGSQL_DTYPE_ROW :
case PLPGSQL_DTYPE_RECFIELD :
case PLPGSQL_DTYPE_ARRAYELEM :
case PLPGSQL_DTYPE_TRIGARG :
/*
* These datum records are read - only at runtime , so no need to
@ -977,10 +994,13 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
if ( rec - > freetup )
{
heap_freetuple ( rec - > tup ) ;
FreeTupleDesc ( rec - > tupdesc ) ;
rec - > freetup = false ;
}
if ( rec - > freetupdesc )
{
FreeTupleDesc ( rec - > tupdesc ) ;
rec - > freetupdesc = false ;
}
rec - > tup = NULL ;
rec - > tupdesc = NULL ;
}
@ -1885,10 +1905,9 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
PLpgSQL_var * curvar ;
char * curname = NULL ;
PLpgSQL_expr * query ;
ParamListInfo paramLI ;
Portal portal ;
int rc ;
Datum * values ;
char * nulls ;
/* ----------
* Get the cursor variable and if it has an assigned name , check
@ -1954,19 +1973,25 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
exec_prepare_plan ( estate , query , curvar - > cursor_options ) ;
/*
* Now build up the values and nulls arguments for SPI_execute_plan ( )
* Set up ParamListInfo ( note this is only carrying a hook function ,
* not any actual data values , at this point )
*/
eval_expr_params ( estate , query , & values , & nulls ) ;
paramLI = setup_param_list ( estate , query ) ;
/*
* Open the cursor
* Open the cursor ( the paramlist will get copied into the portal )
*/
portal = SPI_cursor_open ( curname , query - > plan , values , nulls ,
estate - > readonly_func ) ;
portal = SPI_cursor_open_with_paramlist ( curname , query - > plan ,
paramLI ,
estate - > readonly_func ) ;
if ( portal = = NULL )
elog ( ERROR , " could not open cursor: %s " ,
SPI_result_code_string ( SPI_result ) ) ;
/* don't need paramlist any more */
if ( paramLI )
pfree ( paramLI ) ;
/*
* If cursor variable was NULL , store the generated portal name in it
*/
@ -1992,8 +2017,6 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
curvar - > isnull = true ;
}
pfree ( values ) ;
pfree ( nulls ) ;
if ( curname )
pfree ( curname ) ;
@ -2599,6 +2622,11 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
PLpgSQL_function * func ,
ReturnSetInfo * rsi )
{
/* this link will be restored at exit from plpgsql_call_handler */
func - > cur_estate = estate ;
estate - > func = func ;
estate - > retval = ( Datum ) 0 ;
estate - > retisnull = true ;
estate - > rettype = InvalidOid ;
@ -2616,9 +2644,6 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
estate - > tuple_store_cxt = NULL ;
estate - > rsi = rsi ;
estate - > trig_nargs = 0 ;
estate - > trig_argv = NULL ;
estate - > found_varno = func - > found_varno ;
estate - > ndatums = func - > ndatums ;
estate - > datums = palloc ( sizeof ( PLpgSQL_datum * ) * estate - > ndatums ) ;
@ -2627,11 +2652,14 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
estate - > eval_tuptable = NULL ;
estate - > eval_processed = 0 ;
estate - > eval_lastoid = InvalidOid ;
estate - > eval_econtext = NULL ;
estate - > cur_expr = NULL ;
estate - > err_func = func ;
estate - > err_stmt = NULL ;
estate - > err_text = NULL ;
estate - > plugin_info = NULL ;
/*
* Create an EState and ExprContext for evaluation of simple expressions .
*/
@ -2682,30 +2710,20 @@ static void
exec_prepare_plan ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr , int cursorOptions )
{
int i ;
SPIPlanPtr plan ;
Oid * argtypes ;
/*
* We need a temporary argtypes array to load with data . ( The finished
* plan structure will contain a copy of it . )
* The grammar can ' t conveniently set expr - > func while building the
* parse tree , so make sure it ' s set before parser hooks need it .
*/
argtypes = ( Oid * ) palloc ( expr - > nparams * sizeof ( Oid ) ) ;
for ( i = 0 ; i < expr - > nparams ; i + + )
{
Datum paramval ;
bool paramisnull ;
exec_eval_datum ( estate , estate - > datums [ expr - > params [ i ] ] ,
InvalidOid ,
& argtypes [ i ] , & paramval , & paramisnull ) ;
}
expr - > func = estate - > func ;
/*
* Generate and save the plan
*/
plan = SPI_prepare_cursor ( expr - > query , expr - > nparams , argtypes ,
plan = SPI_prepare_params ( expr - > query ,
( ParserSetupHook ) plpgsql_parser_setup ,
( void * ) expr ,
cursorOptions ) ;
if ( plan = = NULL )
{
@ -2722,17 +2740,13 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
errmsg ( " cannot begin/end transactions in PL/pgSQL " ) ,
errhint ( " Use a BEGIN block with an EXCEPTION clause instead. " ) ) ) ;
default :
elog ( ERROR , " SPI_prepare_cursor failed for \" %s \" : %s " ,
elog ( ERROR , " SPI_prepare_params failed for \" %s \" : %s " ,
expr - > query , SPI_result_code_string ( SPI_result ) ) ;
}
}
expr - > plan = SPI_saveplan ( plan ) ;
SPI_freeplan ( plan ) ;
plan = expr - > plan ;
expr - > plan_argtypes = plan - > argtypes ;
exec_simple_check_plan ( expr ) ;
pfree ( argtypes ) ;
}
@ -2744,8 +2758,7 @@ static int
exec_stmt_execsql ( PLpgSQL_execstate * estate ,
PLpgSQL_stmt_execsql * stmt )
{
Datum * values ;
char * nulls ;
ParamListInfo paramLI ;
long tcount ;
int rc ;
PLpgSQL_expr * expr = stmt - > sqlstmt ;
@ -2782,9 +2795,10 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
}
/*
* Now build up the values and nulls arguments for SPI_execute_plan ( )
* Set up ParamListInfo ( note this is only carrying a hook function ,
* not any actual data values , at this point )
*/
eval_expr_params ( estate , expr , & values , & nulls ) ;
paramLI = setup_param_list ( estate , expr ) ;
/*
* If we have INTO , then we only need one row back . . . but if we have INTO
@ -2810,8 +2824,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
/*
* Execute the plan
*/
rc = SPI_execute_plan ( expr - > plan , values , nulls ,
estate - > readonly_func , tcount ) ;
rc = SPI_execute_plan_with_paramlist ( expr - > plan , paramLI ,
estate - > readonly_func , tcount ) ;
/*
* Check for error , and set FOUND if appropriate ( for historical reasons
@ -2852,7 +2866,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
break ;
default :
elog ( ERROR , " SPI_execute_plan failed executing query \" %s \" : %s " ,
elog ( ERROR , " SPI_execute_plan_with_paramlist failed executing query \" %s \" : %s " ,
expr - > query , SPI_result_code_string ( rc ) ) ;
}
@ -2919,8 +2933,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
( rc = = SPI_OK_SELECT ) ? errhint ( " If you want to discard the results of a SELECT, use PERFORM instead. " ) : 0 ) ) ;
}
pfree ( values ) ;
pfree ( nulls ) ;
if ( paramLI )
pfree ( paramLI ) ;
return PLPGSQL_RC_OK ;
}
@ -3142,8 +3156,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
char * curname = NULL ;
PLpgSQL_expr * query ;
Portal portal ;
Datum * values ;
char * nulls ;
ParamListInfo paramLI ;
bool isnull ;
/* ----------
@ -3280,15 +3293,17 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
}
/*
* Now build up the values and nulls arguments for SPI_execute_plan ( )
* Set up ParamListInfo ( note this is only carrying a hook function ,
* not any actual data values , at this point )
*/
eval_expr_params ( estate , query , & values , & nulls ) ;
paramLI = setup_param_list ( estate , query ) ;
/*
* Open the cursor
*/
portal = SPI_cursor_open ( curname , query - > plan , values , nulls ,
estate - > readonly_func ) ;
portal = SPI_cursor_open_with_paramlist ( curname , query - > plan ,
paramLI ,
estate - > readonly_func ) ;
if ( portal = = NULL )
elog ( ERROR , " could not open cursor: %s " ,
SPI_result_code_string ( SPI_result ) ) ;
@ -3299,10 +3314,10 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
if ( curname = = NULL )
assign_text_var ( curvar , portal - > name ) ;
pfree ( values ) ;
pfree ( nulls ) ;
if ( curname )
pfree ( curname ) ;
if ( paramLI )
pfree ( paramLI ) ;
return PLPGSQL_RC_OK ;
}
@ -3755,7 +3770,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
} while ( target - > dtype = = PLPGSQL_DTYPE_ARRAYELEM ) ;
/* Fetch current value of array datum */
exec_eval_datum ( estate , target , InvalidOid ,
exec_eval_datum ( estate , target ,
& arraytypeid , & oldarraydatum , & oldarrayisnull ) ;
arrayelemtypeid = get_element_type ( arraytypeid ) ;
@ -3860,8 +3875,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
*
* The type oid , value in Datum format , and null flag are returned .
*
* If expectedtypeid isn ' t InvalidOid , it is checked against the actual type .
*
* At present this doesn ' t handle PLpgSQL_expr or PLpgSQL_arrayelem datums .
*
* NOTE : caller must not modify the returned value , since it points right
@ -3872,7 +3885,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
static void
exec_eval_datum ( PLpgSQL_execstate * estate ,
PLpgSQL_datum * datum ,
Oid expectedtypeid ,
Oid * typeid ,
Datum * value ,
bool * isnull )
@ -3888,11 +3900,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
* typeid = var - > datatype - > typoid ;
* value = var - > value ;
* isnull = var - > isnull ;
if ( expectedtypeid ! = InvalidOid & & expectedtypeid ! = * typeid )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " type of \" %s \" does not match that when preparing the plan " ,
var - > refname ) ) ) ;
break ;
}
@ -3913,11 +3920,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
* typeid = row - > rowtupdesc - > tdtypeid ;
* value = HeapTupleGetDatum ( tup ) ;
* isnull = false ;
if ( expectedtypeid ! = InvalidOid & & expectedtypeid ! = * typeid )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " type of \" %s \" does not match that when preparing the plan " ,
row - > refname ) ) ) ;
break ;
}
@ -3950,11 +3952,6 @@ exec_eval_datum(PLpgSQL_execstate *estate,
* typeid = rec - > tupdesc - > tdtypeid ;
* value = HeapTupleGetDatum ( & worktup ) ;
* isnull = false ;
if ( expectedtypeid ! = InvalidOid & & expectedtypeid ! = * typeid )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " type of \" %s \" does not match that when preparing the plan " ,
rec - > refname ) ) ) ;
break ;
}
@ -3979,42 +3976,96 @@ exec_eval_datum(PLpgSQL_execstate *estate,
rec - > refname , recfield - > fieldname ) ) ) ;
* typeid = SPI_gettypeid ( rec - > tupdesc , fno ) ;
* value = SPI_getbinval ( rec - > tup , rec - > tupdesc , fno , isnull ) ;
if ( expectedtypeid ! = InvalidOid & & expectedtypeid ! = * typeid )
break ;
}
default :
elog ( ERROR , " unrecognized dtype: %d " , datum - > dtype ) ;
}
}
/*
* exec_get_datum_type Get datatype of a PLpgSQL_datum
*
* This is the same logic as in exec_eval_datum , except that it can handle
* some cases where exec_eval_datum has to fail ; specifically , we may have
* a tupdesc but no row value for a record variable . ( This currently can
* happen only for a trigger ' s NEW / OLD records . )
*/
static Oid
exec_get_datum_type ( PLpgSQL_execstate * estate ,
PLpgSQL_datum * datum )
{
Oid typeid ;
switch ( datum - > dtype )
{
case PLPGSQL_DTYPE_VAR :
{
PLpgSQL_var * var = ( PLpgSQL_var * ) datum ;
typeid = var - > datatype - > typoid ;
break ;
}
case PLPGSQL_DTYPE_ROW :
{
PLpgSQL_row * row = ( PLpgSQL_row * ) datum ;
if ( ! row - > rowtupdesc ) /* should not happen */
elog ( ERROR , " row variable has no tupdesc " ) ;
/* Make sure we have a valid type/typmod setting */
BlessTupleDesc ( row - > rowtupdesc ) ;
typeid = row - > rowtupdesc - > tdtypeid ;
break ;
}
case PLPGSQL_DTYPE_REC :
{
PLpgSQL_rec * rec = ( PLpgSQL_rec * ) datum ;
if ( rec - > tupdesc = = NULL )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " type of \" %s.%s \" does not match that when preparing the plan " ,
rec - > refname , recfield - > fieldname ) ) ) ;
( errcode ( ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ) ,
errmsg ( " record \" %s \" is not assigned yet " ,
rec - > refname ) ,
errdetail ( " The tuple structure of a not-yet-assigned record is indeterminate. " ) ) ) ;
/* Make sure we have a valid type/typmod setting */
BlessTupleDesc ( rec - > tupdesc ) ;
typeid = rec - > tupdesc - > tdtypeid ;
break ;
}
case PLPGSQL_DTYPE_TRIGARG :
case PLPGSQL_DTYPE_RECFIELD :
{
PLpgSQL_trigarg * trigarg = ( PLpgSQL_trigarg * ) datum ;
int tgargno ;
PLpgSQL_recfield * recfield = ( PLpgSQL_recfield * ) datum ;
PLpgSQL_rec * rec ;
int fno ;
* typeid = TEXTOID ;
tgargno = exec_eval_integer ( estate , trigarg - > argnum , isnull ) ;
if ( * isnull | | tgargno < 0 | | tgargno > = estate - > trig_nargs )
{
* value = ( Datum ) 0 ;
* isnull = true ;
}
else
{
* value = estate - > trig_argv [ tgargno ] ;
* isnull = false ;
}
if ( expectedtypeid ! = InvalidOid & & expectedtypeid ! = * typeid )
rec = ( PLpgSQL_rec * ) ( estate - > datums [ recfield - > recparentno ] ) ;
if ( rec - > tupdesc = = NULL )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " type of tg_argv[%d] does not match that when preparing the plan " ,
tgargno ) ) ) ;
( errcode ( ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ) ,
errmsg ( " record \" %s \" is not assigned yet " ,
rec - > refname ) ,
errdetail ( " The tuple structure of a not-yet-assigned record is indeterminate. " ) ) ) ;
fno = SPI_fnumber ( rec - > tupdesc , recfield - > fieldname ) ;
if ( fno = = SPI_ERROR_NOATTRIBUTE )
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_COLUMN ) ,
errmsg ( " record \" %s \" has no field \" %s \" " ,
rec - > refname , recfield - > fieldname ) ) ) ;
typeid = SPI_gettypeid ( rec - > tupdesc , fno ) ;
break ;
}
default :
elog ( ERROR , " unrecognized dtype: %d " , datum - > dtype ) ;
typeid = InvalidOid ; /* keep compiler quiet */
break ;
}
return typeid ;
}
/* ----------
@ -4145,8 +4196,7 @@ static int
exec_run_select ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr , long maxtuples , Portal * portalP )
{
Datum * values ;
char * nulls ;
ParamListInfo paramLI ;
int rc ;
/*
@ -4156,30 +4206,32 @@ exec_run_select(PLpgSQL_execstate *estate,
exec_prepare_plan ( estate , expr , 0 ) ;
/*
* Now build up the values and nulls arguments for SPI_execute_plan ( )
* Set up ParamListInfo ( note this is only carrying a hook function ,
* not any actual data values , at this point )
*/
eval_expr_params ( estate , expr , & values , & nulls ) ;
paramLI = setup_param_list ( estate , expr ) ;
/*
* If a portal was requested , put the query into the portal
*/
if ( portalP ! = NULL )
{
* portalP = SPI_cursor_open ( NULL , expr - > plan , values , nulls ,
estate - > readonly_func ) ;
* portalP = SPI_cursor_open_with_paramlist ( NULL , expr - > plan ,
paramLI ,
estate - > readonly_func ) ;
if ( * portalP = = NULL )
elog ( ERROR , " could not open implicit cursor for query \" %s \" : %s " ,
expr - > query , SPI_result_code_string ( SPI_result ) ) ;
pfree ( values ) ;
pfree ( nulls ) ;
if ( paramLI )
pfree ( paramLI ) ;
return SPI_OK_CURSOR ;
}
/*
* Execute the query
*/
rc = SPI_execute_plan ( expr - > plan , values , nulls ,
estate - > readonly_func , maxtuples ) ;
rc = SPI_execute_plan_with_paramlist ( expr - > plan , paramLI ,
estate - > readonly_func , maxtuples ) ;
if ( rc ! = SPI_OK_SELECT )
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
@ -4191,8 +4243,8 @@ exec_run_select(PLpgSQL_execstate *estate,
estate - > eval_processed = SPI_processed ;
estate - > eval_lastoid = SPI_lastoid ;
pfree ( values ) ;
pfree ( nulls ) ;
if ( paramLI )
pfree ( paramLI ) ;
return rc ;
}
@ -4378,7 +4430,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
CachedPlanSource * plansource ;
CachedPlan * cplan ;
ParamListInfo paramLI ;
int i ;
PLpgSQL_expr * save_cur_expr ;
MemoryContext oldcontext ;
/*
@ -4425,42 +4477,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
expr - > expr_simple_lxid = curlxid ;
}
/*
* Param list can live in econtext ' s temporary memory context .
*
* XXX think about avoiding repeated palloc ' s for param lists ? Beware
* however that this routine is re - entrant : exec_eval_datum ( ) can call it
* back for subscript evaluation , and so there can be a need to have more
* than one active param list .
*/
if ( expr - > nparams > 0 )
{
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = ( ParamListInfo )
MemoryContextAlloc ( econtext - > ecxt_per_tuple_memory ,
sizeof ( ParamListInfoData ) +
( expr - > nparams - 1 ) * sizeof ( ParamExternData ) ) ;
paramLI - > numParams = expr - > nparams ;
for ( i = 0 ; i < expr - > nparams ; i + + )
{
ParamExternData * prm = & paramLI - > params [ i ] ;
PLpgSQL_datum * datum = estate - > datums [ expr - > params [ i ] ] ;
prm - > pflags = 0 ;
exec_eval_datum ( estate , datum , expr - > plan_argtypes [ i ] ,
& prm - > ptype ,
& prm - > value , & prm - > isnull ) ;
}
}
else
paramLI = NULL ;
/*
* Now we can safely make the econtext point to the param list .
*/
econtext - > ecxt_param_list_info = paramLI ;
/*
* We have to do some of the things SPI_execute_plan would do , in
* particular advance the snapshot if we are in a non - read - only function .
@ -4476,6 +4492,22 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
PushActiveSnapshot ( GetTransactionSnapshot ( ) ) ;
}
/*
* Create the param list in econtext ' s temporary memory context .
* We won ' t need to free it explicitly , since it will go away at the
* next reset of that context .
*
* XXX think about avoiding repeated palloc ' s for param lists ? It should
* be possible - - - this routine isn ' t re - entrant anymore .
*
* Just for paranoia ' s sake , save and restore the prior value of
* estate - > cur_expr , which setup_param_list ( ) sets .
*/
save_cur_expr = estate - > cur_expr ;
paramLI = setup_param_list ( estate , expr ) ;
econtext - > ecxt_param_list_info = paramLI ;
/*
* Finally we can call the executor to evaluate the expression
*/
@ -4483,11 +4515,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
econtext ,
isNull ,
NULL ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/* Assorted cleanup */
estate - > cur_expr = save_cur_expr ;
if ( ! estate - > readonly_func )
PopActiveSnapshot ( ) ;
MemoryContextSwitchTo ( oldcontext ) ;
SPI_pop ( ) ;
/*
@ -4503,32 +4539,136 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
/*
* Build up the values and nulls arguments for SPI_execute_plan ( )
* Create a ParamListInfo to pass to SPI
*
* The ParamListInfo array is initially all zeroes , in particular the
* ptype values are all InvalidOid . This causes the executor to call the
* paramFetch hook each time it wants a value . We thus evaluate only the
* parameters actually demanded .
*
* The result is a locally palloc ' d array that should be pfree ' d after use ;
* but note it can be NULL .
*/
static void
eval_expr_params ( PLpgSQL_execstate * estate ,
PLpgSQL_expr * expr , Datum * * p_values , char * * p_nulls )
static ParamListInfo
setup_param_list ( PLpgSQL_execstate * estate , PLpgSQL_expr * expr )
{
Datum * values ;
char * nulls ;
int i ;
* p_values = values = ( Datum * ) palloc ( expr - > nparams * sizeof ( Datum ) ) ;
* p_nulls = nulls = ( char * ) palloc ( expr - > nparams * sizeof ( char ) ) ;
ParamListInfo paramLI ;
for ( i = 0 ; i < expr - > nparams ; i + + )
/*
* Could we re - use these arrays instead of palloc ' ing a new one each
* time ? However , we ' d have to zero the array each time anyway ,
* since new values might have been assigned to the variables .
*/
if ( estate - > ndatums > 0 )
{
PLpgSQL_datum * datum = estate - > datums [ expr - > params [ i ] ] ;
Oid paramtypeid ;
bool paramisnull ;
exec_eval_datum ( estate , datum , expr - > plan_argtypes [ i ] ,
& paramtypeid , & values [ i ] , & paramisnull ) ;
if ( paramisnull )
nulls [ i ] = ' n ' ;
else
nulls [ i ] = ' ' ;
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = ( ParamListInfo )
palloc0 ( sizeof ( ParamListInfoData ) +
( estate - > ndatums - 1 ) * sizeof ( ParamExternData ) ) ;
paramLI - > paramFetch = plpgsql_param_fetch ;
paramLI - > paramFetchArg = ( void * ) estate ;
paramLI - > parserSetup = ( ParserSetupHook ) plpgsql_parser_setup ;
paramLI - > parserSetupArg = ( void * ) expr ;
paramLI - > numParams = estate - > ndatums ;
/*
* Set up link to active expr where the hook functions can find it .
* Callers must save and restore cur_expr if there is any chance
* that they are interrupting an active use of parameters .
*/
estate - > cur_expr = expr ;
/*
* Also make sure this is set before parser hooks need it . There
* is no need to save and restore , since the value is always correct
* once set .
*/
expr - > func = estate - > func ;
}
else
paramLI = NULL ;
return paramLI ;
}
/*
* plpgsql_parser_setup set up parser hooks for dynamic parameters
*/
static void
plpgsql_parser_setup ( ParseState * pstate , PLpgSQL_expr * expr )
{
pstate - > p_ref_hook_state = ( void * ) expr ;
pstate - > p_paramref_hook = plpgsql_param_ref ;
/* no need to use p_coerce_param_hook */
}
/*
* plpgsql_param_ref parser callback for ParamRefs ( $ n symbols )
*/
static Node *
plpgsql_param_ref ( ParseState * pstate , ParamRef * pref )
{
int paramno = pref - > number ;
PLpgSQL_expr * expr = ( PLpgSQL_expr * ) pstate - > p_ref_hook_state ;
PLpgSQL_execstate * estate ;
Param * param ;
/* Let's just check parameter number is in range */
if ( ! bms_is_member ( paramno - 1 , expr - > paramnos ) )
return NULL ;
/*
* We use the function ' s current estate to resolve parameter data types .
* This is really pretty bogus because there is no provision for updating
* plans when those types change . . .
*/
estate = expr - > func - > cur_estate ;
Assert ( paramno < = estate - > ndatums ) ;
param = makeNode ( Param ) ;
param - > paramkind = PARAM_EXTERN ;
param - > paramid = paramno ;
param - > paramtype = exec_get_datum_type ( estate ,
estate - > datums [ paramno - 1 ] ) ;
param - > paramtypmod = - 1 ;
param - > location = pref - > location ;
return ( Node * ) param ;
}
/*
* plpgsql_param_fetch paramFetch callback for dynamic parameter fetch
*/
static void
plpgsql_param_fetch ( ParamListInfo params , int paramid )
{
int dno ;
PLpgSQL_execstate * estate ;
PLpgSQL_expr * expr ;
PLpgSQL_datum * datum ;
ParamExternData * prm ;
/* paramid's are 1-based, but dnos are 0-based */
dno = paramid - 1 ;
Assert ( dno > = 0 & & dno < params - > numParams ) ;
/* fetch back the hook data */
estate = ( PLpgSQL_execstate * ) params - > paramFetchArg ;
expr = estate - > cur_expr ;
Assert ( params - > numParams = = estate - > ndatums ) ;
/*
* Do nothing if asked for a value that ' s not supposed to be used by
* this SQL expression . This avoids unwanted evaluations when functions
* such as copyParamList try to materialize all the values .
*/
if ( ! bms_is_member ( dno , expr - > paramnos ) )
return ;
/* OK, evaluate the value and store into the appropriate paramlist slot */
datum = estate - > datums [ dno ] ;
prm = & params - > params [ dno ] ;
exec_eval_datum ( estate , datum ,
& prm - > ptype , & prm - > value , & prm - > isnull ) ;
}
@ -4710,7 +4850,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
elog ( ERROR , " dropped rowtype entry for non-dropped column " ) ;
exec_eval_datum ( estate , estate - > datums [ row - > varnos [ i ] ] ,
InvalidOid , & fieldtypeid , & dvalues [ i ] , & nulls [ i ] ) ;
& fieldtypeid , & dvalues [ i ] , & nulls [ i ] ) ;
if ( fieldtypeid ! = tupdesc - > attrs [ i ] - > atttypid )
return NULL ;
}