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 0ea9efbe9ec1bf07. 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.
This commit is contained in:
parent
1038bc91ca
commit
429ee5a822
@ -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;
|
|||||||
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);
|
|||||||
%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; }
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
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 @@
|
|||||||
* 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;
|
|||||||
/* 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]
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
%{
|
||||||
|
/*
|
||||||
|
* 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]
|
|||||||
|
|
||||||
[\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)
|
|||||||
}
|
}
|
||||||
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,
|
||||||
my_commands->cols[2] - 1);
|
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 */
|
/* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user