Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1ea3963

Browse files
committed
Improve logging of bad parameter values in BIND messages.
Since commit ba79cb5, values of bind parameters have been logged during errors in extended query mode. However, we only did that after we'd collected and converted all the parameter values, thus failing to offer any useful localization of invalid-parameter problems. Add a separate callback that's used during parameter collection, and have it print the parameter number, along with the input string if text input format is used. Justin Pryzby and Tom Lane Discussion: https://postgr.es/m/20210104170939.GH9712@telsasoft.com Discussion: https://postgr.es/m/CANfkH5k-6nNt-4cSv1vPB80nq2BZCzhFVR5O4VznYbsX0wZmow@mail.gmail.com
1 parent 0150616 commit 1ea3963

File tree

2 files changed

+107
-3
lines changed

2 files changed

+107
-3
lines changed

src/backend/tcop/postgres.c

+91-3
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,18 @@ int max_stack_depth = 100;
102102
/* wait N seconds to allow attach from a debugger */
103103
int 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);
183194
static int errdetail_params(ParamListInfo params);
184195
static int errdetail_abort(void);
185196
static int errdetail_recovery_conflict(void);
197+
static void bind_param_error_callback(void *arg);
186198
static void start_xact_command(void);
187199
static void finish_xact_command(void);
188200
static 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
*

src/bin/pgbench/t/001_pgbench_with_server.pl

+16
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,22 @@ sub pgbench
389389
"parameter report truncates");
390390
$log = undef;
391391

392+
# Check that bad parameters are reported during typinput phase of BIND
393+
pgbench(
394+
'-n -t1 -c1 -M prepared',
395+
2,
396+
[],
397+
[
398+
qr{ERROR: invalid input syntax for type smallint: "1a"},
399+
qr{CONTEXT: unnamed portal parameter \$2 = '1a'}
400+
],
401+
'server parameter logging',
402+
{
403+
'001_param_6' => q{select 42 as value1, '1a' as value2 \gset
404+
select :value1::smallint, :value2::smallint;
405+
}
406+
});
407+
392408
# Restore default logging config
393409
$node->append_conf('postgresql.conf',
394410
"log_min_duration_statement = -1\n"

0 commit comments

Comments
 (0)