LCOV - code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_scanner.l (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 86.5 % 89 77
Test Date: 2026-03-16 00:15:06 Functions: 71.4 % 7 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %top{
       2              : /*
       3              :  * Scanner for plan advice
       4              :  *
       5              :  * Copyright (c) 2000-2026, PostgreSQL Global Development Group
       6              :  *
       7              :  * contrib/pg_plan_advice/pgpa_scanner.l
       8              :  */
       9              : #include "postgres.h"
      10              : 
      11              : #include "common/string.h"
      12              : #include "nodes/miscnodes.h"
      13              : #include "parser/scansup.h"
      14              : 
      15              : #include "pgpa_ast.h"
      16              : #include "pgpa_parser.h"
      17              : 
      18              : /*
      19              :  * Extra data that we pass around when during scanning.
      20              :  *
      21              :  * 'litbuf' is used to implement the <xd> exclusive state, which handles
      22              :  * double-quoted identifiers.
      23              :  */
      24              : typedef struct pgpa_yy_extra_type
      25              : {
      26              :     StringInfoData  litbuf;
      27              : } pgpa_yy_extra_type;
      28              : 
      29              : }
      30              : 
      31              : %{
      32              : /* LCOV_EXCL_START */
      33              : 
      34              : #define YY_DECL \
      35              :     extern int pgpa_yylex(union YYSTYPE *yylval_param, List **result, \
      36              :                           char **parse_error_msg_p, yyscan_t yyscanner)
      37              : 
      38              : /* No reason to constrain amount of data slurped */
      39              : #define YY_READ_BUF_SIZE 16777216
      40              : 
      41              : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
      42              : #undef fprintf
      43              : #define fprintf(file, fmt, msg)  fprintf_to_ereport(fmt, msg)
      44              : 
      45              : static void
      46            0 : fprintf_to_ereport(const char *fmt, const char *msg)
      47              : {
      48            0 :     ereport(ERROR, (errmsg_internal("%s", msg)));
      49              : }
      50              : %}
      51              : 
      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 noyyalloc
      61              : %option noyyrealloc
      62              : %option noyyfree
      63              : %option warn
      64              : %option prefix="pgpa_yy"
      65              : %option extra-type="pgpa_yy_extra_type *"
      66              : 
      67              : /*
      68              :  * What follows is a severely stripped-down version of the core scanner. We
      69              :  * only care about recognizing identifiers with or without identifier quoting
      70              :  * (i.e. double-quoting), decimal integers, and a small handful of other
      71              :  * things. Keep these rules in sync with src/backend/parser/scan.l. As in that
      72              :  * file, we use an exclusive state called 'xc' for C-style comments, and an
      73              :  * exclusive state called 'xd' for double-quoted identifiers.
      74              :  */
      75              : %x xc
      76              : %x xd
      77              : 
      78              : ident_start     [A-Za-z\200-\377_]
      79              : ident_cont      [A-Za-z\200-\377_0-9\$]
      80              : 
      81              : identifier      {ident_start}{ident_cont}*
      82              : 
      83              : decdigit        [0-9]
      84              : decinteger      {decdigit}(_?{decdigit})*
      85              : 
      86              : space           [ \t\n\r\f\v]
      87              : whitespace      {space}+
      88              : 
      89              : dquote          \"
      90              : xdstart         {dquote}
      91              : xdstop          {dquote}
      92              : xddouble        {dquote}{dquote}
      93              : xdinside        [^"]+
      94              : 
      95              : xcstart         \/\*
      96              : xcstop          \*+\/
      97              : xcinside        [^*/]+
      98              : 
      99              : %%
     100              : 
     101              : {whitespace}    { /* ignore */ }
     102          183 : 
     103          675 : {identifier}    {
     104              :                     char   *str;
     105              :                     bool    fail;
     106              :                     pgpa_advice_tag_type    tag;
     107              : 
     108              :                     /*
     109              :                      * Unlike the core scanner, we don't truncate identifiers
     110              :                      * here. There is no obvious reason to do so.
     111              :                      */
     112          675 :                     str = downcase_identifier(yytext, yyleng, false, false);
     113          675 :                     yylval->str = str;
     114              : 
     115              :                     /*
     116              :                      * If it's not a tag, just return TOK_IDENT; else, return
     117              :                      * a token type based on how further parsing should
     118              :                      * proceed.
     119              :                      */
     120          675 :                     tag = pgpa_parse_advice_tag(str, &fail);
     121          675 :                     if (fail)
     122          418 :                         return TOK_IDENT;
     123          257 :                     else if (tag == PGPA_TAG_JOIN_ORDER)
     124           40 :                         return TOK_TAG_JOIN_ORDER;
     125          217 :                     else if (tag == PGPA_TAG_INDEX_SCAN ||
     126              :                              tag == PGPA_TAG_INDEX_ONLY_SCAN)
     127           38 :                         return TOK_TAG_INDEX;
     128          179 :                     else if (tag == PGPA_TAG_SEQ_SCAN ||
     129          121 :                              tag == PGPA_TAG_TID_SCAN ||
     130          115 :                              tag == PGPA_TAG_BITMAP_HEAP_SCAN ||
     131              :                              tag == PGPA_TAG_NO_GATHER)
     132           76 :                         return TOK_TAG_SIMPLE;
     133              :                     else
     134          103 :                         return TOK_TAG_GENERIC;
     135              :                 }
     136              : 
     137            9 : {decinteger}    {
     138              :                     char   *endptr;
     139              : 
     140            9 :                     errno = 0;
     141            9 :                     yylval->integer = strtoint(yytext, &endptr, 10);
     142            9 :                     if (*endptr != '\0' || errno == ERANGE)
     143            0 :                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     144              :                                      "integer out of range");
     145            9 :                     return TOK_INTEGER;
     146              :                 }
     147              : 
     148           17 : {xcstart}       {
     149           17 :                     BEGIN(xc);
     150              :                 }
     151           17 : 
     152           20 : {xdstart}       {
     153           20 :                     BEGIN(xd);
     154           20 :                     resetStringInfo(&yyextra->litbuf);
     155              :                 }
     156           20 : 
     157          634 : .               { return yytext[0]; }
     158              : 
     159           15 : <xc>{xcstop}  {
     160           15 :                     BEGIN(INITIAL);
     161              :                 }
     162           15 : 
     163           10 : <xc>{xcinside}    {
     164              :                     /* discard multiple characters without slash or asterisk */
     165              :                 }
     166           10 : 
     167            4 : <xc>.         {
     168              :                     /*
     169              :                      * Discard any single character. flex prefers longer
     170              :                      * matches, so this rule will never be picked when we could
     171              :                      * have matched xcstop.
     172              :                      *
     173              :                      * NB: At present, we don't bother to support nested
     174              :                      * C-style comments here, but this logic could be extended
     175              :                      * if that restriction poses a problem.
     176              :                      */
     177              :                 }
     178            4 : 
     179            2 : <xc><<EOF>>       {
     180            2 :                     BEGIN(INITIAL);
     181            2 :                     pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     182              :                                  "unterminated comment");
     183              :                 }
     184            2 : 
     185           19 : <xd>{xdstop}  {
     186           19 :                     BEGIN(INITIAL);
     187           19 :                     if (yyextra->litbuf.len == 0)
     188            1 :                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     189              :                                      "zero-length delimited identifier");
     190           19 :                     yylval->str = pstrdup(yyextra->litbuf.data);
     191           19 :                     return TOK_IDENT;
     192              :                 }
     193              : 
     194            0 : <xd>{xddouble}    {
     195            0 :                     appendStringInfoChar(&yyextra->litbuf, '"');
     196              :                 }
     197            0 : 
     198           18 : <xd>{xdinside}    {
     199           18 :                     appendBinaryStringInfo(&yyextra->litbuf, yytext, yyleng);
     200              :                 }
     201           18 : 
     202            1 : <xd><<EOF>>       {
     203            1 :                     BEGIN(INITIAL);
     204            1 :                     pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     205              :                                  "unterminated quoted identifier");
     206              :                 }
     207            1 : 
     208            0 : %%
     209              : 
     210              : /* LCOV_EXCL_STOP */
     211              : 
     212              : /*
     213              :  * Handler for errors while scanning or parsing advice.
     214              :  *
     215              :  * bison passes the error message to us via 'message', and the context is
     216              :  * available via the 'yytext' macro. We assemble those values into a final
     217              :  * error text and then arrange to pass it back to the caller of pgpa_yyparse()
     218              :  * by storing it into *parse_error_msg_p.
     219              :  */
     220              : void
     221           18 : pgpa_yyerror(List **result, char **parse_error_msg_p, yyscan_t yyscanner,
     222              :              const char *message)
     223              : {
     224           18 :     struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;   /* needed for yytext
     225              :                                                              * macro */
     226              : 
     227              : 
     228              :     /* report only the first error in a parse operation */
     229           18 :     if (*parse_error_msg_p)
     230            1 :         return;
     231              : 
     232           17 :     if (yytext[0])
     233           11 :         *parse_error_msg_p = psprintf("%s at or near \"%s\"", message, yytext);
     234              :     else
     235            6 :         *parse_error_msg_p = psprintf("%s at end of input", message);
     236              : }
     237              : 
     238              : /*
     239              :  * Initialize the advice scanner.
     240              :  *
     241              :  * This should be called before parsing begins.
     242              :  */
     243              : void
     244          243 : pgpa_scanner_init(const char *str, yyscan_t *yyscannerp)
     245              : {
     246              :     yyscan_t    yyscanner;
     247          243 :     pgpa_yy_extra_type  *yyext = palloc0_object(pgpa_yy_extra_type);
     248              : 
     249          243 :     if (yylex_init(yyscannerp) != 0)
     250            0 :         elog(ERROR, "yylex_init() failed: %m");
     251              : 
     252          243 :     yyscanner = *yyscannerp;
     253              : 
     254          243 :     initStringInfo(&yyext->litbuf);
     255          243 :     pgpa_yyset_extra(yyext, yyscanner);
     256              : 
     257          243 :     yy_scan_string(str, yyscanner);
     258          243 : }
     259              : 
     260              : 
     261              : /*
     262              :  * Shut down the advice scanner.
     263              :  *
     264              :  * This should be called after parsing is complete.
     265              :  */
     266              : void
     267          243 : pgpa_scanner_finish(yyscan_t yyscanner)
     268              : {
     269          243 :     yylex_destroy(yyscanner);
     270          243 : }
     271              : 
     272              : /*
     273              :  * Interface functions to make flex use palloc() instead of malloc().
     274              :  * It'd be better to make these static, but flex insists otherwise.
     275              :  */
     276              : 
     277              : void *
     278          972 : yyalloc(yy_size_t size, yyscan_t yyscanner)
     279              : {
     280          972 :     return palloc(size);
     281              : }
     282              : 
     283              : void *
     284            0 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
     285              : {
     286            0 :     if (ptr)
     287            0 :         return repalloc(ptr, size);
     288              :     else
     289            0 :         return palloc(size);
     290              : }
     291              : 
     292              : void
     293         1215 : yyfree(void *ptr, yyscan_t yyscanner)
     294              : {
     295         1215 :     if (ptr)
     296          972 :         pfree(ptr);
     297         1215 : }
        

Generated by: LCOV version 2.0-1