@ -221,6 +221,11 @@ static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
PLpgSQL_expr * dynquery , List * params ,
const char * portalname , int cursorOptions ) ;
static char * format_expr_params ( PLpgSQL_execstate * estate ,
const PLpgSQL_expr * expr ) ;
static char * format_preparedparamsdata ( PLpgSQL_execstate * estate ,
const PreparedParamsData * ppd ) ;
/* ----------
* plpgsql_exec_function Called by the call handler for
@ -3391,18 +3396,40 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
if ( n = = 0 )
{
if ( stmt - > strict )
{
char * errdetail ;
if ( estate - > func - > print_strict_params )
errdetail = format_expr_params ( estate , expr ) ;
else
errdetail = NULL ;
ereport ( ERROR ,
( errcode ( ERRCODE_NO_DATA_FOUND ) ,
errmsg ( " query returned no rows " ) ) ) ;
errmsg ( " query returned no rows " ) ,
errdetail ?
errdetail_internal ( " parameters: %s " , errdetail ) : 0 ) ) ;
}
/* set the target to NULL(s) */
exec_move_row ( estate , rec , row , NULL , tuptab - > tupdesc ) ;
}
else
{
if ( n > 1 & & ( stmt - > strict | | stmt - > mod_stmt ) )
{
char * errdetail ;
if ( estate - > func - > print_strict_params )
errdetail = format_expr_params ( estate , expr ) ;
else
errdetail = NULL ;
ereport ( ERROR ,
( errcode ( ERRCODE_TOO_MANY_ROWS ) ,
errmsg ( " query returned more than one row " ) ) ) ;
errmsg ( " query returned more than one row " ) ,
errdetail ?
errdetail_internal ( " parameters: %s " , errdetail ) : 0 ) ) ;
}
/* Put the first result row into the target */
exec_move_row ( estate , rec , row , tuptab - > vals [ 0 ] , tuptab - > tupdesc ) ;
}
@ -3442,6 +3469,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
Oid restype ;
char * querystr ;
int exec_res ;
PreparedParamsData * ppd = NULL ;
/*
* First we evaluate the string expression after the EXECUTE keyword . Its
@ -3466,14 +3494,11 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
*/
if ( stmt - > params )
{
PreparedParamsData * ppd ;
ppd = exec_eval_using_params ( estate , stmt - > params ) ;
exec_res = SPI_execute_with_args ( querystr ,
ppd - > nargs , ppd - > types ,
ppd - > values , ppd - > nulls ,
estate - > readonly_func , 0 ) ;
free_params_data ( ppd ) ;
}
else
exec_res = SPI_execute ( querystr , estate - > readonly_func , 0 ) ;
@ -3565,18 +3590,41 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
if ( n = = 0 )
{
if ( stmt - > strict )
{
char * errdetail ;
if ( estate - > func - > print_strict_params )
errdetail = format_preparedparamsdata ( estate , ppd ) ;
else
errdetail = NULL ;
ereport ( ERROR ,
( errcode ( ERRCODE_NO_DATA_FOUND ) ,
errmsg ( " query returned no rows " ) ) ) ;
errmsg ( " query returned no rows " ) ,
errdetail ?
errdetail_internal ( " parameters: %s " , errdetail ) : 0 ) ) ;
}
/* set the target to NULL(s) */
exec_move_row ( estate , rec , row , NULL , tuptab - > tupdesc ) ;
}
else
{
if ( n > 1 & & stmt - > strict )
{
char * errdetail ;
if ( estate - > func - > print_strict_params )
errdetail = format_preparedparamsdata ( estate , ppd ) ;
else
errdetail = NULL ;
ereport ( ERROR ,
( errcode ( ERRCODE_TOO_MANY_ROWS ) ,
errmsg ( " query returned more than one row " ) ) ) ;
errmsg ( " query returned more than one row " ) ,
errdetail ?
errdetail_internal ( " parameters: %s " , errdetail ) : 0 ) ) ;
}
/* Put the first result row into the target */
exec_move_row ( estate , rec , row , tuptab - > vals [ 0 ] , tuptab - > tupdesc ) ;
}
@ -3592,6 +3640,9 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
*/
}
if ( ppd )
free_params_data ( ppd ) ;
/* Release any result from SPI_execute, as well as the querystring */
SPI_freetuptable ( SPI_tuptable ) ;
pfree ( querystr ) ;
@ -6456,3 +6507,103 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
return portal ;
}
/*
* Return a formatted string with information about an expression ' s parameters ,
* or NULL if the expression does not take any parameters .
*/
static char *
format_expr_params ( PLpgSQL_execstate * estate ,
const PLpgSQL_expr * expr )
{
int paramno ;
int dno ;
StringInfoData paramstr ;
Bitmapset * tmpset ;
if ( ! expr - > paramnos )
return NULL ;
initStringInfo ( & paramstr ) ;
tmpset = bms_copy ( expr - > paramnos ) ;
paramno = 0 ;
while ( ( dno = bms_first_member ( tmpset ) ) > = 0 )
{
Datum paramdatum ;
Oid paramtypeid ;
bool paramisnull ;
int32 paramtypmod ;
PLpgSQL_var * curvar ;
curvar = ( PLpgSQL_var * ) estate - > datums [ dno ] ;
exec_eval_datum ( estate , ( PLpgSQL_datum * ) curvar , & paramtypeid ,
& paramtypmod , & paramdatum , & paramisnull ) ;
appendStringInfo ( & paramstr , " %s%s = " ,
paramno > 0 ? " , " : " " ,
curvar - > refname ) ;
if ( paramisnull )
appendStringInfoString ( & paramstr , " NULL " ) ;
else
{
char * value = convert_value_to_string ( estate , paramdatum , paramtypeid ) ;
char * p ;
appendStringInfoCharMacro ( & paramstr , ' \' ' ) ;
for ( p = value ; * p ; p + + )
{
if ( * p = = ' \' ' ) /* double single quotes */
appendStringInfoCharMacro ( & paramstr , * p ) ;
appendStringInfoCharMacro ( & paramstr , * p ) ;
}
appendStringInfoCharMacro ( & paramstr , ' \' ' ) ;
}
paramno + + ;
}
bms_free ( tmpset ) ;
return paramstr . data ;
}
/*
* Return a formatted string with information about PreparedParamsData , or NULL
* if the there are no parameters .
*/
static char *
format_preparedparamsdata ( PLpgSQL_execstate * estate ,
const PreparedParamsData * ppd )
{
int paramno ;
StringInfoData paramstr ;
if ( ! ppd )
return NULL ;
initStringInfo ( & paramstr ) ;
for ( paramno = 0 ; paramno < ppd - > nargs ; paramno + + )
{
appendStringInfo ( & paramstr , " %s$%d = " ,
paramno > 0 ? " , " : " " ,
paramno + 1 ) ;
if ( ppd - > nulls [ paramno ] = = ' n ' )
appendStringInfoString ( & paramstr , " NULL " ) ;
else
{
char * value = convert_value_to_string ( estate , ppd - > values [ paramno ] , ppd - > types [ paramno ] ) ;
char * p ;
appendStringInfoCharMacro ( & paramstr , ' \' ' ) ;
for ( p = value ; * p ; p + + )
{
if ( * p = = ' \' ' ) /* double single quotes */
appendStringInfoCharMacro ( & paramstr , * p ) ;
appendStringInfoCharMacro ( & paramstr , * p ) ;
}
appendStringInfoCharMacro ( & paramstr , ' \' ' ) ;
}
}
return paramstr . data ;
}