2/*-------------------------------------------------------------------------
5 * lexical scanner for pgbench backslash commands
7 * This lexer supports two operating modes:
9 * In INITIAL state, just parse off whitespace-separated words (this mode
10 * is basically equivalent to strtok(), which is what we used to use).
12 * In EXPR state, lex for the simple expression syntax of exprparse.y.
14 * In either mode, stop upon hitting newline or end of string.
16 * Note that this lexer operates within the framework created by psqlscan.l,
18 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
21 * src/bin/pgbench/exprscan.l
23 *-------------------------------------------------------------------------
28 * NB: include exprparse.h only AFTER including pgbench.h, because pgbench.h
29 * contains definitions needed for YYSTYPE. Likewise, pgbench.h must come after
30 * psqlscan_int.h for yyscan_t.
38/* context information for reporting errors in expressions */
44/* indicates whether last yylex() call read a newline */
static const char * expr_source
static bool last_was_newline
static int expr_start_offset
static const char * expr_command
51/* Except for the prefix, these options should match psqlscan.l */
55%option never-interactive
61%option prefix="expr_yy"
63/* Character classes */
64alpha [a-zA-Z200円-377円_]
66alnum [A-Za-z200円-377円_0-9]
67/* {space} + {nonspace} + {newline} should cover all characters */
69nonspace [^ \t\r\f\v\n]
72/* Line continuation marker */
73continuation \\\r?{newline}
75/* case insensitive keywords */
85false [Ff][Aa][Ll][Ss][Ee]
88isnull [Ii][Ss][Nn][Uu][Ll][Ll]
89notnull [Nn][Oo][Tt][Nn][Uu][Ll][Ll]
97 /* Declare some local variables inside yylex(), for convenience */
98 PsqlScanState cur_state =
yyextra;
101 * Force flex into the state indicated by start_state. This has a
102 * couple of purposes: it lets some of the functions below set a new
103 * starting state without ugly direct access to flex variables, and it
104 * allows us to transition from one flex lexer to another so that we
105 * can lex different parts of the source string using separate lexers.
107 BEGIN(cur_state->start_state);
109 /* Reset was-newline flag */
110 last_was_newline =
false;
116 /* Found a word, emit and return it */
void psqlscan_emit(PsqlScanState state, const char *txt, int len)
122 * We need this rule to avoid returning "word\" instead of recognizing
123 * a continuation marker just after a word:
125{nonspace}+{continuation} {
126 /* Found "word\\\r?\n", emit and return just "word" */
128 if (yytext[wordlen] ==
'\r')
130 Assert(yytext[wordlen] ==
'\\');
Assert(PointerIsAligned(start, uint64))
135{space}+ {
/* ignore */ }
137{continuation} {
/* ignore */ }
140 /* report end of command */
153"%" {
return '%'; }
/* C version, also in Pg SQL */
155"<>" {
return NE_OP; }
156"!=" {
return NE_OP; }
/* C version, also in Pg SQL */
157"<=" {
return LE_OP; }
158">=" {
return GE_OP; }
159"<<" {
return LS_OP; }
160">>" {
return RS_OP; }
172{and} {
return AND_OP; }
173{or} {
return OR_OP; }
174{not} {
return NOT_OP; }
175{is} {
return IS_OP; }
176{isnull} {
return ISNULL_OP; }
177{notnull} {
return NOTNULL_OP; }
179{case} {
return CASE_KW; }
180{when} {
return WHEN_KW; }
181{then} {
return THEN_KW; }
182{else} {
return ELSE_KW; }
183{end} {
return END_KW; }
char * pg_strdup(const char *in)
190{null} {
return NULL_CONST; }
193 return BOOLEAN_CONST;
196 yylval->bval =
false;
197 return BOOLEAN_CONST;
199"9223372036854775808" {
201 * Special handling for PG_INT64_MIN, which can't
202 * accurately be represented here, as the minus sign is
203 * lexed separately and INT64_MIN can't be represented as
204 * a positive integer.
206 return MAXINT_PLUS_ONE_CONST;
212 return INTEGER_CONST;
void expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more)
bool strtoint64(const char *str, bool errorOK, int64 *result)
214{digit}+(\.{digit}*)?([eE][-+]?{digit}+)? {
bool strtodouble(const char *str, bool errorOK, double *dv)
220\.{digit}+([eE][-+]?{digit}+)? {
231{space}+ {
/* ignore */ }
233{continuation} {
/* ignore */ }
236 /* report end of command */
243 * must strdup yytext so that expr_yyerror_more doesn't
244 * change it while finding end of line
248 /* NOTREACHED, syntax_error calls exit() */
255 if (cur_state->buffer_stack == NULL)
256 return 0;
/* end of input reached */
259 * We were expanding a variable, so pop the inclusion
260 * stack and keep lexing
void psqlscan_select_top_buffer(PsqlScanState state)
void psqlscan_pop_buffer_stack(PsqlScanState state)
275 int error_detection_offset;
280 error_detection_offset--;
283 * While parsing an expression, we may not have collected the whole line
284 * yet from the input source. Lex till EOL so we can report whole line.
285 * (If we're at EOF, it's okay to call yylex() an extra time.)
289 while (
yylex(&lval, yyscanner))
293 /* Extract the line, trimming trailing newline if any */
303 * (The first argument is enforced by Bison to match the first argument of
304 * yyparse(), but it is not used here.)
313 * Collect a space-separated word from a backslash command and return it
314 * in word_buf, along with its starting string offset in *offset.
315 * Returns true if successful, false if at end of command.
323 /* Must be scanning already */
326 /* Set current output target */
327 state->output_buf = word_buf;
330 /* Set input source */
331 if (
state->buffer_stack != NULL)
332 yy_switch_to_buffer(
state->buffer_stack->buf,
state->scanner);
334 yy_switch_to_buffer(
state->scanbufhandle,
state->scanner);
336 /* Set start state */
337 state->start_state = INITIAL;
342 /* Save start offset of word, if any. */
349 *offset = end_offset - word_buf->
len;
355 * In case the caller returns to using the regular SQL lexer, reselect the
356 * appropriate initial state.
360 return (
bool) lexresult;
364 * Prepare to lex an expression via expr_yyparse().
366 * Returns the yyscan_t that is to be passed to expr_yyparse().
367 * (This is just state->scanner, but callers don't need to know that.)
371 const char *
source,
int lineno,
int start_offset,
374 /* Save error context info */
380 /* Must be scanning already */
383 /* Set current output target */
384 state->output_buf = NULL;
386 /* Set input source */
387 if (
state->buffer_stack != NULL)
388 yy_switch_to_buffer(
state->buffer_stack->buf,
state->scanner);
390 yy_switch_to_buffer(
state->scanbufhandle,
state->scanner);
392 /* Set start state */
393 state->start_state = EXPR;
395 return state->scanner;
399 * Finish lexing an expression.
407 * Reselect appropriate initial state for SQL lexer.
413 * Get a malloc'd copy of the lexer input string from start_offset
414 * to end of current lexer token. If chomp is true, drop any trailing
417 * We rely on the knowledge that flex modifies the scan buffer by storing
418 * a NUL at the end of the current token (yytext). Note that this might
419 * not work quite right if we were parsing a sub-buffer, but since pgbench
420 * never invokes that functionality, it doesn't matter. Also, this will
421 * give the wrong answer (the whole remainder of the input) if called
422 * before any yylex() call has been done.
430 const char *scanptr =
state->scanbuf + start_offset;
431 size_t slen = strlen(scanptr);
436 (scanptr[slen - 1] ==
'\n' || scanptr[slen - 1] ==
'\r'))
441 memcpy(result, scanptr, slen);
bool expr_lex_one_word(PsqlScanState state, PQExpBuffer word_buf, int *offset)
char * expr_scanner_get_substring(PsqlScanState state, int start_offset, bool chomp)
void expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner, const char *message)
int yylex(YYSTYPE *yylval_param, yyscan_t yyscanner)
void expr_scanner_finish(yyscan_t yyscanner)
yyscan_t expr_scanner_init(PsqlScanState state, const char *source, int lineno, int start_offset, const char *command)
void * pg_malloc(size_t size)
static rewind_source * source
void syntax_error(const char *source, int lineno, const char *line, const char *command, const char *msg, const char *more, int column)
void resetPQExpBuffer(PQExpBuffer str)
void psql_scan_get_location(PsqlScanState state, int *lineno, int *offset)
void psql_scan_reselect_sql_lexer(PsqlScanState state)