diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y index cac4d5e4f44..55fda5b254c 100644 --- a/src/bin/pgbench/exprparse.y +++ b/src/bin/pgbench/exprparse.y @@ -7,6 +7,8 @@ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * + * src/bin/pgbench/exprparse.y + * *------------------------------------------------------------------------- */ @@ -19,16 +21,19 @@ PgBenchExpr *expr_parse_result; static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list); static PgBenchExpr *make_integer_constant(int64 ival); static PgBenchExpr *make_variable(char *varname); -static PgBenchExpr *make_op(const char *operator, PgBenchExpr *lexpr, - PgBenchExpr *rexpr); -static int find_func(const char *fname); -static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args); +static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator, + PgBenchExpr *lexpr, PgBenchExpr *rexpr); +static int find_func(yyscan_t yyscanner, const char *fname); +static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args); %} %expect 0 %name-prefix="expr_yy" +%parse-param {yyscan_t yyscanner} +%lex-param {yyscan_t yyscanner} + %union { int64 ival; @@ -43,7 +48,6 @@ static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args); %type VARIABLE FUNCTION %token INTEGER VARIABLE FUNCTION -%token CHAR_ERROR /* never used, will raise a syntax error */ /* Precedence: lowest to highest */ %left '+' '-' @@ -61,18 +65,19 @@ elist: { $$ = NULL; } expr: '(' expr ')' { $$ = $2; } | '+' expr %prec UMINUS { $$ = $2; } - | '-' expr %prec UMINUS { $$ = make_op("-", make_integer_constant(0), $2); } - | expr '+' expr { $$ = make_op("+", $1, $3); } - | expr '-' expr { $$ = make_op("-", $1, $3); } - | expr '*' expr { $$ = make_op("*", $1, $3); } - | expr '/' expr { $$ = make_op("/", $1, $3); } - | expr '%' expr { $$ = make_op("%", $1, $3); } + | '-' expr %prec UMINUS { $$ = make_op(yyscanner, "-", + make_integer_constant(0), $2); } + | expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); } + | expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); } + | expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); } + | expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); } + | expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); } | INTEGER { $$ = make_integer_constant($1); } | VARIABLE { $$ = make_variable($1); } - | function '(' elist ')'{ $$ = make_func($1, $3); } + | function '(' elist ')' { $$ = make_func(yyscanner, $1, $3); } ; -function: FUNCTION { $$ = find_func($1); pg_free($1); } +function: FUNCTION { $$ = find_func(yyscanner, $1); pg_free($1); } ; %% @@ -98,9 +103,10 @@ make_variable(char *varname) } static PgBenchExpr * -make_op(const char *operator, PgBenchExpr *lexpr, PgBenchExpr *rexpr) +make_op(yyscan_t yyscanner, const char *operator, + PgBenchExpr *lexpr, PgBenchExpr *rexpr) { - return make_func(find_func(operator), + return make_func(yyscanner, find_func(yyscanner, operator), make_elist(rexpr, make_elist(lexpr, NULL))); } @@ -139,7 +145,7 @@ static struct * or fail if the function is unknown. */ static int -find_func(const char * fname) +find_func(yyscan_t yyscanner, const char *fname) { int i = 0; @@ -150,7 +156,7 @@ find_func(const char * fname) i++; } - expr_yyerror_more("unexpected function name", fname); + expr_yyerror_more(yyscanner, "unexpected function name", fname); /* not reached */ return -1; @@ -198,7 +204,7 @@ elist_length(PgBenchExprList *list) /* Build function call expression */ static PgBenchExpr * -make_func(const int fnumber, PgBenchExprList *args) +make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args) { PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr)); @@ -206,13 +212,13 @@ make_func(const int fnumber, PgBenchExprList *args) if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 && PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args)) - expr_yyerror_more("unexpected number of arguments", + expr_yyerror_more(yyscanner, "unexpected number of arguments", PGBENCH_FUNCTIONS[fnumber].fname); /* check at least one arg for min & max */ if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 && elist_length(args) == 0) - expr_yyerror_more("at least one argument expected", + expr_yyerror_more(yyscanner, "at least one argument expected", PGBENCH_FUNCTIONS[fnumber].fname); expr->etype = ENODE_FUNCTION; @@ -226,4 +232,15 @@ make_func(const int fnumber, PgBenchExprList *args) return expr; } +/* + * exprscan.l is compiled as part of exprparse.y. Currently, this is + * unavoidable because exprparse does not create a .h file to export + * its token symbols. If these files ever grow large enough to be + * worth compiling separately, that could be fixed; but for now it + * seems like useless complication. + */ + +/* First, get rid of "#define yyscan_t" from pgbench.h */ +#undef yyscan_t + #include "exprscan.c" diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l index fc7615f5585..00cb74d7dad 100644 --- a/src/bin/pgbench/exprscan.l +++ b/src/bin/pgbench/exprscan.l @@ -7,6 +7,8 @@ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * + * src/bin/pgbench/exprscan.l + * *------------------------------------------------------------------------- */ @@ -16,16 +18,27 @@ static int yyline = 0, yycol = 0; /* Handles to the buffer that the lexer uses internally */ static YY_BUFFER_STATE scanbufhandle; static char *scanbuf; -static int scanbuflen; /* context information for error reporting */ -static char *expr_source = NULL; +static const char *expr_source = NULL; static int expr_lineno = 0; -static char *expr_full_line = NULL; -static char *expr_command = NULL; +static const char *expr_full_line = NULL; +static const char *expr_command = NULL; static int expr_col = 0; + +/* + * Work around a bug in flex 2.5.35: it emits a couple of functions that + * it forgets to emit declarations for. Since we use -Wmissing-prototypes, + * this would cause warnings. Providing our own declarations should be + * harmless even when the bug gets fixed. + */ +extern int expr_yyget_column(yyscan_t yyscanner); +extern void expr_yyset_column(int column_no, yyscan_t yyscanner); + %} +/* Except for the prefix, these options should match psqlscan.l */ +%option reentrant %option 8bit %option never-interactive %option nodefault @@ -42,6 +55,15 @@ space [ \t\r\f] %% +%{ + /* + * Force flex into the appropriate start state ... which, for this + * case, is always INITIAL. This ensures that we can transition + * between different lexers sharing the same yyscan_t. + */ + BEGIN(INITIAL); +%} + "+" { yycol += yyleng; return '+'; } "-" { yycol += yyleng; return '-'; } "*" { yycol += yyleng; return '*'; } @@ -69,77 +91,76 @@ space [ \t\r\f] [\n] { yycol = 0; yyline++; } -{space}+ { yycol += yyleng; /* ignore */ } +{space}+ { yycol += yyleng; /* otherwise ignore */ } . { yycol += yyleng; 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; + /* NOTREACHED, exit is called from syntax_error */ + return 0; } %% void -expr_yyerror_more(const char *message, const char *more) +expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more) { syntax_error(expr_source, expr_lineno, expr_full_line, expr_command, message, more, expr_col + yycol); } void -yyerror(const char *message) +yyerror(yyscan_t yyscanner, const char *message) { - expr_yyerror_more(message, NULL); + expr_yyerror_more(yyscanner, message, NULL); } /* * Called before any actual parsing is done */ -void +yyscan_t expr_scanner_init(const char *str, const char *source, - const int lineno, const char *line, - const char *cmd, const int ecol) + int lineno, const char *line, + const char *cmd, int ecol) { + yyscan_t yyscanner; 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; + /* Set up yyscan_t */ + yylex_init(&yyscanner); + + /* save context information for error messages */ + expr_source = source; + expr_lineno = lineno; + expr_full_line = line; + expr_command = cmd; + expr_col = ecol; /* reset error pointers for this scan */ yycol = yyline = 0; - /* - * Might be left over after error - */ - if (YY_CURRENT_BUFFER) - yy_delete_buffer(YY_CURRENT_BUFFER); - /* * Make a scan buffer with special termination needed by flex. */ - scanbuflen = slen; scanbuf = pg_malloc(slen + 2); memcpy(scanbuf, str, slen); scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; - scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); + scanbufhandle = yy_scan_buffer(scanbuf, slen + 2, yyscanner); - BEGIN(INITIAL); + return yyscanner; } /* - * Called after parsing is done to clean up after seg_scanner_init() + * Called after parsing is done to clean up after expr_scanner_init() */ void -expr_scanner_finish(void) +expr_scanner_finish(yyscan_t yyscanner) { - yy_delete_buffer(scanbufhandle); + yy_delete_buffer(scanbufhandle, yyscanner); pg_free(scanbuf); + yylex_destroy(yyscanner); + expr_source = NULL; expr_lineno = 0; expr_full_line = NULL; diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 4606fb00158..dab1ed4114e 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -2495,17 +2495,22 @@ process_commands(char *buf, const char *source, const int lineno) } else if (pg_strcasecmp(my_commands->argv[0], "set") == 0) { + yyscan_t yyscanner; + if (my_commands->argc < 3) { syntax_error(source, lineno, my_commands->line, my_commands->argv[0], "missing argument", NULL, -1); } - expr_scanner_init(my_commands->argv[2], source, lineno, - my_commands->line, my_commands->argv[0], - my_commands->cols[2] - 1); + yyscanner = 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(yyscanner) != 0) { /* dead code: exit done from syntax_error called by yyerror */ exit(1); @@ -2513,7 +2518,7 @@ process_commands(char *buf, const char *source, const int lineno) my_commands->expr = expr_parse_result; - expr_scanner_finish(); + expr_scanner_finish(yyscanner); } else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0) { diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h index c6aeb5b6dbd..ba2c51acc96 100644 --- a/src/bin/pgbench/pgbench.h +++ b/src/bin/pgbench/pgbench.h @@ -11,6 +11,15 @@ #ifndef PGBENCH_H #define PGBENCH_H +/* + * This file is included outside exprscan.l, in places where we can't see + * flex's definition of typedef yyscan_t. Fortunately, it's documented as + * being "void *", so we can use a macro to keep the function declarations + * here looking like the definitions in exprscan.l. exprparse.y also + * uses this to be able to declare things as "yyscan_t". + */ +#define yyscan_t void * + /* Types of expression nodes */ typedef enum PgBenchExprType { @@ -73,17 +82,18 @@ struct PgBenchExprList extern PgBenchExpr *expr_parse_result; -extern int expr_yyparse(void); -extern int expr_yylex(void); -extern void expr_yyerror(const char *str); -extern void expr_yyerror_more(const char *str, const char *more); -extern void expr_scanner_init(const char *str, const char *source, - const int lineno, const char *line, - const char *cmd, const int ecol); +extern int expr_yyparse(yyscan_t yyscanner); +extern int expr_yylex(yyscan_t yyscanner); +extern void expr_yyerror(yyscan_t yyscanner, const char *str); +extern void expr_yyerror_more(yyscan_t yyscanner, const char *str, + const char *more); +extern yyscan_t expr_scanner_init(const char *str, const char *source, + int lineno, const char *line, + const char *cmd, 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(yyscan_t yyscanner); extern int64 strtoint64(const char *str);