Commit e41beea0 authored by Robert Haas's avatar Robert Haas

Improve pgbench error reporting.

This would have been worth doing on general principle anyway, but the
recent addition of an expression syntax to pgbench makes it an even
better idea than it would have been otherwise.

Fabien Coelho
parent 05cce2f9
...@@ -17,6 +17,13 @@ static int yyline = 0, yycol = 0; ...@@ -17,6 +17,13 @@ static int yyline = 0, yycol = 0;
static YY_BUFFER_STATE scanbufhandle; static YY_BUFFER_STATE scanbufhandle;
static char *scanbuf; static char *scanbuf;
static int scanbuflen; static int scanbuflen;
/* context information for error reporting */
static char *expr_source = NULL;
static int expr_lineno = 0;
static char *expr_full_line = NULL;
static char *expr_command = NULL;
static int expr_col = 0;
%} %}
%option 8bit %option 8bit
...@@ -56,7 +63,9 @@ space [ \t\r\f] ...@@ -56,7 +63,9 @@ space [ \t\r\f]
. { . {
yycol += yyleng; yycol += yyleng;
fprintf(stderr, "unexpected character \"%s\"\n", yytext); syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
"unexpected character", yytext, expr_col + yycol);
/* dead code, exit is called from syntax_error */
return CHAR_ERROR; return CHAR_ERROR;
} }
%% %%
...@@ -64,20 +73,27 @@ space [ \t\r\f] ...@@ -64,20 +73,27 @@ space [ \t\r\f]
void void
yyerror(const char *message) yyerror(const char *message)
{ {
/* yyline is always 1 as pgbench calls the parser for each line... syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
* so the interesting location information is the column number */ message, NULL, expr_col + yycol);
fprintf(stderr, "%s at column %d\n", message, yycol);
/* go on to raise the error from pgbench with more information */
} }
/* /*
* Called before any actual parsing is done * Called before any actual parsing is done
*/ */
void void
expr_scanner_init(const char *str) expr_scanner_init(const char *str, const char *source,
const int lineno, const char *line,
const char *cmd, const int ecol)
{ {
Size slen = strlen(str); Size slen = strlen(str);
/* save context informations for error messages */
expr_source = (char *) source;
expr_lineno = (int) lineno;
expr_full_line = (char *) line;
expr_command = (char *) cmd;
expr_col = (int) ecol;
/* /*
* Might be left over after error * Might be left over after error
*/ */
...@@ -105,4 +121,9 @@ expr_scanner_finish(void) ...@@ -105,4 +121,9 @@ expr_scanner_finish(void)
{ {
yy_delete_buffer(scanbufhandle); yy_delete_buffer(scanbufhandle);
pg_free(scanbuf); pg_free(scanbuf);
expr_source = NULL;
expr_lineno = 0;
expr_full_line = NULL;
expr_command = NULL;
expr_col = 0;
} }
...@@ -287,6 +287,7 @@ typedef struct ...@@ -287,6 +287,7 @@ typedef struct
int type; /* command type (SQL_COMMAND or META_COMMAND) */ int type; /* command type (SQL_COMMAND or META_COMMAND) */
int argc; /* number of command words */ int argc; /* number of command words */
char *argv[MAX_ARGS]; /* command word list */ char *argv[MAX_ARGS]; /* command word list */
int cols[MAX_ARGS]; /* corresponding column starting from 1 */
PgBenchExpr *expr; /* parsed expression */ PgBenchExpr *expr; /* parsed expression */
} Command; } Command;
...@@ -2185,6 +2186,32 @@ parseQuery(Command *cmd, const char *raw_sql) ...@@ -2185,6 +2186,32 @@ parseQuery(Command *cmd, const char *raw_sql)
return true; return true;
} }
void
syntax_error(const char *source, const int lineno,
const char *line, const char *command,
const char *msg, const char *more, const int column)
{
fprintf(stderr, "%s:%d: %s", source, lineno, msg);
if (more != NULL)
fprintf(stderr, " (%s)", more);
if (column != -1)
fprintf(stderr, " at column %d", column);
fprintf(stderr, " in command \"%s\"\n", command);
if (line != NULL)
{
fprintf(stderr, "%s\n", line);
if (column != -1)
{
int i;
for (i = 0; i < column - 1; i++)
fprintf(stderr, " ");
fprintf(stderr, "^ error found here\n");
}
}
exit(1);
}
/* Parse a command; return a Command struct, or NULL if it's a comment */ /* Parse a command; return a Command struct, or NULL if it's a comment */
static Command * static Command *
process_commands(char *buf, const char *source, const int lineno) process_commands(char *buf, const char *source, const int lineno)
...@@ -2229,6 +2256,7 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2229,6 +2256,7 @@ process_commands(char *buf, const char *source, const int lineno)
while (tok != NULL) while (tok != NULL)
{ {
my_commands->cols[j] = tok - buf + 1;
my_commands->argv[j++] = pg_strdup(tok); my_commands->argv[j++] = pg_strdup(tok);
my_commands->argc++; my_commands->argc++;
if (max_args >= 0 && my_commands->argc >= max_args) if (max_args >= 0 && my_commands->argc >= max_args)
...@@ -2246,9 +2274,10 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2246,9 +2274,10 @@ process_commands(char *buf, const char *source, const int lineno)
if (my_commands->argc < 4) if (my_commands->argc < 4)
{ {
fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing arguments", NULL, -1);
} }
/* argc >= 4 */ /* argc >= 4 */
if (my_commands->argc == 4 || /* uniform without/with "uniform" keyword */ if (my_commands->argc == 4 || /* uniform without/with "uniform" keyword */
...@@ -2263,41 +2292,38 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2263,41 +2292,38 @@ process_commands(char *buf, const char *source, const int lineno)
{ {
if (my_commands->argc < 6) if (my_commands->argc < 6)
{ {
fprintf(stderr, "%s(%s): missing threshold argument\n", my_commands->argv[0], my_commands->argv[4]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing threshold argument", my_commands->argv[4], -1);
} }
else if (my_commands->argc > 6) else if (my_commands->argc > 6)
{ {
fprintf(stderr, "%s(%s): too many arguments (extra:", syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
my_commands->argv[0], my_commands->argv[4]); "too many arguments", my_commands->argv[4],
for (j = 6; j < my_commands->argc; j++) my_commands->cols[6]);
fprintf(stderr, " %s", my_commands->argv[j]);
fprintf(stderr, ")\n");
exit(1);
} }
} }
else /* cannot parse, unexpected arguments */ else /* cannot parse, unexpected arguments */
{ {
fprintf(stderr, "%s: unexpected arguments (bad:", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
for (j = 4; j < my_commands->argc; j++) "unexpected argument", my_commands->argv[4],
fprintf(stderr, " %s", my_commands->argv[j]); my_commands->cols[4]);
fprintf(stderr, ")\n");
exit(1);
} }
} }
else if (pg_strcasecmp(my_commands->argv[0], "set") == 0) else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
{ {
if (my_commands->argc < 3) if (my_commands->argc < 3)
{ {
fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing argument", NULL, -1);
} }
expr_scanner_init(my_commands->argv[2]); expr_scanner_init(my_commands->argv[2], source, lineno,
my_commands->line, my_commands->argv[0],
my_commands->cols[2] - 1);
if (expr_yyparse() != 0) if (expr_yyparse() != 0)
{ {
fprintf(stderr, "%s: parse error\n", my_commands->argv[0]); /* dead code: exit done from syntax_error called by yyerror */
exit(1); exit(1);
} }
...@@ -2309,8 +2335,8 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2309,8 +2335,8 @@ process_commands(char *buf, const char *source, const int lineno)
{ {
if (my_commands->argc < 2) if (my_commands->argc < 2)
{ {
fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing argument", NULL, -1);
} }
/* /*
...@@ -2339,12 +2365,13 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2339,12 +2365,13 @@ process_commands(char *buf, const char *source, const int lineno)
pg_strcasecmp(my_commands->argv[2], "ms") != 0 && pg_strcasecmp(my_commands->argv[2], "ms") != 0 &&
pg_strcasecmp(my_commands->argv[2], "s") != 0) pg_strcasecmp(my_commands->argv[2], "s") != 0)
{ {
fprintf(stderr, "%s: unknown time unit '%s' - must be us, ms or s\n", syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
my_commands->argv[0], my_commands->argv[2]); "unknown time unit, must be us, ms or s",
exit(1); my_commands->argv[2], my_commands->cols[2]);
} }
} }
/* this should be an error?! */
for (j = 3; j < my_commands->argc; j++) for (j = 3; j < my_commands->argc; j++)
fprintf(stderr, "%s: extra argument \"%s\" ignored\n", fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
my_commands->argv[0], my_commands->argv[j]); my_commands->argv[0], my_commands->argv[j]);
...@@ -2353,22 +2380,22 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2353,22 +2380,22 @@ process_commands(char *buf, const char *source, const int lineno)
{ {
if (my_commands->argc < 3) if (my_commands->argc < 3)
{ {
fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing argument", NULL, -1);
} }
} }
else if (pg_strcasecmp(my_commands->argv[0], "shell") == 0) else if (pg_strcasecmp(my_commands->argv[0], "shell") == 0)
{ {
if (my_commands->argc < 1) if (my_commands->argc < 1)
{ {
fprintf(stderr, "%s: missing command\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "missing command", NULL, -1);
} }
} }
else else
{ {
fprintf(stderr, "Invalid command %s\n", my_commands->argv[0]); syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
exit(1); "invalid command", NULL, -1);
} }
} }
else else
......
...@@ -47,7 +47,12 @@ extern PgBenchExpr *expr_parse_result; ...@@ -47,7 +47,12 @@ extern PgBenchExpr *expr_parse_result;
extern int expr_yyparse(void); extern int expr_yyparse(void);
extern int expr_yylex(void); extern int expr_yylex(void);
extern void expr_yyerror(const char *str); extern void expr_yyerror(const char *str);
extern void expr_scanner_init(const char *str); extern void expr_scanner_init(const char *str, const char *source,
const int lineno, const char *line,
const char *cmd, const int ecol);
extern void syntax_error(const char* source, const int lineno, const char* line,
const char* cmd, const char* msg, const char* more,
const int col);
extern void expr_scanner_finish(void); extern void expr_scanner_finish(void);
extern int64 strtoint64(const char *str); extern int64 strtoint64(const char *str);
......
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