LCOV - code coverage report
Current view: top level - src/bin/pgbench - exprscan.l (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 68 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %top{
       2              : /*-------------------------------------------------------------------------
       3              :  *
       4              :  * exprscan.l
       5              :  *        lexical scanner for pgbench backslash commands
       6              :  *
       7              :  * This lexer supports two operating modes:
       8              :  *
       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).
      11              :  *
      12              :  * In EXPR state, lex for the simple expression syntax of exprparse.y.
      13              :  *
      14              :  * In either mode, stop upon hitting newline or end of string.
      15              :  *
      16              :  * Note that this lexer operates within the framework created by psqlscan.l,
      17              :  *
      18              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      19              :  * Portions Copyright (c) 1994, Regents of the University of California
      20              :  *
      21              :  * src/bin/pgbench/exprscan.l
      22              :  *
      23              :  *-------------------------------------------------------------------------
      24              :  */
      25              : #include "postgres_fe.h"
      26              : 
      27              : /*
      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.
      31              :  */
      32              : #include "fe_utils/psqlscan_int.h"
      33              : #include "pgbench.h"
      34              : #include "exprparse.h"
      35              : }
      36              : 
      37              : %{
      38              : /* context information for reporting errors in expressions */
      39              : static const char *expr_source = NULL;
      40              : static int      expr_lineno = 0;
      41              : static int      expr_start_offset = 0;
      42              : static const char *expr_command = NULL;
      43              : 
      44              : /* indicates whether last yylex() call read a newline */
      45              : static bool last_was_newline = false;
      46              : 
      47              : /* LCOV_EXCL_START */
      48              : 
      49              : %}
      50              : 
      51              : /* Except for the prefix, these options should match psqlscan.l */
      52              : %option reentrant
      53              : %option bison-bridge
      54              : %option 8bit
      55              : %option never-interactive
      56              : %option nodefault
      57              : %option noinput
      58              : %option nounput
      59              : %option noyywrap
      60              : %option warn
      61              : %option prefix="expr_yy"
      62              : 
      63              : /* Character classes */
      64              : alpha                   [a-zA-Z\200-\377_]
      65              : digit                   [0-9]
      66              : alnum                   [A-Za-z\200-\377_0-9]
      67              : /* {space} + {nonspace} + {newline} should cover all characters */
      68              : space                   [ \t\r\f\v]
      69              : nonspace                [^ \t\r\f\v\n]
      70              : newline                 [\n]
      71              : 
      72              : /* Line continuation marker */
      73              : continuation    \\\r?{newline}
      74              : 
      75              : /* case insensitive keywords */
      76              : and                             [Aa][Nn][Dd]
      77              : or                              [Oo][Rr]
      78              : not                             [Nn][Oo][Tt]
      79              : case                    [Cc][Aa][Ss][Ee]
      80              : when                    [Ww][Hh][Ee][Nn]
      81              : then                    [Tt][Hh][Ee][Nn]
      82              : else                    [Ee][Ll][Ss][Ee]
      83              : end                             [Ee][Nn][Dd]
      84              : true                    [Tt][Rr][Uu][Ee]
      85              : false                   [Ff][Aa][Ll][Ss][Ee]
      86              : null                    [Nn][Uu][Ll][Ll]
      87              : is                              [Ii][Ss]
      88              : isnull                  [Ii][Ss][Nn][Uu][Ll][Ll]
      89              : notnull                 [Nn][Oo][Tt][Nn][Uu][Ll][Ll]
      90              : 
      91              : /* Exclusive states */
      92              : %x EXPR
      93              : 
      94              : %%
      95              : 
      96              : %{
      97              :                 /* Declare some local variables inside yylex(), for convenience */
      98              :                 PsqlScanState cur_state = yyextra;
      99              : 
     100              :                 /*
     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.
     106              :                  */
     107              :                 BEGIN(cur_state->start_state);
     108              : 
     109              :                 /* Reset was-newline flag */
     110              :                 last_was_newline = false;
     111              : %}
     112              : 
     113              :         /* INITIAL state */
     114              : 
     115              : {nonspace}+             {
     116              :                                         /* Found a word, emit and return it */
     117              :                                         psqlscan_emit(cur_state, yytext, yyleng);
     118              :                                         return 1;
     119              :                                 }
     120              : 
     121              :         /*
     122              :          * We need this rule to avoid returning "word\" instead of recognizing
     123              :          * a continuation marker just after a word:
     124              :          */
     125              : {nonspace}+{continuation}       {
     126              :                                         /* Found "word\\\r?\n", emit and return just "word" */
     127              :                                         int             wordlen = yyleng - 2;
     128              :                                         if (yytext[wordlen] == '\r')
     129              :                                                 wordlen--;
     130              :                                         Assert(yytext[wordlen] == '\\');
     131              :                                         psqlscan_emit(cur_state, yytext, wordlen);
     132              :                                         return 1;
     133              :                                 }
     134              : 
     135              : {space}+                { /* ignore */ }
     136              : 
     137              : {continuation}  { /* ignore */ }
     138              : 
     139              : {newline}               {
     140              :                                         /* report end of command */
     141              :                                         last_was_newline = true;
     142              :                                         return 0;
     143              :                                 }
     144              : 
     145              :         /* EXPR state */
     146              : 
     147              : <EXPR>{
     148              : 
     149              : "+"                           { return '+'; }
     150              : "-"                           { return '-'; }
     151              : "*"                           { return '*'; }
     152              : "/"                           { return '/'; }
     153              : "%"                           { return '%'; } /* C version, also in Pg SQL */
     154              : "="                           { return '='; }
     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; }
     161              : "<"                                { return '<'; }
     162              : ">"                                { return '>'; }
     163              : "|"                           { return '|'; }
     164              : "&"                               { return '&'; }
     165              : "#"                           { return '#'; }
     166              : "~"                           { return '~'; }
     167              : 
     168              : "("                           { return '('; }
     169              : ")"                           { return ')'; }
     170              : ","                           { return ','; }
     171              : 
     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; }
     178              : 
     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; }
     184              : 
     185              : :{alnum}+               {
     186              :                                         yylval->str = pg_strdup(yytext + 1);
     187              :                                         return VARIABLE;
     188              :                                 }
     189              : 
     190              : {null}                  { return NULL_CONST; }
     191              : {true}                  {
     192              :                                         yylval->bval = true;
     193              :                                         return BOOLEAN_CONST;
     194              :                                 }
     195              : {false}                 {
     196              :                                         yylval->bval = false;
     197              :                                         return BOOLEAN_CONST;
     198              :                                 }
     199              : "9223372036854775808" {
     200              :                                         /*
     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.
     205              :                                          */
     206              :                                         return MAXINT_PLUS_ONE_CONST;
     207              :                                 }
     208              : {digit}+                {
     209              :                                         if (!strtoint64(yytext, true, &yylval->ival))
     210              :                                                 expr_yyerror_more(yyscanner, "bigint constant overflow",
     211              :                                                                                   strdup(yytext));
     212              :                                         return INTEGER_CONST;
     213              :                                 }
     214              : {digit}+(\.{digit}*)?([eE][-+]?{digit}+)?       {
     215              :                                         if (!strtodouble(yytext, true, &yylval->dval))
     216              :                                                 expr_yyerror_more(yyscanner, "double constant overflow",
     217              :                                                                                   strdup(yytext));
     218              :                                         return DOUBLE_CONST;
     219              :                                 }
     220              : \.{digit}+([eE][-+]?{digit}+)?  {
     221              :                                         if (!strtodouble(yytext, true, &yylval->dval))
     222              :                                                 expr_yyerror_more(yyscanner, "double constant overflow",
     223              :                                                                                   strdup(yytext));
     224              :                                         return DOUBLE_CONST;
     225              :                                 }
     226              : {alpha}{alnum}* {
     227              :                                         yylval->str = pg_strdup(yytext);
     228              :                                         return FUNCTION;
     229              :                                 }
     230              : 
     231              : {space}+                { /* ignore */ }
     232              : 
     233              : {continuation}  { /* ignore */ }
     234              : 
     235              : {newline}               {
     236              :                                         /* report end of command */
     237              :                                         last_was_newline = true;
     238              :                                         return 0;
     239              :                                 }
     240              : 
     241              : .                               {
     242              :                                         /*
     243              :                                          * must strdup yytext so that expr_yyerror_more doesn't
     244              :                                          * change it while finding end of line
     245              :                                          */
     246              :                                         expr_yyerror_more(yyscanner, "unexpected character",
     247              :                                                                           pg_strdup(yytext));
     248              :                                         /* NOTREACHED, syntax_error calls exit() */
     249              :                                         return 0;
     250              :                                 }
     251              : 
     252              : }
     253              : 
     254              : <<EOF>>                     {
     255              :                                         if (cur_state->buffer_stack == NULL)
     256              :                                                 return 0;                       /* end of input reached */
     257              : 
     258              :                                         /*
     259              :                                          * We were expanding a variable, so pop the inclusion
     260              :                                          * stack and keep lexing
     261              :                                          */
     262              :                                         psqlscan_pop_buffer_stack(cur_state);
     263              :                                         psqlscan_select_top_buffer(cur_state);
     264              :                                 }
     265              : 
     266              : %%
     267              : 
     268              : /* LCOV_EXCL_STOP */
     269              : 
     270              : void
     271              : expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more)
     272            0 : {
     273              :         PsqlScanState state = yyget_extra(yyscanner);
     274            0 :         int                     lineno;
     275            0 :         int                     error_detection_offset;
     276            0 :         YYSTYPE         lval;
     277            0 :         char       *full_line;
     278            0 : 
     279              :         psql_scan_get_location(state, &lineno, &error_detection_offset);
     280            0 :         error_detection_offset--;
     281            0 : 
     282              :         /*
     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.)
     286              :          */
     287              :         if (!last_was_newline)
     288            0 :         {
     289              :                 while (yylex(&lval, yyscanner))
     290            0 :                          /* skip */ ;
     291              :         }
     292            0 : 
     293              :         /* Extract the line, trimming trailing newline if any */
     294              :         full_line = expr_scanner_get_substring(state,
     295            0 :                                                                                    expr_start_offset,
     296            0 :                                                                                    true);
     297              : 
     298              :         syntax_error(expr_source, expr_lineno, full_line, expr_command,
     299            0 :                                  message, more, error_detection_offset - expr_start_offset);
     300            0 : }
     301              : 
     302              : /*
     303              :  * (The first argument is enforced by Bison to match the first argument of
     304              :  * yyparse(), but it is not used here.)
     305              :  */
     306              : void
     307              : expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner, const char *message)
     308            0 : {
     309              :         expr_yyerror_more(yyscanner, message, NULL);
     310            0 : }
     311              : 
     312              : /*
     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.
     316              :  */
     317              : bool
     318              : expr_lex_one_word(PsqlScanState state, PQExpBuffer word_buf, int *offset)
     319            0 : {
     320              :         int                     lexresult;
     321            0 :         YYSTYPE         lval;
     322            0 : 
     323              :         /* Must be scanning already */
     324              :         Assert(state->scanbufhandle != NULL);
     325            0 : 
     326              :         /* Set current output target */
     327              :         state->output_buf = word_buf;
     328            0 :         resetPQExpBuffer(word_buf);
     329            0 : 
     330              :         /* Set input source */
     331              :         if (state->buffer_stack != NULL)
     332            0 :                 yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     333            0 :         else
     334              :                 yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     335            0 : 
     336              :         /* Set start state */
     337              :         state->start_state = INITIAL;
     338            0 : 
     339              :         /* And lex. */
     340              :         lexresult = yylex(&lval, state->scanner);
     341            0 : 
     342              :         /* Save start offset of word, if any. */
     343              :         if (lexresult)
     344            0 :         {
     345              :                 int                     lineno;
     346            0 :                 int                     end_offset;
     347            0 : 
     348              :                 psql_scan_get_location(state, &lineno, &end_offset);
     349            0 :                 *offset = end_offset - word_buf->len;
     350            0 :         }
     351            0 :         else
     352              :                 *offset = -1;
     353            0 : 
     354              :         /*
     355              :          * In case the caller returns to using the regular SQL lexer, reselect the
     356              :          * appropriate initial state.
     357              :          */
     358              :         psql_scan_reselect_sql_lexer(state);
     359            0 : 
     360              :         return (bool) lexresult;
     361            0 : }
     362            0 : 
     363              : /*
     364              :  * Prepare to lex an expression via expr_yyparse().
     365              :  *
     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.)
     368              :  */
     369              : yyscan_t
     370              : expr_scanner_init(PsqlScanState state,
     371            0 :                                   const char *source, int lineno, int start_offset,
     372              :                                   const char *command)
     373              : {
     374              :         /* Save error context info */
     375              :         expr_source = source;
     376            0 :         expr_lineno = lineno;
     377            0 :         expr_start_offset = start_offset;
     378            0 :         expr_command = command;
     379            0 : 
     380              :         /* Must be scanning already */
     381              :         Assert(state->scanbufhandle != NULL);
     382            0 : 
     383              :         /* Set current output target */
     384              :         state->output_buf = NULL;
     385            0 : 
     386              :         /* Set input source */
     387              :         if (state->buffer_stack != NULL)
     388            0 :                 yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
     389            0 :         else
     390              :                 yy_switch_to_buffer(state->scanbufhandle, state->scanner);
     391            0 : 
     392              :         /* Set start state */
     393              :         state->start_state = EXPR;
     394            0 : 
     395              :         return state->scanner;
     396            0 : }
     397              : 
     398              : /*
     399              :  * Finish lexing an expression.
     400              :  */
     401              : void
     402              : expr_scanner_finish(yyscan_t yyscanner)
     403            0 : {
     404              :         PsqlScanState state = yyget_extra(yyscanner);
     405            0 : 
     406              :         /*
     407              :          * Reselect appropriate initial state for SQL lexer.
     408              :          */
     409              :         psql_scan_reselect_sql_lexer(state);
     410            0 : }
     411            0 : 
     412              : /*
     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
     415              :  * newline(s).
     416              :  *
     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.
     423              :  */
     424              : char *
     425              : expr_scanner_get_substring(PsqlScanState state,
     426            0 :                                                    int start_offset,
     427              :                                                    bool chomp)
     428              : {
     429              :         char       *result;
     430            0 :         const char *scanptr = state->scanbuf + start_offset;
     431            0 :         size_t          slen = strlen(scanptr);
     432            0 : 
     433              :         if (chomp)
     434            0 :         {
     435              :                 while (slen > 0 &&
     436            0 :                            (scanptr[slen - 1] == '\n' || scanptr[slen - 1] == '\r'))
     437            0 :                         slen--;
     438            0 :         }
     439            0 : 
     440              :         result = (char *) pg_malloc(slen + 1);
     441            0 :         memcpy(result, scanptr, slen);
     442            0 :         result[slen] = '\0';
     443            0 : 
     444              :         return result;
     445            0 : }
     446            0 : /* /Users/rhaas/pgsql/src/bin/pgbench/exprscan.l not long enough */
        

Generated by: LCOV version 2.3.2-1