LCOV - code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_scanner.l (source / functions) Coverage Total Hit
Test: Code coverage Lines: 74.3 % 35 26
Test Date: 2026-01-26 10:56:24 Functions: 71.4 % 7 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 40.0 % 20 8

             Branch data     Line data    Source code
       1                 :             : %top{
       2                 :             : /*
       3                 :             :  * Scanner for plan advice
       4                 :             :  *
       5                 :             :  * Copyright (c) 2000-2025, 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                 :           0 : }
      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                 :             : 
     103                 :             : {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                 :             :                                         str = downcase_identifier(yytext, yyleng, false, false);
     113                 :             :                                         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                 :             :                                         tag = pgpa_parse_advice_tag(str, &fail);
     121                 :             :                                         if (fail)
     122                 :             :                                                 return TOK_IDENT;
     123                 :             :                                         else if (tag == PGPA_TAG_JOIN_ORDER)
     124                 :             :                                                 return TOK_TAG_JOIN_ORDER;
     125                 :             :                                         else if (tag == PGPA_TAG_INDEX_SCAN ||
     126                 :             :                                                          tag == PGPA_TAG_INDEX_ONLY_SCAN)
     127                 :             :                                                 return TOK_TAG_INDEX;
     128                 :             :                                         else if (tag == PGPA_TAG_BITMAP_HEAP_SCAN)
     129                 :             :                                                 return TOK_TAG_BITMAP;
     130                 :             :                                         else if (tag == PGPA_TAG_SEQ_SCAN ||
     131                 :             :                                                          tag == PGPA_TAG_TID_SCAN ||
     132                 :             :                                                          tag == PGPA_TAG_NO_GATHER)
     133                 :             :                                                 return TOK_TAG_SIMPLE;
     134                 :             :                                         else
     135                 :             :                                                 return TOK_TAG_GENERIC;
     136                 :             :                                 }
     137                 :             : 
     138                 :             : {decinteger}    {
     139                 :             :                                         char   *endptr;
     140                 :             : 
     141                 :             :                                         errno = 0;
     142                 :             :                                         yylval->integer = strtoint(yytext, &endptr, 10);
     143                 :             :                                         if (*endptr != '\0' || errno == ERANGE)
     144                 :             :                                                 pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     145                 :             :                                                                          "integer out of range");
     146                 :             :                                         return TOK_INTEGER;
     147                 :             :                                 }
     148                 :             : 
     149                 :             : {xcstart}               {
     150                 :             :                                         BEGIN(xc);
     151                 :             :                                 }
     152                 :             : 
     153                 :             : {xdstart}               {
     154                 :             :                                         BEGIN(xd);
     155                 :             :                                         resetStringInfo(&yyextra->litbuf);
     156                 :             :                                 }
     157                 :             : 
     158                 :             : "||"                  { return TOK_OR; }
     159                 :             : 
     160                 :             : "&&"                  { return TOK_AND; }
     161                 :             : 
     162                 :             : .                               { return yytext[0]; }
     163                 :             : 
     164                 :             : <xc>{xcstop}      {
     165                 :             :                                         BEGIN(INITIAL);
     166                 :             :                                 }
     167                 :             : 
     168                 :             : <xc>{xcinside}    {
     169                 :             :                                         /* discard multiple characters without slash or asterisk */
     170                 :             :                                 }
     171                 :             : 
     172                 :             : <xc>.                     {
     173                 :             :                                         /*
     174                 :             :                                          * Discard any single character. flex prefers longer
     175                 :             :                                          * matches, so this rule will never be picked when we could
     176                 :             :                                          * have matched xcstop.
     177                 :             :                                          *
     178                 :             :                                          * NB: At present, we don't bother to support nested
     179                 :             :                                          * C-style comments here, but this logic could be extended
     180                 :             :                                          * if that restriction poses a problem.
     181                 :             :                                          */
     182                 :             :                                 }
     183                 :             : 
     184                 :             : <xc><<EOF>>           {
     185                 :             :                                         BEGIN(INITIAL);
     186                 :             :                                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     187                 :             :                                                                  "unterminated comment");
     188                 :             :                                 }
     189                 :             : 
     190                 :             : <xd>{xdstop}      {
     191                 :             :                                         BEGIN(INITIAL);
     192                 :             :                                         if (yyextra->litbuf.len == 0)
     193                 :             :                                                 pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     194                 :             :                                                                          "zero-length delimited identifier");
     195                 :             :                                         yylval->str = pstrdup(yyextra->litbuf.data);
     196                 :             :                                         return TOK_IDENT;
     197                 :             :                                 }
     198                 :             : 
     199                 :             : <xd>{xddouble}    {
     200                 :             :                                         appendStringInfoChar(&yyextra->litbuf, '"');
     201                 :             :                                 }
     202                 :             : 
     203                 :             : <xd>{xdinside}    {
     204                 :             :                                         appendBinaryStringInfo(&yyextra->litbuf, yytext, yyleng);
     205                 :             :                                 }
     206                 :             : 
     207                 :             : <xd><<EOF>>           {
     208                 :             :                                         BEGIN(INITIAL);
     209                 :             :                                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     210                 :             :                                                                  "unterminated quoted identifier");
     211                 :             :                                 }
     212                 :             : 
     213                 :             : %%
     214                 :             : 
     215                 :             : /* LCOV_EXCL_STOP */
     216                 :             : 
     217                 :             : /*
     218                 :             :  * Handler for errors while scanning or parsing advice.
     219                 :             :  *
     220                 :             :  * bison passes the error message to us via 'message', and the context is
     221                 :             :  * available via the 'yytext' macro. We assemble those values into a final
     222                 :             :  * error text and then arrange to pass it back to the caller of pgpa_yyparse()
     223                 :             :  * by storing it into *parse_error_msg_p.
     224                 :             :  */
     225                 :             : void
     226                 :          18 : pgpa_yyerror(List **result, char **parse_error_msg_p, yyscan_t yyscanner,
     227                 :             :                          const char *message)
     228                 :             : {
     229                 :          18 :         struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;   /* needed for yytext
     230                 :             :                                                                                                                          * macro */
     231                 :             : 
     232                 :             : 
     233                 :             :         /* report only the first error in a parse operation */
     234         [ +  + ]:          18 :         if (*parse_error_msg_p)
     235                 :           1 :                 return;
     236                 :             : 
     237         [ +  + ]:          17 :         if (yytext[0])
     238                 :          11 :                 *parse_error_msg_p = psprintf("%s at or near \"%s\"", message, yytext);
     239                 :             :         else
     240                 :           6 :                 *parse_error_msg_p = psprintf("%s at end of input", message);
     241         [ -  + ]:          18 : }
     242                 :             : 
     243                 :             : /*
     244                 :             :  * Initialize the advice scanner.
     245                 :             :  *
     246                 :             :  * This should be called before parsing begins.
     247                 :             :  */
     248                 :             : void
     249                 :       43647 : pgpa_scanner_init(const char *str, yyscan_t *yyscannerp)
     250                 :             : {
     251                 :       43647 :         yyscan_t        yyscanner;
     252                 :       43647 :         pgpa_yy_extra_type      *yyext = palloc0_object(pgpa_yy_extra_type);
     253                 :             : 
     254         [ +  - ]:       43647 :         if (yylex_init(yyscannerp) != 0)
     255   [ #  #  #  # ]:           0 :                 elog(ERROR, "yylex_init() failed: %m");
     256                 :             : 
     257                 :       43647 :         yyscanner = *yyscannerp;
     258                 :             : 
     259                 :       43647 :         initStringInfo(&yyext->litbuf);
     260                 :       43647 :         pgpa_yyset_extra(yyext, yyscanner);
     261                 :             : 
     262                 :       43647 :         yy_scan_string(str, yyscanner);
     263                 :       43647 : }
     264                 :             : 
     265                 :             : 
     266                 :             : /*
     267                 :             :  * Shut down the advice scanner.
     268                 :             :  *
     269                 :             :  * This should be called after parsing is complete.
     270                 :             :  */
     271                 :             : void
     272                 :       43647 : pgpa_scanner_finish(yyscan_t yyscanner)
     273                 :             : {
     274                 :       43647 :         yylex_destroy(yyscanner);
     275                 :       43647 : }
     276                 :             : 
     277                 :             : /*
     278                 :             :  * Interface functions to make flex use palloc() instead of malloc().
     279                 :             :  * It'd be better to make these static, but flex insists otherwise.
     280                 :             :  */
     281                 :             : 
     282                 :             : void *
     283                 :      174588 : yyalloc(yy_size_t size, yyscan_t yyscanner)
     284                 :             : {
     285                 :      174588 :         return palloc(size);
     286                 :             : }
     287                 :             : 
     288                 :             : void *
     289                 :           0 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
     290                 :             : {
     291         [ #  # ]:           0 :         if (ptr)
     292                 :           0 :                 return repalloc(ptr, size);
     293                 :             :         else
     294                 :           0 :                 return palloc(size);
     295                 :           0 : }
     296                 :             : 
     297                 :             : void
     298                 :      218235 : yyfree(void *ptr, yyscan_t yyscanner)
     299                 :             : {
     300         [ +  + ]:      218235 :         if (ptr)
     301                 :      174588 :                 pfree(ptr);
     302                 :      218235 : }
        

Generated by: LCOV version 2.3.2-1