Commit 429ee5a8 authored by Tom Lane's avatar Tom Lane

Make pgbench's expression lexer reentrant.

This is a necessary preliminary step for making it play with psqlscan.l
given the way I set up the lexer input-buffer sharing mechanism in commit
0ea9efbe.

I've not tried to make it *actually* reentrant; there's still some static
variables laying about.  But flex thinks it's reentrant, and that's what
counts.

In support of that, fix exprparse.y to pass through the yyscan_t from the
caller.  Also do some minor code beautification, like not casting away
const.
parent 1038bc91
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* src/bin/pgbench/exprparse.y
*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,16 +21,19 @@ PgBenchExpr *expr_parse_result; ...@@ -19,16 +21,19 @@ PgBenchExpr *expr_parse_result;
static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list); static PgBenchExprList *make_elist(PgBenchExpr *exp, PgBenchExprList *list);
static PgBenchExpr *make_integer_constant(int64 ival); static PgBenchExpr *make_integer_constant(int64 ival);
static PgBenchExpr *make_variable(char *varname); static PgBenchExpr *make_variable(char *varname);
static PgBenchExpr *make_op(const char *operator, PgBenchExpr *lexpr, static PgBenchExpr *make_op(yyscan_t yyscanner, const char *operator,
PgBenchExpr *rexpr); PgBenchExpr *lexpr, PgBenchExpr *rexpr);
static int find_func(const char *fname); static int find_func(yyscan_t yyscanner, const char *fname);
static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args); static PgBenchExpr *make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args);
%} %}
%expect 0 %expect 0
%name-prefix="expr_yy" %name-prefix="expr_yy"
%parse-param {yyscan_t yyscanner}
%lex-param {yyscan_t yyscanner}
%union %union
{ {
int64 ival; int64 ival;
...@@ -43,7 +48,6 @@ static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args); ...@@ -43,7 +48,6 @@ static PgBenchExpr *make_func(const int fnumber, PgBenchExprList *args);
%type <str> VARIABLE FUNCTION %type <str> VARIABLE FUNCTION
%token INTEGER VARIABLE FUNCTION %token INTEGER VARIABLE FUNCTION
%token CHAR_ERROR /* never used, will raise a syntax error */
/* Precedence: lowest to highest */ /* Precedence: lowest to highest */
%left '+' '-' %left '+' '-'
...@@ -61,18 +65,19 @@ elist: { $$ = NULL; } ...@@ -61,18 +65,19 @@ elist: { $$ = NULL; }
expr: '(' expr ')' { $$ = $2; } expr: '(' expr ')' { $$ = $2; }
| '+' expr %prec UMINUS { $$ = $2; } | '+' expr %prec UMINUS { $$ = $2; }
| '-' expr %prec UMINUS { $$ = make_op("-", make_integer_constant(0), $2); } | '-' expr %prec UMINUS { $$ = make_op(yyscanner, "-",
| expr '+' expr { $$ = make_op("+", $1, $3); } make_integer_constant(0), $2); }
| expr '-' expr { $$ = make_op("-", $1, $3); } | expr '+' expr { $$ = make_op(yyscanner, "+", $1, $3); }
| expr '*' expr { $$ = make_op("*", $1, $3); } | expr '-' expr { $$ = make_op(yyscanner, "-", $1, $3); }
| expr '/' expr { $$ = make_op("/", $1, $3); } | expr '*' expr { $$ = make_op(yyscanner, "*", $1, $3); }
| expr '%' expr { $$ = make_op("%", $1, $3); } | expr '/' expr { $$ = make_op(yyscanner, "/", $1, $3); }
| expr '%' expr { $$ = make_op(yyscanner, "%", $1, $3); }
| INTEGER { $$ = make_integer_constant($1); } | INTEGER { $$ = make_integer_constant($1); }
| VARIABLE { $$ = make_variable($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) ...@@ -98,9 +103,10 @@ make_variable(char *varname)
} }
static PgBenchExpr * 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))); make_elist(rexpr, make_elist(lexpr, NULL)));
} }
...@@ -139,7 +145,7 @@ static struct ...@@ -139,7 +145,7 @@ static struct
* or fail if the function is unknown. * or fail if the function is unknown.
*/ */
static int static int
find_func(const char * fname) find_func(yyscan_t yyscanner, const char *fname)
{ {
int i = 0; int i = 0;
...@@ -150,7 +156,7 @@ find_func(const char * fname) ...@@ -150,7 +156,7 @@ find_func(const char * fname)
i++; i++;
} }
expr_yyerror_more("unexpected function name", fname); expr_yyerror_more(yyscanner, "unexpected function name", fname);
/* not reached */ /* not reached */
return -1; return -1;
...@@ -198,7 +204,7 @@ elist_length(PgBenchExprList *list) ...@@ -198,7 +204,7 @@ elist_length(PgBenchExprList *list)
/* Build function call expression */ /* Build function call expression */
static PgBenchExpr * static PgBenchExpr *
make_func(const int fnumber, PgBenchExprList *args) make_func(yyscan_t yyscanner, int fnumber, PgBenchExprList *args)
{ {
PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr)); PgBenchExpr *expr = pg_malloc(sizeof(PgBenchExpr));
...@@ -206,13 +212,13 @@ make_func(const int fnumber, PgBenchExprList *args) ...@@ -206,13 +212,13 @@ make_func(const int fnumber, PgBenchExprList *args)
if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 && if (PGBENCH_FUNCTIONS[fnumber].nargs >= 0 &&
PGBENCH_FUNCTIONS[fnumber].nargs != elist_length(args)) 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); PGBENCH_FUNCTIONS[fnumber].fname);
/* check at least one arg for min & max */ /* check at least one arg for min & max */
if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 && if (PGBENCH_FUNCTIONS[fnumber].nargs == -1 &&
elist_length(args) == 0) 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); PGBENCH_FUNCTIONS[fnumber].fname);
expr->etype = ENODE_FUNCTION; expr->etype = ENODE_FUNCTION;
...@@ -226,4 +232,15 @@ make_func(const int fnumber, PgBenchExprList *args) ...@@ -226,4 +232,15 @@ make_func(const int fnumber, PgBenchExprList *args)
return expr; 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" #include "exprscan.c"
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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; ...@@ -16,16 +18,27 @@ static int yyline = 0, yycol = 0;
/* Handles to the buffer that the lexer uses internally */ /* Handles to the buffer that the lexer uses internally */
static YY_BUFFER_STATE scanbufhandle; static YY_BUFFER_STATE scanbufhandle;
static char *scanbuf; static char *scanbuf;
static int scanbuflen;
/* context information for error reporting */ /* context information for error reporting */
static char *expr_source = NULL; static const char *expr_source = NULL;
static int expr_lineno = 0; static int expr_lineno = 0;
static char *expr_full_line = NULL; static const char *expr_full_line = NULL;
static char *expr_command = NULL; static const char *expr_command = NULL;
static int expr_col = 0; 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 8bit
%option never-interactive %option never-interactive
%option nodefault %option nodefault
...@@ -42,6 +55,15 @@ space [ \t\r\f] ...@@ -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 '-'; } "-" { yycol += yyleng; return '-'; }
"*" { yycol += yyleng; return '*'; } "*" { yycol += yyleng; return '*'; }
...@@ -69,77 +91,76 @@ space [ \t\r\f] ...@@ -69,77 +91,76 @@ space [ \t\r\f]
[\n] { yycol = 0; yyline++; } [\n] { yycol = 0; yyline++; }
{space}+ { yycol += yyleng; /* ignore */ } {space}+ { yycol += yyleng; /* otherwise ignore */ }
. { . {
yycol += yyleng; yycol += yyleng;
syntax_error(expr_source, expr_lineno, expr_full_line, expr_command, syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
"unexpected character", yytext, expr_col + yycol); "unexpected character", yytext, expr_col + yycol);
/* dead code, exit is called from syntax_error */ /* NOTREACHED, exit is called from syntax_error */
return CHAR_ERROR; return 0;
} }
%% %%
void 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, syntax_error(expr_source, expr_lineno, expr_full_line, expr_command,
message, more, expr_col + yycol); message, more, expr_col + yycol);
} }
void 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 * Called before any actual parsing is done
*/ */
void yyscan_t
expr_scanner_init(const char *str, const char *source, expr_scanner_init(const char *str, const char *source,
const int lineno, const char *line, int lineno, const char *line,
const char *cmd, const int ecol) const char *cmd, int ecol)
{ {
yyscan_t yyscanner;
Size slen = strlen(str); Size slen = strlen(str);
/* save context informations for error messages */ /* Set up yyscan_t */
expr_source = (char *) source; yylex_init(&yyscanner);
expr_lineno = (int) lineno;
expr_full_line = (char *) line; /* save context information for error messages */
expr_command = (char *) cmd; expr_source = source;
expr_col = (int) ecol; expr_lineno = lineno;
expr_full_line = line;
expr_command = cmd;
expr_col = ecol;
/* reset error pointers for this scan */ /* reset error pointers for this scan */
yycol = yyline = 0; 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. * Make a scan buffer with special termination needed by flex.
*/ */
scanbuflen = slen;
scanbuf = pg_malloc(slen + 2); scanbuf = pg_malloc(slen + 2);
memcpy(scanbuf, str, slen); memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; 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 void
expr_scanner_finish(void) expr_scanner_finish(yyscan_t yyscanner)
{ {
yy_delete_buffer(scanbufhandle); yy_delete_buffer(scanbufhandle, yyscanner);
pg_free(scanbuf); pg_free(scanbuf);
yylex_destroy(yyscanner);
expr_source = NULL; expr_source = NULL;
expr_lineno = 0; expr_lineno = 0;
expr_full_line = NULL; expr_full_line = NULL;
......
...@@ -2495,17 +2495,22 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2495,17 +2495,22 @@ process_commands(char *buf, const char *source, const int lineno)
} }
else if (pg_strcasecmp(my_commands->argv[0], "set") == 0) else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
{ {
yyscan_t yyscanner;
if (my_commands->argc < 3) if (my_commands->argc < 3)
{ {
syntax_error(source, lineno, my_commands->line, my_commands->argv[0], syntax_error(source, lineno, my_commands->line, my_commands->argv[0],
"missing argument", NULL, -1); "missing argument", NULL, -1);
} }
expr_scanner_init(my_commands->argv[2], source, lineno, yyscanner = expr_scanner_init(my_commands->argv[2],
my_commands->line, my_commands->argv[0], source,
lineno,
my_commands->line,
my_commands->argv[0],
my_commands->cols[2] - 1); my_commands->cols[2] - 1);
if (expr_yyparse() != 0) if (expr_yyparse(yyscanner) != 0)
{ {
/* dead code: exit done from syntax_error called by yyerror */ /* dead code: exit done from syntax_error called by yyerror */
exit(1); exit(1);
...@@ -2513,7 +2518,7 @@ process_commands(char *buf, const char *source, const int lineno) ...@@ -2513,7 +2518,7 @@ process_commands(char *buf, const char *source, const int lineno)
my_commands->expr = expr_parse_result; my_commands->expr = expr_parse_result;
expr_scanner_finish(); expr_scanner_finish(yyscanner);
} }
else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0) else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0)
{ {
......
...@@ -11,6 +11,15 @@ ...@@ -11,6 +11,15 @@
#ifndef PGBENCH_H #ifndef PGBENCH_H
#define 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 */ /* Types of expression nodes */
typedef enum PgBenchExprType typedef enum PgBenchExprType
{ {
...@@ -73,17 +82,18 @@ struct PgBenchExprList ...@@ -73,17 +82,18 @@ struct PgBenchExprList
extern PgBenchExpr *expr_parse_result; extern PgBenchExpr *expr_parse_result;
extern int expr_yyparse(void); extern int expr_yyparse(yyscan_t yyscanner);
extern int expr_yylex(void); extern int expr_yylex(yyscan_t yyscanner);
extern void expr_yyerror(const char *str); extern void expr_yyerror(yyscan_t yyscanner, const char *str);
extern void expr_yyerror_more(const char *str, const char *more); extern void expr_yyerror_more(yyscan_t yyscanner, const char *str,
extern void expr_scanner_init(const char *str, const char *source, const char *more);
const int lineno, const char *line, extern yyscan_t expr_scanner_init(const char *str, const char *source,
const char *cmd, const int ecol); int lineno, const char *line,
const char *cmd, int ecol);
extern void syntax_error(const char *source, const int lineno, const char *line, extern void syntax_error(const char *source, const int lineno, const char *line,
const char *cmd, const char *msg, const char *more, const char *cmd, const char *msg, const char *more,
const int col); const int col);
extern void expr_scanner_finish(void); extern void expr_scanner_finish(yyscan_t yyscanner);
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