Commit 5b75a4f8 authored by Peter Eisentraut's avatar Peter Eisentraut

pgbench: Report errors during run better

When an error occurs during a benchmark run, exit with a nonzero exit
code and write a message at the end.  Previously, it would just print
the error message when it happened but then proceed to print the run
summary normally and exit with status 0.  To still allow
distinguishing setup from run-time errors, we use exit status 2 for
the new state, whereas existing errors during pgbench initialization
use exit status 1.
Reviewed-by: default avatarFabien COELHO <coelho@cri.ensmp.fr>
parent 35584fd0
...@@ -804,6 +804,18 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d ...@@ -804,6 +804,18 @@ pgbench <optional> <replaceable>options</replaceable> </optional> <replaceable>d
</refsect2> </refsect2>
</refsect1> </refsect1>
<refsect1>
<title>Exit Status</title>
<para>
A successful run will exit with status 0. Exit status 1 indicates static
problems such as invalid command-line options. Errors during the run such
as database errors or problems in the script will result in exit status 2.
In the latter case, <application>pgbench</application> will print partial
results.
</para>
</refsect1>
<refsect1> <refsect1>
<title>Notes</title> <title>Notes</title>
......
...@@ -4931,6 +4931,8 @@ main(int argc, char **argv) ...@@ -4931,6 +4931,8 @@ main(int argc, char **argv)
PGresult *res; PGresult *res;
char *env; char *env;
int exit_code = 0;
progname = get_progname(argv[0]); progname = get_progname(argv[0]);
if (argc > 1) if (argc > 1)
...@@ -5675,6 +5677,10 @@ main(int argc, char **argv) ...@@ -5675,6 +5677,10 @@ main(int argc, char **argv)
(void) threadRun(thread); (void) threadRun(thread);
#endif /* ENABLE_THREAD_SAFETY */ #endif /* ENABLE_THREAD_SAFETY */
for (int j = 0; j < thread->nstate; j++)
if (thread->state[j].state == CSTATE_ABORTED)
exit_code = 2;
/* aggregate thread level stats */ /* aggregate thread level stats */
mergeSimpleStats(&stats.latency, &thread->stats.latency); mergeSimpleStats(&stats.latency, &thread->stats.latency);
mergeSimpleStats(&stats.lag, &thread->stats.lag); mergeSimpleStats(&stats.lag, &thread->stats.lag);
...@@ -5699,7 +5705,10 @@ main(int argc, char **argv) ...@@ -5699,7 +5705,10 @@ main(int argc, char **argv)
INSTR_TIME_SUBTRACT(total_time, start_time); INSTR_TIME_SUBTRACT(total_time, start_time);
printResults(threads, &stats, total_time, conn_total_time, latency_late); printResults(threads, &stats, total_time, conn_total_time, latency_late);
return 0; if (exit_code != 0)
fprintf(stderr, "Run was aborted; the above results are incomplete.\n");
return exit_code;
} }
static void * static void *
......
...@@ -537,7 +537,7 @@ my @errors = ( ...@@ -537,7 +537,7 @@ my @errors = (
# SQL # SQL
[ [
'sql syntax error', 'sql syntax error',
0, 2,
[ [
qr{ERROR: syntax error}, qr{ERROR: syntax error},
qr{prepared statement .* does not exist} qr{prepared statement .* does not exist}
...@@ -556,11 +556,11 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -556,11 +556,11 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
# SHELL # SHELL
[ [
'shell bad command', 0, 'shell bad command', 2,
[qr{\(shell\) .* meta-command failed}], q{\shell no-such-command} [qr{\(shell\) .* meta-command failed}], q{\shell no-such-command}
], ],
[ [
'shell undefined variable', 0, 'shell undefined variable', 2,
[qr{undefined variable ":nosuchvariable"}], [qr{undefined variable ":nosuchvariable"}],
q{-- undefined variable in shell q{-- undefined variable in shell
\shell echo ::foo :nosuchvariable \shell echo ::foo :nosuchvariable
...@@ -600,75 +600,75 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -600,75 +600,75 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
[qr{unexpected function name}], q{\set i noSuchFunction()} [qr{unexpected function name}], q{\set i noSuchFunction()}
], ],
[ [
'set invalid variable name', 0, 'set invalid variable name', 2,
[qr{invalid variable name}], q{\set . 1} [qr{invalid variable name}], q{\set . 1}
], ],
[ [
'set division by zero', 0, 'set division by zero', 2,
[qr{division by zero}], q{\set i 1/0} [qr{division by zero}], q{\set i 1/0}
], ],
[ 'set undefined variable', [ 'set undefined variable',
0, 2,
[qr{undefined variable "nosuchvariable"}], [qr{undefined variable "nosuchvariable"}],
q{\set i :nosuchvariable} q{\set i :nosuchvariable}
], ],
[ 'set unexpected char', 1, [qr{unexpected character .;.}], q{\set i ;} ], [ 'set unexpected char', 1, [qr{unexpected character .;.}], q{\set i ;} ],
[ [
'set too many args', 'set too many args',
0, 2,
[qr{too many function arguments}], [qr{too many function arguments}],
q{\set i least(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)} q{\set i least(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)}
], ],
[ [
'set empty random range', 0, 'set empty random range', 2,
[qr{empty range given to random}], q{\set i random(5,3)} [qr{empty range given to random}], q{\set i random(5,3)}
], ],
[ [
'set random range too large', 'set random range too large',
0, 2,
[qr{random range is too large}], [qr{random range is too large}],
q{\set i random(:minint, :maxint)} q{\set i random(:minint, :maxint)}
], ],
[ [
'set gaussian param too small', 'set gaussian param too small',
0, 2,
[qr{gaussian param.* at least 2}], [qr{gaussian param.* at least 2}],
q{\set i random_gaussian(0, 10, 1.0)} q{\set i random_gaussian(0, 10, 1.0)}
], ],
[ [
'set exponential param greater 0', 'set exponential param greater 0',
0, 2,
[qr{exponential parameter must be greater }], [qr{exponential parameter must be greater }],
q{\set i random_exponential(0, 10, 0.0)} q{\set i random_exponential(0, 10, 0.0)}
], ],
[ [
'set zipfian param to 1', 'set zipfian param to 1',
0, 2,
[qr{zipfian parameter must be in range \(0, 1\) U \(1, \d+\]}], [qr{zipfian parameter must be in range \(0, 1\) U \(1, \d+\]}],
q{\set i random_zipfian(0, 10, 1)} q{\set i random_zipfian(0, 10, 1)}
], ],
[ [
'set zipfian param too large', 'set zipfian param too large',
0, 2,
[qr{zipfian parameter must be in range \(0, 1\) U \(1, \d+\]}], [qr{zipfian parameter must be in range \(0, 1\) U \(1, \d+\]}],
q{\set i random_zipfian(0, 10, 1000000)} q{\set i random_zipfian(0, 10, 1000000)}
], ],
[ [
'set non numeric value', 0, 'set non numeric value', 2,
[qr{malformed variable "foo" value: "bla"}], q{\set i :foo + 1} [qr{malformed variable "foo" value: "bla"}], q{\set i :foo + 1}
], ],
[ 'set no expression', 1, [qr{syntax error}], q{\set i} ], [ 'set no expression', 1, [qr{syntax error}], q{\set i} ],
[ 'set missing argument', 1, [qr{missing argument}i], q{\set} ], [ 'set missing argument', 1, [qr{missing argument}i], q{\set} ],
[ [
'set not a bool', 0, 'set not a bool', 2,
[qr{cannot coerce double to boolean}], q{\set b NOT 0.0} [qr{cannot coerce double to boolean}], q{\set b NOT 0.0}
], ],
[ [
'set not an int', 0, 'set not an int', 2,
[qr{cannot coerce boolean to int}], q{\set i TRUE + 2} [qr{cannot coerce boolean to int}], q{\set i TRUE + 2}
], ],
[ [
'set not a double', 0, 'set not a double', 2,
[qr{cannot coerce boolean to double}], q{\set d ln(TRUE)} [qr{cannot coerce boolean to double}], q{\set d ln(TRUE)}
], ],
[ [
...@@ -678,7 +678,7 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -678,7 +678,7 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
q{\set i CASE TRUE THEN 1 ELSE 0 END} q{\set i CASE TRUE THEN 1 ELSE 0 END}
], ],
[ [
'set random error', 0, 'set random error', 2,
[qr{cannot coerce boolean to int}], q{\set b random(FALSE, TRUE)} [qr{cannot coerce boolean to int}], q{\set b random(FALSE, TRUE)}
], ],
[ [
...@@ -691,31 +691,31 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -691,31 +691,31 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
], ],
# SET: ARITHMETIC OVERFLOW DETECTION # SET: ARITHMETIC OVERFLOW DETECTION
[ 'set double to int overflow', 0, [ 'set double to int overflow', 2,
[ qr{double to int overflow for 100} ], q{\set i int(1E32)} ], [ qr{double to int overflow for 100} ], q{\set i int(1E32)} ],
[ 'set bigint add overflow', 0, [ 'set bigint add overflow', 2,
[ qr{int add out} ], q{\set i (1<<62) + (1<<62)} ], [ qr{int add out} ], q{\set i (1<<62) + (1<<62)} ],
[ 'set bigint sub overflow', 0, [ 'set bigint sub overflow', 2,
[ qr{int sub out} ], q{\set i 0 - (1<<62) - (1<<62) - (1<<62)} ], [ qr{int sub out} ], q{\set i 0 - (1<<62) - (1<<62) - (1<<62)} ],
[ 'set bigint mul overflow', 0, [ 'set bigint mul overflow', 2,
[ qr{int mul out} ], q{\set i 2 * (1<<62)} ], [ qr{int mul out} ], q{\set i 2 * (1<<62)} ],
[ 'set bigint div out of range', 0, [ 'set bigint div out of range', 2,
[ qr{bigint div out of range} ], q{\set i :minint / -1} ], [ qr{bigint div out of range} ], q{\set i :minint / -1} ],
# SETSHELL # SETSHELL
[ [
'setshell not an int', 0, 'setshell not an int', 2,
[qr{command must return an integer}], q{\setshell i echo -n one} [qr{command must return an integer}], q{\setshell i echo -n one}
], ],
[ 'setshell missing arg', 1, [qr{missing argument }], q{\setshell var} ], [ 'setshell missing arg', 1, [qr{missing argument }], q{\setshell var} ],
[ [
'setshell no such command', 0, 'setshell no such command', 2,
[qr{could not read result }], q{\setshell var no-such-command} [qr{could not read result }], q{\setshell var no-such-command}
], ],
# SLEEP # SLEEP
[ [
'sleep undefined variable', 0, 'sleep undefined variable', 2,
[qr{sleep: undefined variable}], q{\sleep :nosuchvariable} [qr{sleep: undefined variable}], q{\sleep :nosuchvariable}
], ],
[ [
...@@ -738,7 +738,7 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -738,7 +738,7 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
], ],
[ 'misc empty script', 1, [qr{empty command list for script}], q{} ], [ 'misc empty script', 1, [qr{empty command list for script}], q{} ],
[ [
'bad boolean', 0, 'bad boolean', 2,
[qr{malformed variable.*trueXXX}], q{\set b :badtrue or true} [qr{malformed variable.*trueXXX}], q{\set b :badtrue or true}
],); ],);
...@@ -746,13 +746,14 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i); ...@@ -746,13 +746,14 @@ SELECT LEAST(:i, :i, :i, :i, :i, :i, :i, :i, :i, :i, :i);
for my $e (@errors) for my $e (@errors)
{ {
my ($name, $status, $re, $script) = @$e; my ($name, $status, $re, $script) = @$e;
$status != 0 or die "invalid expected status for test \"$name\"";
my $n = '001_pgbench_error_' . $name; my $n = '001_pgbench_error_' . $name;
$n =~ s/ /_/g; $n =~ s/ /_/g;
pgbench( pgbench(
'-n -t 1 -M prepared -Dfoo=bla -Dnull=null -Dtrue=true -Done=1 -Dzero=0.0 ' . '-n -t 1 -M prepared -Dfoo=bla -Dnull=null -Dtrue=true -Done=1 -Dzero=0.0 ' .
'-Dbadtrue=trueXXX -Dmaxint=9223372036854775807 -Dminint=-9223372036854775808', '-Dbadtrue=trueXXX -Dmaxint=9223372036854775807 -Dminint=-9223372036854775808',
$status, $status,
[ $status ? qr{^$} : qr{processed: 0/1} ], [ $status == 1 ? qr{^$} : qr{processed: 0/1} ],
$re, $re,
'pgbench script error: ' . $name, 'pgbench script error: ' . $name,
{ $n => $script }); { $n => $script });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment