@@ -102,7 +102,18 @@ int max_stack_depth = 100;
102102/* wait N seconds to allow attach from a debugger */
103103int PostAuthDelay = 0 ;
104104
105+ /* ----------------
106+ * private typedefs etc
107+ * ----------------
108+ */
105109
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 ;
106117
107118/* ----------------
108119 * private variables
@@ -183,6 +194,7 @@ static int errdetail_execute(List *raw_parsetree_list);
183194static int errdetail_params (ParamListInfo params );
184195static int errdetail_abort (void );
185196static int errdetail_recovery_conflict (void );
197+ static void bind_param_error_callback (void * arg );
186198static void start_xact_command (void );
187199static void finish_xact_command (void );
188200static bool IsTransactionExitStmt (Node * parsetree );
@@ -1698,6 +1710,19 @@ exec_bind_message(StringInfo input_message)
16981710 if (numParams > 0 )
16991711 {
17001712 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 ;
17011726
17021727 params = makeParamList (numParams );
17031728
@@ -1711,6 +1736,9 @@ exec_bind_message(StringInfo input_message)
17111736 char csave ;
17121737 int16 pformat ;
17131738
1739+ one_param_data .paramno = paramno ;
1740+ one_param_data .paramval = NULL ;
1741+
17141742 plength = pq_getmsgint (input_message , 4 );
17151743 isNull = (plength == -1 );
17161744
@@ -1764,8 +1792,13 @@ exec_bind_message(StringInfo input_message)
17641792 else
17651793 pstring = pg_client_to_server (pbuf .data , plength );
17661794
1795+ /* Now we can log the input string in case of error */
1796+ one_param_data .paramval = pstring ;
1797+
17671798 pval = OidInputFunctionCall (typinput , pstring , typioparam , -1 );
17681799
1800+ one_param_data .paramval = NULL ;
1801+
17691802 /*
17701803 * If we might need to log parameters later, save a copy of
17711804 * the converted string in MessageContext; then free the
@@ -1855,10 +1888,13 @@ exec_bind_message(StringInfo input_message)
18551888 params -> params [paramno ].ptype = ptype ;
18561889 }
18571890
1891+ /* Pop the per-parameter error callback */
1892+ error_context_stack = error_context_stack -> previous ;
1893+
18581894 /*
18591895 * 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.)
18621898 */
18631899 if (log_parameter_max_length_on_error != 0 )
18641900 params -> paramValuesStr =
@@ -1872,7 +1908,10 @@ exec_bind_message(StringInfo input_message)
18721908 /* Done storing stuff in portal's context */
18731909 MemoryContextSwitchTo (oldContext );
18741910
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+ */
18761915 params_data .portalName = portal -> name ;
18771916 params_data .params = params ;
18781917 params_errcxt .previous = error_context_stack ;
@@ -2413,6 +2452,55 @@ errdetail_recovery_conflict(void)
24132452 return 0 ;
24142453}
24152454
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+
24162504/*
24172505 * exec_describe_statement_message
24182506 *
0 commit comments