LCOV - code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_scanner.l (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 88.3 % 94 83
Test Date: 2026-07-25 22:15:46 Functions: 71.4 % 7 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 80.6 % 36 29

             Branch data     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 "nodes/miscnodes.h"
      12                 :             : #include "parser/scansup.h"
      13                 :             : #include "utils/builtins.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                 :             : integer_junk    {decinteger}{identifier}
      86                 :             : 
      87                 :             : space           [ \t\n\r\f\v]
      88                 :             : whitespace      {space}+
      89                 :             : 
      90                 :             : dquote          \"
      91                 :             : xdstart         {dquote}
      92                 :             : xdstop          {dquote}
      93                 :             : xddouble        {dquote}{dquote}
      94                 :             : xdinside        [^"]+
      95                 :             : 
      96                 :             : xcstart         \/\*
      97                 :             : xcstop          \*+\/
      98                 :             : xcinside        [^*/]+
      99                 :             : 
     100                 :             : %%
     101                 :             : 
     102                 :             : {whitespace}    { /* ignore */ }
     103                 :      131774 : 
     104                 :      332063 : {identifier}    {
     105                 :             :                     char   *str;
     106                 :             :                     bool    fail;
     107                 :             :                     pgpa_advice_tag_type    tag;
     108                 :             : 
     109                 :             :                     /*
     110                 :             :                      * Unlike the core scanner, we don't truncate identifiers
     111                 :             :                      * here. There is no obvious reason to do so.
     112                 :             :                      */
     113                 :      332063 :                     str = downcase_identifier(yytext, yyleng, false, false);
     114                 :      332063 :                     yylval->str = str;
     115                 :             : 
     116                 :             :                     /*
     117                 :             :                      * If it's not a tag, just return TOK_IDENT; else, return
     118                 :             :                      * a token type based on how further parsing should
     119                 :             :                      * proceed.
     120                 :             :                      */
     121                 :      332063 :                     tag = pgpa_parse_advice_tag(str, &fail);
     122         [ +  + ]:      332063 :                     if (fail)
     123                 :      236513 :                         return TOK_IDENT;
     124         [ +  + ]:       95550 :                     else if (tag == PGPA_TAG_JOIN_ORDER)
     125                 :       10983 :                         return TOK_TAG_JOIN_ORDER;
     126   [ +  +  +  + ]:       84567 :                     else if (tag == PGPA_TAG_INDEX_SCAN ||
     127                 :             :                              tag == PGPA_TAG_INDEX_ONLY_SCAN)
     128                 :        9045 :                         return TOK_TAG_INDEX;
     129   [ +  +  +  + ]:       75522 :                     else if (tag == PGPA_TAG_SEQ_SCAN ||
     130         [ +  + ]:       58695 :                              tag == PGPA_TAG_TID_SCAN ||
     131         [ +  + ]:       56119 :                              tag == PGPA_TAG_BITMAP_HEAP_SCAN ||
     132         [ +  + ]:       12634 :                              tag == PGPA_TAG_NO_GATHER ||
     133                 :             :                              tag == PGPA_TAG_DO_NOT_SCAN)
     134                 :       63070 :                         return TOK_TAG_SIMPLE;
     135                 :             :                     else
     136                 :       12452 :                         return TOK_TAG_GENERIC;
     137                 :             :                 }
     138                 :             : 
     139                 :        3436 : {decinteger}    {
     140                 :        3436 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
     141                 :             : 
     142                 :        3436 :                     yylval->integer = pg_strtoint32_safe(yytext,
     143                 :             :                                                          (Node *) &escontext);
     144         [ +  + ]:        3436 :                     if (escontext.error_occurred)
     145                 :           1 :                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     146                 :             :                                      "integer out of range");
     147                 :        3436 :                     return TOK_INTEGER;
     148                 :             :                 }
     149                 :             : 
     150                 :           1 : {integer_junk}  {
     151                 :           1 :                     BEGIN(INITIAL);
     152                 :           1 :                     pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     153                 :             :                                  "trailing junk after numeric literal");
     154                 :             :                 }
     155                 :           1 : 
     156                 :          17 : {xcstart}       {
     157                 :          17 :                     BEGIN(xc);
     158                 :             :                 }
     159                 :          17 : 
     160                 :       20466 : {xdstart}       {
     161                 :       20466 :                     BEGIN(xd);
     162                 :       20466 :                     resetStringInfo(&yyextra->litbuf);
     163                 :             :                 }
     164                 :       20466 : 
     165                 :      278661 : .               { return yytext[0]; }
     166                 :             : 
     167                 :          15 : <xc>{xcstop}  {
     168                 :          15 :                     BEGIN(INITIAL);
     169                 :             :                 }
     170                 :          15 : 
     171                 :          10 : <xc>{xcinside}    {
     172                 :             :                     /* discard multiple characters without slash or asterisk */
     173                 :             :                 }
     174                 :          10 : 
     175                 :           4 : <xc>.         {
     176                 :             :                     /*
     177                 :             :                      * Discard any single character. flex prefers longer
     178                 :             :                      * matches, so this rule will never be picked when we could
     179                 :             :                      * have matched xcstop.
     180                 :             :                      *
     181                 :             :                      * NB: At present, we don't bother to support nested
     182                 :             :                      * C-style comments here, but this logic could be extended
     183                 :             :                      * if that restriction poses a problem.
     184                 :             :                      */
     185                 :             :                 }
     186                 :           4 : 
     187                 :           2 : <xc><<EOF>>       {
     188                 :           2 :                     BEGIN(INITIAL);
     189                 :           2 :                     pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     190                 :             :                                  "unterminated comment");
     191                 :             :                 }
     192                 :           2 : 
     193                 :       20465 : <xd>{xdstop}  {
     194                 :       20465 :                     BEGIN(INITIAL);
     195         [ +  + ]:       20465 :                     if (yyextra->litbuf.len == 0)
     196                 :           1 :                         pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     197                 :             :                                      "zero-length delimited identifier");
     198                 :       20465 :                     yylval->str = pstrdup(yyextra->litbuf.data);
     199                 :       20465 :                     return TOK_IDENT;
     200                 :             :                 }
     201                 :             : 
     202                 :           0 : <xd>{xddouble}    {
     203                 :           0 :                     appendStringInfoChar(&yyextra->litbuf, '"');
     204                 :             :                 }
     205                 :           0 : 
     206                 :       20464 : <xd>{xdinside}    {
     207                 :       20464 :                     appendBinaryStringInfo(&yyextra->litbuf, yytext, yyleng);
     208                 :             :                 }
     209                 :       20464 : 
     210                 :           1 : <xd><<EOF>>       {
     211                 :           1 :                     BEGIN(INITIAL);
     212                 :           1 :                     pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     213                 :             :                                  "unterminated quoted identifier");
     214                 :             :                 }
     215                 :           1 : 
     216                 :           0 : %%
     217                 :             : 
     218                 :             : /* LCOV_EXCL_STOP */
     219                 :             : 
     220                 :             : /*
     221                 :             :  * Handler for errors while scanning or parsing advice.
     222                 :             :  *
     223                 :             :  * bison passes the error message to us via 'message', and the context is
     224                 :             :  * available via the 'yytext' macro. We assemble those values into a final
     225                 :             :  * error text and then arrange to pass it back to the caller of pgpa_yyparse()
     226                 :             :  * by storing it into *parse_error_msg_p.
     227                 :             :  */
     228                 :             : void
     229                 :          23 : pgpa_yyerror(List **result, char **parse_error_msg_p, yyscan_t yyscanner,
     230                 :             :              const char *message)
     231                 :             : {
     232                 :          23 :     struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;   /* needed for yytext
     233                 :             :                                                              * macro */
     234                 :             : 
     235                 :             : 
     236                 :             :     /* report only the first error in a parse operation */
     237         [ +  + ]:          23 :     if (*parse_error_msg_p)
     238                 :           3 :         return;
     239                 :             : 
     240         [ +  + ]:          20 :     if (yytext[0])
     241                 :          14 :         *parse_error_msg_p = psprintf("%s at or near \"%s\"", message, yytext);
     242                 :             :     else
     243                 :           6 :         *parse_error_msg_p = psprintf("%s at end of input", message);
     244                 :             : }
     245                 :             : 
     246                 :             : /*
     247                 :             :  * Initialize the advice scanner.
     248                 :             :  *
     249                 :             :  * This should be called before parsing begins.
     250                 :             :  */
     251                 :             : void
     252                 :       43915 : pgpa_scanner_init(const char *str, yyscan_t *yyscannerp)
     253                 :             : {
     254                 :             :     yyscan_t    yyscanner;
     255                 :       43915 :     pgpa_yy_extra_type  *yyext = palloc0_object(pgpa_yy_extra_type);
     256                 :             : 
     257         [ -  + ]:       43915 :     if (yylex_init(yyscannerp) != 0)
     258         [ #  # ]:           0 :         elog(ERROR, "yylex_init() failed: %m");
     259                 :             : 
     260                 :       43915 :     yyscanner = *yyscannerp;
     261                 :             : 
     262                 :       43915 :     initStringInfo(&yyext->litbuf);
     263                 :       43915 :     pgpa_yyset_extra(yyext, yyscanner);
     264                 :             : 
     265                 :       43915 :     yy_scan_string(str, yyscanner);
     266                 :       43915 : }
     267                 :             : 
     268                 :             : 
     269                 :             : /*
     270                 :             :  * Shut down the advice scanner.
     271                 :             :  *
     272                 :             :  * This should be called after parsing is complete.
     273                 :             :  */
     274                 :             : void
     275                 :       43915 : pgpa_scanner_finish(yyscan_t yyscanner)
     276                 :             : {
     277                 :       43915 :     yylex_destroy(yyscanner);
     278                 :       43915 : }
     279                 :             : 
     280                 :             : /*
     281                 :             :  * Interface functions to make flex use palloc() instead of malloc().
     282                 :             :  * It'd be better to make these static, but flex insists otherwise.
     283                 :             :  */
     284                 :             : 
     285                 :             : void *
     286                 :      175660 : yyalloc(yy_size_t size, yyscan_t yyscanner)
     287                 :             : {
     288                 :      175660 :     return palloc(size);
     289                 :             : }
     290                 :             : 
     291                 :             : void *
     292                 :           0 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
     293                 :             : {
     294         [ #  # ]:           0 :     if (ptr)
     295                 :           0 :         return repalloc(ptr, size);
     296                 :             :     else
     297                 :           0 :         return palloc(size);
     298                 :             : }
     299                 :             : 
     300                 :             : void
     301                 :      219575 : yyfree(void *ptr, yyscan_t yyscanner)
     302                 :             : {
     303         [ +  + ]:      219575 :     if (ptr)
     304                 :      175660 :         pfree(ptr);
     305                 :      219575 : }
        

Generated by: LCOV version 2.0-1