@@ -221,6 +221,11 @@ static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
221
221
PLpgSQL_expr * dynquery , List * params ,
222
222
const char * portalname , int cursorOptions );
223
223
224
+ static char * format_expr_params (PLpgSQL_execstate * estate ,
225
+ const PLpgSQL_expr * expr );
226
+ static char * format_preparedparamsdata (PLpgSQL_execstate * estate ,
227
+ const PreparedParamsData * ppd );
228
+
224
229
225
230
/* ----------
226
231
* plpgsql_exec_function Called by the call handler for
@@ -3391,18 +3396,40 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
3391
3396
if (n == 0 )
3392
3397
{
3393
3398
if (stmt -> strict )
3399
+ {
3400
+ char * errdetail ;
3401
+
3402
+ if (estate -> func -> print_strict_params )
3403
+ errdetail = format_expr_params (estate , expr );
3404
+ else
3405
+ errdetail = NULL ;
3406
+
3394
3407
ereport (ERROR ,
3395
3408
(errcode (ERRCODE_NO_DATA_FOUND ),
3396
- errmsg ("query returned no rows" )));
3409
+ errmsg ("query returned no rows" ),
3410
+ errdetail ?
3411
+ errdetail_internal ("parameters: %s" , errdetail ) : 0 ));
3412
+ }
3397
3413
/* set the target to NULL(s) */
3398
3414
exec_move_row (estate , rec , row , NULL , tuptab -> tupdesc );
3399
3415
}
3400
3416
else
3401
3417
{
3402
3418
if (n > 1 && (stmt -> strict || stmt -> mod_stmt ))
3419
+ {
3420
+ char * errdetail ;
3421
+
3422
+ if (estate -> func -> print_strict_params )
3423
+ errdetail = format_expr_params (estate , expr );
3424
+ else
3425
+ errdetail = NULL ;
3426
+
3403
3427
ereport (ERROR ,
3404
3428
(errcode (ERRCODE_TOO_MANY_ROWS ),
3405
- errmsg ("query returned more than one row" )));
3429
+ errmsg ("query returned more than one row" ),
3430
+ errdetail ?
3431
+ errdetail_internal ("parameters: %s" , errdetail ) : 0 ));
3432
+ }
3406
3433
/* Put the first result row into the target */
3407
3434
exec_move_row (estate , rec , row , tuptab -> vals [0 ], tuptab -> tupdesc );
3408
3435
}
@@ -3442,6 +3469,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
3442
3469
Oid restype ;
3443
3470
char * querystr ;
3444
3471
int exec_res ;
3472
+ PreparedParamsData * ppd = NULL ;
3445
3473
3446
3474
/*
3447
3475
* First we evaluate the string expression after the EXECUTE keyword. Its
@@ -3466,14 +3494,11 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
3466
3494
*/
3467
3495
if (stmt -> params )
3468
3496
{
3469
- PreparedParamsData * ppd ;
3470
-
3471
3497
ppd = exec_eval_using_params (estate , stmt -> params );
3472
3498
exec_res = SPI_execute_with_args (querystr ,
3473
3499
ppd -> nargs , ppd -> types ,
3474
3500
ppd -> values , ppd -> nulls ,
3475
3501
estate -> readonly_func , 0 );
3476
- free_params_data (ppd );
3477
3502
}
3478
3503
else
3479
3504
exec_res = SPI_execute (querystr , estate -> readonly_func , 0 );
@@ -3565,18 +3590,41 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
3565
3590
if (n == 0 )
3566
3591
{
3567
3592
if (stmt -> strict )
3593
+ {
3594
+ char * errdetail ;
3595
+
3596
+ if (estate -> func -> print_strict_params )
3597
+ errdetail = format_preparedparamsdata (estate , ppd );
3598
+ else
3599
+ errdetail = NULL ;
3600
+
3568
3601
ereport (ERROR ,
3569
3602
(errcode (ERRCODE_NO_DATA_FOUND ),
3570
- errmsg ("query returned no rows" )));
3603
+ errmsg ("query returned no rows" ),
3604
+ errdetail ?
3605
+ errdetail_internal ("parameters: %s" , errdetail ) : 0 ));
3606
+ }
3571
3607
/* set the target to NULL(s) */
3572
3608
exec_move_row (estate , rec , row , NULL , tuptab -> tupdesc );
3573
3609
}
3574
3610
else
3575
3611
{
3576
3612
if (n > 1 && stmt -> strict )
3613
+ {
3614
+ char * errdetail ;
3615
+
3616
+ if (estate -> func -> print_strict_params )
3617
+ errdetail = format_preparedparamsdata (estate , ppd );
3618
+ else
3619
+ errdetail = NULL ;
3620
+
3577
3621
ereport (ERROR ,
3578
3622
(errcode (ERRCODE_TOO_MANY_ROWS ),
3579
- errmsg ("query returned more than one row" )));
3623
+ errmsg ("query returned more than one row" ),
3624
+ errdetail ?
3625
+ errdetail_internal ("parameters: %s" , errdetail ) : 0 ));
3626
+ }
3627
+
3580
3628
/* Put the first result row into the target */
3581
3629
exec_move_row (estate , rec , row , tuptab -> vals [0 ], tuptab -> tupdesc );
3582
3630
}
@@ -3592,6 +3640,9 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
3592
3640
*/
3593
3641
}
3594
3642
3643
+ if (ppd )
3644
+ free_params_data (ppd );
3645
+
3595
3646
/* Release any result from SPI_execute, as well as the querystring */
3596
3647
SPI_freetuptable (SPI_tuptable );
3597
3648
pfree (querystr );
@@ -6456,3 +6507,103 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate,
6456
6507
6457
6508
return portal ;
6458
6509
}
6510
+
6511
+ /*
6512
+ * Return a formatted string with information about an expression's parameters,
6513
+ * or NULL if the expression does not take any parameters.
6514
+ */
6515
+ static char *
6516
+ format_expr_params (PLpgSQL_execstate * estate ,
6517
+ const PLpgSQL_expr * expr )
6518
+ {
6519
+ int paramno ;
6520
+ int dno ;
6521
+ StringInfoData paramstr ;
6522
+ Bitmapset * tmpset ;
6523
+
6524
+ if (!expr -> paramnos )
6525
+ return NULL ;
6526
+
6527
+ initStringInfo (& paramstr );
6528
+ tmpset = bms_copy (expr -> paramnos );
6529
+ paramno = 0 ;
6530
+ while ((dno = bms_first_member (tmpset )) >= 0 )
6531
+ {
6532
+ Datum paramdatum ;
6533
+ Oid paramtypeid ;
6534
+ bool paramisnull ;
6535
+ int32 paramtypmod ;
6536
+ PLpgSQL_var * curvar ;
6537
+
6538
+ curvar = (PLpgSQL_var * ) estate -> datums [dno ];
6539
+
6540
+ exec_eval_datum (estate , (PLpgSQL_datum * ) curvar , & paramtypeid ,
6541
+ & paramtypmod , & paramdatum , & paramisnull );
6542
+
6543
+ appendStringInfo (& paramstr , "%s%s = " ,
6544
+ paramno > 0 ? ", " : "" ,
6545
+ curvar -> refname );
6546
+
6547
+ if (paramisnull )
6548
+ appendStringInfoString (& paramstr , "NULL" );
6549
+ else
6550
+ {
6551
+ char * value = convert_value_to_string (estate , paramdatum , paramtypeid );
6552
+ char * p ;
6553
+ appendStringInfoCharMacro (& paramstr , '\'' );
6554
+ for (p = value ; * p ; p ++ )
6555
+ {
6556
+ if (* p == '\'' ) /* double single quotes */
6557
+ appendStringInfoCharMacro (& paramstr , * p );
6558
+ appendStringInfoCharMacro (& paramstr , * p );
6559
+ }
6560
+ appendStringInfoCharMacro (& paramstr , '\'' );
6561
+ }
6562
+
6563
+ paramno ++ ;
6564
+ }
6565
+ bms_free (tmpset );
6566
+
6567
+ return paramstr .data ;
6568
+ }
6569
+
6570
+ /*
6571
+ * Return a formatted string with information about PreparedParamsData, or NULL
6572
+ * if the there are no parameters.
6573
+ */
6574
+ static char *
6575
+ format_preparedparamsdata (PLpgSQL_execstate * estate ,
6576
+ const PreparedParamsData * ppd )
6577
+ {
6578
+ int paramno ;
6579
+ StringInfoData paramstr ;
6580
+
6581
+ if (!ppd )
6582
+ return NULL ;
6583
+
6584
+ initStringInfo (& paramstr );
6585
+ for (paramno = 0 ; paramno < ppd -> nargs ; paramno ++ )
6586
+ {
6587
+ appendStringInfo (& paramstr , "%s$%d = " ,
6588
+ paramno > 0 ? ", " : "" ,
6589
+ paramno + 1 );
6590
+
6591
+ if (ppd -> nulls [paramno ] == 'n' )
6592
+ appendStringInfoString (& paramstr , "NULL" );
6593
+ else
6594
+ {
6595
+ char * value = convert_value_to_string (estate , ppd -> values [paramno ], ppd -> types [paramno ]);
6596
+ char * p ;
6597
+ appendStringInfoCharMacro (& paramstr , '\'' );
6598
+ for (p = value ; * p ; p ++ )
6599
+ {
6600
+ if (* p == '\'' ) /* double single quotes */
6601
+ appendStringInfoCharMacro (& paramstr , * p );
6602
+ appendStringInfoCharMacro (& paramstr , * p );
6603
+ }
6604
+ appendStringInfoCharMacro (& paramstr , '\'' );
6605
+ }
6606
+ }
6607
+
6608
+ return paramstr .data ;
6609
+ }
0 commit comments