LCOV - code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_parser.y (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.7 % 92 89
Test Date: 2026-07-25 22:15:46 Functions: 100.0 % 1 1
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 81.0 % 42 34

             Branch data     Line data    Source code
       1                 :             : %{
       2                 :             : /*
       3                 :             :  * Parser for plan advice
       4                 :             :  *
       5                 :             :  * Copyright (c) 2000-2026, PostgreSQL Global Development Group
       6                 :             :  *
       7                 :             :  * contrib/pg_plan_advice/pgpa_parser.y
       8                 :             :  */
       9                 :             : 
      10                 :             : #include "postgres.h"
      11                 :             : 
      12                 :             : #include <float.h>
      13                 :             : #include <math.h>
      14                 :             : 
      15                 :             : #include "fmgr.h"
      16                 :             : #include "nodes/miscnodes.h"
      17                 :             : #include "utils/builtins.h"
      18                 :             : #include "utils/float.h"
      19                 :             : 
      20                 :             : #include "pgpa_ast.h"
      21                 :             : #include "pgpa_parser.h"
      22                 :             : 
      23                 :             : /*
      24                 :             :  * Bison doesn't allocate anything that needs to live across parser calls,
      25                 :             :  * so we can easily have it use palloc instead of malloc.  This prevents
      26                 :             :  * memory leaks if we error out during parsing.
      27                 :             :  */
      28                 :             : #define YYMALLOC palloc
      29                 :             : #define YYFREE   pfree
      30                 :             : %}
      31                 :             : 
      32                 :             : /* BISON Declarations */
      33                 :             : %parse-param {List **result}
      34                 :             : %parse-param {char **parse_error_msg_p}
      35                 :             : %parse-param {yyscan_t yyscanner}
      36                 :             : %lex-param {List **result}
      37                 :             : %lex-param {char **parse_error_msg_p}
      38                 :             : %lex-param {yyscan_t yyscanner}
      39                 :             : %pure-parser
      40                 :             : %expect 0
      41                 :             : %name-prefix="pgpa_yy"
      42                 :             : 
      43                 :             : %union
      44                 :             : {
      45                 :             :     char       *str;
      46                 :             :     int         integer;
      47                 :             :     List       *list;
      48                 :             :     pgpa_advice_item *item;
      49                 :             :     pgpa_advice_target *target;
      50                 :             :     pgpa_index_target *itarget;
      51                 :             : }
      52                 :             : %token <str> TOK_IDENT TOK_TAG_JOIN_ORDER TOK_TAG_INDEX
      53                 :             : %token <str> TOK_TAG_SIMPLE TOK_TAG_GENERIC
      54                 :             : %token <integer> TOK_INTEGER
      55                 :             : 
      56                 :             : %type <integer> opt_ri_occurrence
      57                 :             : %type <item> advice_item
      58                 :             : %type <list> advice_item_list generic_target_list
      59                 :             : %type <list> index_target_list join_order_target_list
      60                 :             : %type <list> opt_partition simple_target_list
      61                 :             : %type <str> identifier opt_plan_name
      62                 :             : %type <target> generic_sublist join_order_sublist
      63                 :             : %type <target> relation_identifier
      64                 :             : %type <itarget> index_name
      65                 :             : 
      66                 :             : %start parse_toplevel
      67                 :             : 
      68                 :             : /* Grammar follows */
      69                 :             : %%
      70                 :             : 
      71                 :             : parse_toplevel: advice_item_list
      72                 :             :         {
      73                 :             :             (void) yynerrs;             /* suppress compiler warning */
      74                 :       43906 :             *result = $1;
      75                 :             :         }
      76                 :             :     ;
      77                 :             : 
      78                 :             : advice_item_list: advice_item_list advice_item
      79                 :       95529 :         { $$ = lappend($1, $2); }
      80                 :             :     |
      81                 :       43915 :         { $$ = NIL; }
      82                 :             :     ;
      83                 :             : 
      84                 :             : advice_item: TOK_TAG_JOIN_ORDER '(' join_order_target_list ')'
      85                 :             :         {
      86                 :       10981 :             $$ = palloc0_object(pgpa_advice_item);
      87                 :       10981 :             $$->tag = PGPA_TAG_JOIN_ORDER;
      88                 :       10981 :             $$->targets = $3;
      89         [ +  + ]:       10981 :             if ($3 == NIL)
      90                 :           1 :                 pgpa_yyerror(result, parse_error_msg_p, yyscanner,
      91                 :             :                              "JOIN_ORDER must have at least one target");
      92                 :             :         }
      93                 :             :     | TOK_TAG_INDEX '(' index_target_list ')'
      94                 :             :         {
      95                 :        9045 :             $$ = palloc0_object(pgpa_advice_item);
      96         [ +  + ]:        9045 :             if (strcmp($1, "index_only_scan") == 0)
      97                 :        1566 :                 $$->tag = PGPA_TAG_INDEX_ONLY_SCAN;
      98         [ +  - ]:        7479 :             else if (strcmp($1, "index_scan") == 0)
      99                 :        7479 :                 $$->tag = PGPA_TAG_INDEX_SCAN;
     100                 :             :             else
     101         [ #  # ]:           0 :                 elog(ERROR, "tag parsing failed: %s", $1);
     102                 :        9045 :             $$->targets = $3;
     103                 :             :         }
     104                 :             :     | TOK_TAG_SIMPLE '(' simple_target_list ')'
     105                 :             :         {
     106                 :       63059 :             $$ = palloc0_object(pgpa_advice_item);
     107         [ +  + ]:       63059 :             if (strcmp($1, "bitmap_heap_scan") == 0)
     108                 :        2576 :                 $$->tag = PGPA_TAG_BITMAP_HEAP_SCAN;
     109         [ +  + ]:       60483 :             else if (strcmp($1, "do_not_scan") == 0)
     110                 :         181 :                 $$->tag = PGPA_TAG_DO_NOT_SCAN;
     111         [ +  + ]:       60302 :             else if (strcmp($1, "no_gather") == 0)
     112                 :       43485 :                 $$->tag = PGPA_TAG_NO_GATHER;
     113         [ +  + ]:       16817 :             else if (strcmp($1, "seq_scan") == 0)
     114                 :       16412 :                 $$->tag = PGPA_TAG_SEQ_SCAN;
     115         [ +  - ]:         405 :             else if (strcmp($1, "tid_scan") == 0)
     116                 :         405 :                 $$->tag = PGPA_TAG_TID_SCAN;
     117                 :             :             else
     118         [ #  # ]:           0 :                 elog(ERROR, "tag parsing failed: %s", $1);
     119                 :       63059 :             $$->targets = $3;
     120                 :             :         }
     121                 :             :     | TOK_TAG_GENERIC '(' generic_target_list ')'
     122                 :             :         {
     123                 :             :             bool    fail;
     124                 :             : 
     125                 :       12444 :             $$ = palloc0_object(pgpa_advice_item);
     126                 :       12444 :             $$->tag = pgpa_parse_advice_tag($1, &fail);
     127         [ -  + ]:       12444 :             if (fail)
     128                 :             :             {
     129                 :           0 :                 pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     130                 :             :                              "unrecognized advice tag");
     131                 :             :             }
     132                 :             : 
     133         [ +  + ]:       12444 :             if ($$->tag == PGPA_TAG_FOREIGN_JOIN)
     134                 :             :             {
     135   [ +  -  +  +  :          12 :                 foreach_ptr(pgpa_advice_target, target, $3)
                   +  + ]
     136                 :             :                 {
     137   [ +  +  +  + ]:           7 :                     if (target->ttype == PGPA_TARGET_IDENTIFIER ||
     138                 :           3 :                         list_length(target->children) == 1)
     139                 :           2 :                             pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     140                 :             :                                          "FOREIGN_JOIN targets must contain more than one relation identifier");
     141                 :             :                 }
     142                 :             :             }
     143                 :             : 
     144                 :       12444 :             $$->targets = $3;
     145                 :             :         }
     146                 :             :     ;
     147                 :             : 
     148                 :             : relation_identifier: identifier opt_ri_occurrence opt_partition opt_plan_name
     149                 :             :         {
     150                 :      161183 :             $$ = palloc0_object(pgpa_advice_target);
     151                 :      161183 :             $$->ttype = PGPA_TARGET_IDENTIFIER;
     152                 :      161183 :             $$->rid.alias_name = $1;
     153                 :      161183 :             $$->rid.occurrence = $2;
     154         [ +  + ]:      161183 :             if (list_length($3) == 2)
     155                 :             :             {
     156                 :       14247 :                 $$->rid.partnsp = linitial($3);
     157                 :       14247 :                 $$->rid.partrel = lsecond($3);
     158                 :             :             }
     159         [ +  + ]:      146936 :             else if ($3 != NIL)
     160                 :          14 :                 $$->rid.partrel = linitial($3);
     161                 :      161183 :             $$->rid.plan_name = $4;
     162                 :             :         }
     163                 :             :     ;
     164                 :             : 
     165                 :             : index_name: identifier
     166                 :             :         {
     167                 :          35 :             $$ = palloc0_object(pgpa_index_target);
     168                 :          35 :             $$->indname = $1;
     169                 :             :         }
     170                 :             :     | identifier '.' identifier
     171                 :             :         {
     172                 :       14423 :             $$ = palloc0_object(pgpa_index_target);
     173                 :       14423 :             $$->indnamespace = $1;
     174                 :       14423 :             $$->indname = $3;
     175                 :             :         }
     176                 :             :     ;
     177                 :             : 
     178                 :             : opt_ri_occurrence:
     179                 :             :     '#' TOK_INTEGER
     180                 :             :         {
     181         [ +  + ]:        3435 :             if ($2 <= 0)
     182                 :           1 :                 pgpa_yyerror(result, parse_error_msg_p, yyscanner,
     183                 :             :                              "only positive occurrence numbers are permitted");
     184                 :        3435 :             $$ = $2;
     185                 :             :         }
     186                 :             :     |
     187                 :             :         {
     188                 :             :             /* The default occurrence number is 1. */
     189                 :      157748 :             $$ = 1;
     190                 :             :         }
     191                 :             :     ;
     192                 :             : 
     193                 :             : identifier: TOK_IDENT
     194                 :             :     | TOK_TAG_JOIN_ORDER
     195                 :             :     | TOK_TAG_INDEX
     196                 :             :     | TOK_TAG_SIMPLE
     197                 :             :     | TOK_TAG_GENERIC
     198                 :             :     ;
     199                 :             : 
     200                 :             : /*
     201                 :             :  * When generating advice, we always schema-qualify the partition name, but
     202                 :             :  * when parsing advice, we accept a specification that lacks one.
     203                 :             :  */
     204                 :             : opt_partition:
     205                 :             :     '/' identifier '.' identifier
     206                 :       14247 :         { $$ = list_make2($2, $4); }
     207                 :             :     | '/' identifier
     208                 :          14 :         { $$ = list_make1($2); }
     209                 :             :     |
     210                 :      146922 :         { $$ = NIL; }
     211                 :             :     ;
     212                 :             : 
     213                 :             : opt_plan_name:
     214                 :             :     '@' identifier
     215                 :       38416 :         { $$ = $2; }
     216                 :             :     |
     217                 :      122767 :         { $$ = NULL; }
     218                 :             :     ;
     219                 :             : 
     220                 :             : generic_target_list: generic_target_list relation_identifier
     221                 :       17423 :         { $$ = lappend($1, $2); }
     222                 :             :     | generic_target_list generic_sublist
     223                 :         857 :         { $$ = lappend($1, $2); }
     224                 :             :     |
     225                 :       12445 :         { $$ = NIL; }
     226                 :             :     ;
     227                 :             : 
     228                 :             : generic_sublist: '(' simple_target_list ')'
     229                 :             :         {
     230                 :         857 :             $$ = palloc0_object(pgpa_advice_target);
     231                 :         857 :             $$->ttype = PGPA_TARGET_ORDERED_LIST;
     232                 :         857 :             $$->children = $2;
     233                 :             :         }
     234                 :             :     ;
     235                 :             : 
     236                 :             : index_target_list:
     237                 :             :       index_target_list relation_identifier index_name
     238                 :             :         {
     239                 :       14458 :             $2->itarget = $3;
     240                 :       14458 :             $$ = lappend($1, $2);
     241                 :             :         }
     242                 :             :     |
     243                 :        9045 :         { $$ = NIL; }
     244                 :             :     ;
     245                 :             : 
     246                 :             : join_order_target_list: join_order_target_list relation_identifier
     247                 :       26077 :         { $$ = lappend($1, $2); }
     248                 :             :     | join_order_target_list join_order_sublist
     249                 :         545 :         { $$ = lappend($1, $2); }
     250                 :             :     |
     251                 :       11511 :         { $$ = NIL; }
     252                 :             :     ;
     253                 :             : 
     254                 :             : join_order_sublist:
     255                 :             :     '(' join_order_target_list ')'
     256                 :             :         {
     257                 :         530 :             $$ = palloc0_object(pgpa_advice_target);
     258                 :         530 :             $$->ttype = PGPA_TARGET_ORDERED_LIST;
     259                 :         530 :             $$->children = $2;
     260                 :             :         }
     261                 :             :     | '{' simple_target_list '}'
     262                 :             :         {
     263                 :          15 :             $$ = palloc0_object(pgpa_advice_target);
     264                 :          15 :             $$->ttype = PGPA_TARGET_UNORDERED_LIST;
     265                 :          15 :             $$->children = $2;
     266                 :             :         }
     267                 :             :     ;
     268                 :             : 
     269                 :             : simple_target_list: simple_target_list relation_identifier
     270                 :      103225 :         { $$ = lappend($1, $2); }
     271                 :             :     |
     272                 :       63939 :         { $$ = NIL; }
     273                 :             :     ;
     274                 :             : 
     275                 :             : %%
     276                 :             : 
     277                 :             : /*
     278                 :             :  * Parse an advice_string and return the resulting list of pgpa_advice_item
     279                 :             :  * objects. If a parse error occurs, instead return NULL.
     280                 :             :  *
     281                 :             :  * If the return value is NULL, *error_p will be set to the error message;
     282                 :             :  * otherwise, *error_p will be set to NULL.
     283                 :             :  */
     284                 :             : List *
     285                 :       43915 : pgpa_parse(const char *advice_string, char **error_p)
     286                 :             : {
     287                 :             :     yyscan_t    scanner;
     288                 :             :     List       *result;
     289                 :       43915 :     char       *error = NULL;
     290                 :             : 
     291                 :       43915 :     pgpa_scanner_init(advice_string, &scanner);
     292                 :       43915 :     pgpa_yyparse(&result, &error, scanner);
     293                 :       43915 :     pgpa_scanner_finish(scanner);
     294                 :             : 
     295         [ +  + ]:       43915 :     if (error != NULL)
     296                 :             :     {
     297                 :          20 :         *error_p = error;
     298                 :          20 :         return NULL;
     299                 :             :     }
     300                 :             : 
     301                 :       43895 :     *error_p = NULL;
     302                 :       43895 :     return result;
     303                 :             : }
        

Generated by: LCOV version 2.0-1