@@ -102,7 +102,18 @@ int max_stack_depth = 100;
102
102
/* wait N seconds to allow attach from a debugger */
103
103
int PostAuthDelay = 0 ;
104
104
105
+ /* ----------------
106
+ * private typedefs etc
107
+ * ----------------
108
+ */
105
109
110
+ /* type of argument for bind_param_error_callback */
111
+ typedef struct BindParamCbData
112
+ {
113
+ const char * portalName ;
114
+ int paramno ; /* zero-based param number, or -1 initially */
115
+ const char * paramval ; /* textual input string, if available */
116
+ } BindParamCbData ;
106
117
107
118
/* ----------------
108
119
* private variables
@@ -183,6 +194,7 @@ static int errdetail_execute(List *raw_parsetree_list);
183
194
static int errdetail_params (ParamListInfo params );
184
195
static int errdetail_abort (void );
185
196
static int errdetail_recovery_conflict (void );
197
+ static void bind_param_error_callback (void * arg );
186
198
static void start_xact_command (void );
187
199
static void finish_xact_command (void );
188
200
static bool IsTransactionExitStmt (Node * parsetree );
@@ -1698,6 +1710,19 @@ exec_bind_message(StringInfo input_message)
1698
1710
if (numParams > 0 )
1699
1711
{
1700
1712
char * * knownTextValues = NULL ; /* allocate on first use */
1713
+ BindParamCbData one_param_data ;
1714
+
1715
+ /*
1716
+ * Set up an error callback so that if there's an error in this phase,
1717
+ * we can report the specific parameter causing the problem.
1718
+ */
1719
+ one_param_data .portalName = portal -> name ;
1720
+ one_param_data .paramno = -1 ;
1721
+ one_param_data .paramval = NULL ;
1722
+ params_errcxt .previous = error_context_stack ;
1723
+ params_errcxt .callback = bind_param_error_callback ;
1724
+ params_errcxt .arg = (void * ) & one_param_data ;
1725
+ error_context_stack = & params_errcxt ;
1701
1726
1702
1727
params = makeParamList (numParams );
1703
1728
@@ -1711,6 +1736,9 @@ exec_bind_message(StringInfo input_message)
1711
1736
char csave ;
1712
1737
int16 pformat ;
1713
1738
1739
+ one_param_data .paramno = paramno ;
1740
+ one_param_data .paramval = NULL ;
1741
+
1714
1742
plength = pq_getmsgint (input_message , 4 );
1715
1743
isNull = (plength == -1 );
1716
1744
@@ -1764,8 +1792,13 @@ exec_bind_message(StringInfo input_message)
1764
1792
else
1765
1793
pstring = pg_client_to_server (pbuf .data , plength );
1766
1794
1795
+ /* Now we can log the input string in case of error */
1796
+ one_param_data .paramval = pstring ;
1797
+
1767
1798
pval = OidInputFunctionCall (typinput , pstring , typioparam , -1 );
1768
1799
1800
+ one_param_data .paramval = NULL ;
1801
+
1769
1802
/*
1770
1803
* If we might need to log parameters later, save a copy of
1771
1804
* the converted string in MessageContext; then free the
@@ -1855,10 +1888,13 @@ exec_bind_message(StringInfo input_message)
1855
1888
params -> params [paramno ].ptype = ptype ;
1856
1889
}
1857
1890
1891
+ /* Pop the per-parameter error callback */
1892
+ error_context_stack = error_context_stack -> previous ;
1893
+
1858
1894
/*
1859
1895
* Once all parameters have been received, prepare for printing them
1860
- * in errors, if configured to do so. (This is saved in the portal,
1861
- * so that they'll appear when the query is executed later.)
1896
+ * in future errors, if configured to do so. (This is saved in the
1897
+ * portal, so that they'll appear when the query is executed later.)
1862
1898
*/
1863
1899
if (log_parameter_max_length_on_error != 0 )
1864
1900
params -> paramValuesStr =
@@ -1872,7 +1908,10 @@ exec_bind_message(StringInfo input_message)
1872
1908
/* Done storing stuff in portal's context */
1873
1909
MemoryContextSwitchTo (oldContext );
1874
1910
1875
- /* Set the error callback so that parameters are logged, as needed */
1911
+ /*
1912
+ * Set up another error callback so that all the parameters are logged if
1913
+ * we get an error during the rest of the BIND processing.
1914
+ */
1876
1915
params_data .portalName = portal -> name ;
1877
1916
params_data .params = params ;
1878
1917
params_errcxt .previous = error_context_stack ;
@@ -2413,6 +2452,55 @@ errdetail_recovery_conflict(void)
2413
2452
return 0 ;
2414
2453
}
2415
2454
2455
+ /*
2456
+ * bind_param_error_callback
2457
+ *
2458
+ * Error context callback used while parsing parameters in a Bind message
2459
+ */
2460
+ static void
2461
+ bind_param_error_callback (void * arg )
2462
+ {
2463
+ BindParamCbData * data = (BindParamCbData * ) arg ;
2464
+ StringInfoData buf ;
2465
+ char * quotedval ;
2466
+
2467
+ if (data -> paramno < 0 )
2468
+ return ;
2469
+
2470
+ /* If we have a textual value, quote it, and trim if necessary */
2471
+ if (data -> paramval )
2472
+ {
2473
+ initStringInfo (& buf );
2474
+ appendStringInfoStringQuoted (& buf , data -> paramval ,
2475
+ log_parameter_max_length_on_error );
2476
+ quotedval = buf .data ;
2477
+ }
2478
+ else
2479
+ quotedval = NULL ;
2480
+
2481
+ if (data -> portalName && data -> portalName [0 ] != '\0' )
2482
+ {
2483
+ if (quotedval )
2484
+ errcontext ("portal \"%s\" parameter $%d = %s" ,
2485
+ data -> portalName , data -> paramno + 1 , quotedval );
2486
+ else
2487
+ errcontext ("portal \"%s\" parameter $%d" ,
2488
+ data -> portalName , data -> paramno + 1 );
2489
+ }
2490
+ else
2491
+ {
2492
+ if (quotedval )
2493
+ errcontext ("unnamed portal parameter $%d = %s" ,
2494
+ data -> paramno + 1 , quotedval );
2495
+ else
2496
+ errcontext ("unnamed portal parameter $%d" ,
2497
+ data -> paramno + 1 );
2498
+ }
2499
+
2500
+ if (quotedval )
2501
+ pfree (quotedval );
2502
+ }
2503
+
2416
2504
/*
2417
2505
* exec_describe_statement_message
2418
2506
*
0 commit comments