LCOV - code coverage report
Current view: top level - src/pl/plpgsql/src - pl_scanner.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.8 % 152 138
Test Date: 2026-10-13 15:16:50 Functions: 100.0 % 16 16
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 78.9 % 90 71

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pl_scanner.c
       4                 :             :  *    lexical scanning for PL/pgSQL
       5                 :             :  *
       6                 :             :  *
       7                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :             :  *
      10                 :             :  *
      11                 :             :  * IDENTIFICATION
      12                 :             :  *    src/pl/plpgsql/src/pl_scanner.c
      13                 :             :  *
      14                 :             :  *-------------------------------------------------------------------------
      15                 :             :  */
      16                 :             : #include "postgres.h"
      17                 :             : 
      18                 :             : #include "mb/pg_wchar.h"
      19                 :             : #include "parser/scanner.h"
      20                 :             : 
      21                 :             : #include "plpgsql.h"
      22                 :             : #include "pl_gram.h"          /* must be after parser/scanner.h */
      23                 :             : 
      24                 :             : 
      25                 :             : /* Klugy flag to tell scanner how to look up identifiers */
      26                 :             : IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
      27                 :             : 
      28                 :             : /*
      29                 :             :  * A word about keywords:
      30                 :             :  *
      31                 :             :  * We keep reserved and unreserved keywords in separate headers.  Be careful
      32                 :             :  * not to put the same word in both headers.  Also be sure that pl_gram.y's
      33                 :             :  * unreserved_keyword production agrees with the unreserved header.  The
      34                 :             :  * reserved keywords are passed to the core scanner, so they will be
      35                 :             :  * recognized before (and instead of) any variable name.  Unreserved words
      36                 :             :  * are checked for separately, usually after determining that the identifier
      37                 :             :  * isn't a known variable name.  If plpgsql_IdentifierLookup is DECLARE then
      38                 :             :  * no variable names will be recognized, so the unreserved words always work.
      39                 :             :  * (Note in particular that this helps us avoid reserving keywords that are
      40                 :             :  * only needed in DECLARE sections.)
      41                 :             :  *
      42                 :             :  * In certain contexts it is desirable to prefer recognizing an unreserved
      43                 :             :  * keyword over recognizing a variable name.  In particular, at the start
      44                 :             :  * of a statement we should prefer unreserved keywords unless the statement
      45                 :             :  * looks like an assignment (i.e., first token is followed by ':=' or '[').
      46                 :             :  * This rule allows most statement-introducing keywords to be kept unreserved.
      47                 :             :  * (We still have to reserve initial keywords that might follow a block
      48                 :             :  * label, unfortunately, since the method used to determine if we are at
      49                 :             :  * start of statement doesn't recognize such cases.  We'd also have to
      50                 :             :  * reserve any keyword that could legitimately be followed by ':=' or '['.)
      51                 :             :  * Some additional cases are handled in pl_gram.y using tok_is_keyword().
      52                 :             :  *
      53                 :             :  * We try to avoid reserving more keywords than we have to; but there's
      54                 :             :  * little point in not reserving a word if it's reserved in the core grammar.
      55                 :             :  * Currently, the following words are reserved here but not in the core:
      56                 :             :  * BEGIN BY DECLARE FOREACH IF LOOP WHILE
      57                 :             :  */
      58                 :             : 
      59                 :             : /* ScanKeywordList lookup data for PL/pgSQL keywords */
      60                 :             : #include "pl_reserved_kwlist_d.h"
      61                 :             : #include "pl_unreserved_kwlist_d.h"
      62                 :             : 
      63                 :             : /* Token codes for PL/pgSQL keywords */
      64                 :             : #define PG_KEYWORD(kwname, value) value,
      65                 :             : 
      66                 :             : static const uint16 ReservedPLKeywordTokens[] = {
      67                 :             : #include "pl_reserved_kwlist.h"
      68                 :             : };
      69                 :             : 
      70                 :             : static const uint16 UnreservedPLKeywordTokens[] = {
      71                 :             : #include "pl_unreserved_kwlist.h"
      72                 :             : };
      73                 :             : 
      74                 :             : #undef PG_KEYWORD
      75                 :             : 
      76                 :             : /*
      77                 :             :  * This macro must recognize all tokens that can immediately precede a
      78                 :             :  * PL/pgSQL executable statement (that is, proc_sect or proc_stmt in the
      79                 :             :  * grammar).  Fortunately, there are not very many, so hard-coding in this
      80                 :             :  * fashion seems sufficient.
      81                 :             :  */
      82                 :             : #define AT_STMT_START(prev_token) \
      83                 :             :     ((prev_token) == ';' || \
      84                 :             :      (prev_token) == K_BEGIN || \
      85                 :             :      (prev_token) == K_THEN || \
      86                 :             :      (prev_token) == K_ELSE || \
      87                 :             :      (prev_token) == K_LOOP)
      88                 :             : 
      89                 :             : 
      90                 :             : /* Auxiliary data about a token (other than the token type) */
      91                 :             : typedef struct
      92                 :             : {
      93                 :             :     YYSTYPE     lval;           /* semantic information */
      94                 :             :     YYLTYPE     lloc;           /* offset in scanbuf */
      95                 :             :     int         leng;           /* length in bytes */
      96                 :             : } TokenAuxData;
      97                 :             : 
      98                 :             : #define MAX_PUSHBACKS 4
      99                 :             : 
     100                 :             : /*
     101                 :             :  * Scanner working state.
     102                 :             :  */
     103                 :             : struct plpgsql_yy_extra_type
     104                 :             : {
     105                 :             :     /* The stuff the core lexer needs */
     106                 :             :     core_yy_extra_type core_yy_extra;
     107                 :             : 
     108                 :             :     /* The original input string */
     109                 :             :     const char *scanorig;
     110                 :             : 
     111                 :             :     /*
     112                 :             :      * Current token's length (corresponds to plpgsql_yylval and
     113                 :             :      * plpgsql_yylloc)
     114                 :             :      */
     115                 :             :     int         plpgsql_yyleng;
     116                 :             : 
     117                 :             :     /* Current token's code (corresponds to plpgsql_yylval and plpgsql_yylloc) */
     118                 :             :     int         plpgsql_yytoken;
     119                 :             : 
     120                 :             :     /* Token pushback stack */
     121                 :             :     int         num_pushbacks;
     122                 :             :     int         pushback_token[MAX_PUSHBACKS];
     123                 :             :     TokenAuxData pushback_auxdata[MAX_PUSHBACKS];
     124                 :             : 
     125                 :             :     /* State for plpgsql_location_to_lineno() */
     126                 :             :     const char *cur_line_start;
     127                 :             :     const char *cur_line_end;
     128                 :             :     int         cur_line_num;
     129                 :             : };
     130                 :             : 
     131                 :             : /* Internal functions */
     132                 :             : static int  internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
     133                 :             : static void push_back_token(int token, TokenAuxData *auxdata, yyscan_t yyscanner);
     134                 :             : static void location_lineno_init(yyscan_t yyscanner);
     135                 :             : 
     136                 :             : /*
     137                 :             :  * This is normally provided by the generated flex code, but we don't have
     138                 :             :  * that here, so we make a minimal version ourselves.
     139                 :             :  */
     140                 :             : struct yyguts_t
     141                 :             : {
     142                 :             :     struct plpgsql_yy_extra_type *yyextra_r;
     143                 :             : };
     144                 :             : 
     145                 :             : /* see scan.l */
     146                 :             : #undef yyextra
     147                 :             : #define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
     148                 :             : 
     149                 :             : 
     150                 :             : /*
     151                 :             :  * This is the yylex routine called from the PL/pgSQL grammar.
     152                 :             :  * It is a wrapper around the core lexer, with the ability to recognize
     153                 :             :  * PL/pgSQL variables and return them as special T_DATUM tokens.  If a
     154                 :             :  * word or compound word does not match any variable name, or if matching
     155                 :             :  * is turned off by plpgsql_IdentifierLookup, it is returned as
     156                 :             :  * T_WORD or T_CWORD respectively, or as an unreserved keyword if it
     157                 :             :  * matches one of those.
     158                 :             :  */
     159                 :             : int
     160                 :      254588 : plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
     161                 :             : {
     162                 :             :     int         tok1;
     163                 :             :     TokenAuxData aux1;
     164                 :             :     int         kwnum;
     165                 :             : 
     166                 :      254588 :     tok1 = internal_yylex(&aux1, yyscanner);
     167   [ +  +  +  + ]:      254588 :     if (tok1 == IDENT || tok1 == PARAM)
     168                 :             :     {
     169                 :             :         int         tok2;
     170                 :             :         TokenAuxData aux2;
     171                 :             : 
     172                 :       85278 :         tok2 = internal_yylex(&aux2, yyscanner);
     173         [ +  + ]:       85278 :         if (tok2 == '.')
     174                 :             :         {
     175                 :             :             int         tok3;
     176                 :             :             TokenAuxData aux3;
     177                 :             : 
     178                 :        5652 :             tok3 = internal_yylex(&aux3, yyscanner);
     179         [ +  + ]:        5652 :             if (tok3 == IDENT)
     180                 :             :             {
     181                 :             :                 int         tok4;
     182                 :             :                 TokenAuxData aux4;
     183                 :             : 
     184                 :        5514 :                 tok4 = internal_yylex(&aux4, yyscanner);
     185         [ +  + ]:        5514 :                 if (tok4 == '.')
     186                 :             :                 {
     187                 :             :                     int         tok5;
     188                 :             :                     TokenAuxData aux5;
     189                 :             : 
     190                 :          33 :                     tok5 = internal_yylex(&aux5, yyscanner);
     191         [ +  - ]:          33 :                     if (tok5 == IDENT)
     192                 :             :                     {
     193         [ +  + ]:          33 :                         if (plpgsql_parse_tripword(aux1.lval.str,
     194                 :             :                                                    aux3.lval.str,
     195                 :             :                                                    aux5.lval.str,
     196                 :             :                                                    &aux1.lval.wdatum,
     197                 :             :                                                    &aux1.lval.cword))
     198                 :          23 :                             tok1 = T_DATUM;
     199                 :             :                         else
     200                 :          10 :                             tok1 = T_CWORD;
     201                 :             :                         /* Adjust token length to include A.B.C */
     202                 :          33 :                         aux1.leng = aux5.lloc - aux1.lloc + aux5.leng;
     203                 :             :                     }
     204                 :             :                     else
     205                 :             :                     {
     206                 :             :                         /* not A.B.C, so just process A.B */
     207                 :           0 :                         push_back_token(tok5, &aux5, yyscanner);
     208                 :           0 :                         push_back_token(tok4, &aux4, yyscanner);
     209         [ #  # ]:           0 :                         if (plpgsql_parse_dblword(aux1.lval.str,
     210                 :             :                                                   aux3.lval.str,
     211                 :             :                                                   &aux1.lval.wdatum,
     212                 :             :                                                   &aux1.lval.cword))
     213                 :           0 :                             tok1 = T_DATUM;
     214                 :             :                         else
     215                 :           0 :                             tok1 = T_CWORD;
     216                 :             :                         /* Adjust token length to include A.B */
     217                 :           0 :                         aux1.leng = aux3.lloc - aux1.lloc + aux3.leng;
     218                 :             :                     }
     219                 :             :                 }
     220                 :             :                 else
     221                 :             :                 {
     222                 :             :                     /* not A.B.C, so just process A.B */
     223                 :        5481 :                     push_back_token(tok4, &aux4, yyscanner);
     224         [ +  + ]:        5481 :                     if (plpgsql_parse_dblword(aux1.lval.str,
     225                 :             :                                               aux3.lval.str,
     226                 :             :                                               &aux1.lval.wdatum,
     227                 :             :                                               &aux1.lval.cword))
     228                 :        4850 :                         tok1 = T_DATUM;
     229                 :             :                     else
     230                 :         631 :                         tok1 = T_CWORD;
     231                 :             :                     /* Adjust token length to include A.B */
     232                 :        5481 :                     aux1.leng = aux3.lloc - aux1.lloc + aux3.leng;
     233                 :             :                 }
     234                 :             :             }
     235                 :             :             else
     236                 :             :             {
     237                 :             :                 /* not A.B, so just process A */
     238                 :         138 :                 push_back_token(tok3, &aux3, yyscanner);
     239                 :         138 :                 push_back_token(tok2, &aux2, yyscanner);
     240         [ -  + ]:         138 :                 if (plpgsql_parse_word(aux1.lval.str,
     241                 :         138 :                                        yyextra->core_yy_extra.scanbuf + aux1.lloc,
     242                 :             :                                        true,
     243                 :             :                                        &aux1.lval.wdatum,
     244                 :             :                                        &aux1.lval.word))
     245                 :           0 :                     tok1 = T_DATUM;
     246   [ +  -  -  + ]:         276 :                 else if (!aux1.lval.word.quoted &&
     247                 :         138 :                          (kwnum = ScanKeywordLookup(aux1.lval.word.ident,
     248                 :             :                                                     &UnreservedPLKeywords)) >= 0)
     249                 :             :                 {
     250                 :           0 :                     aux1.lval.keyword = GetScanKeyword(kwnum,
     251                 :             :                                                        &UnreservedPLKeywords);
     252                 :           0 :                     tok1 = UnreservedPLKeywordTokens[kwnum];
     253                 :             :                 }
     254                 :             :                 else
     255                 :         138 :                     tok1 = T_WORD;
     256                 :             :             }
     257                 :             :         }
     258                 :             :         else
     259                 :             :         {
     260                 :             :             /* not A.B, so just process A */
     261                 :       79626 :             push_back_token(tok2, &aux2, yyscanner);
     262                 :             : 
     263                 :             :             /*
     264                 :             :              * See if it matches a variable name, except in the context where
     265                 :             :              * we are at start of statement and the next token isn't
     266                 :             :              * assignment or '['.  In that case, it couldn't validly be a
     267                 :             :              * variable name, and skipping the lookup allows variable names to
     268                 :             :              * be used that would conflict with plpgsql or core keywords that
     269                 :             :              * introduce statements (e.g., "comment").  Without this special
     270                 :             :              * logic, every statement-introducing keyword would effectively be
     271                 :             :              * reserved in PL/pgSQL, which would be unpleasant.
     272                 :             :              *
     273                 :             :              * If it isn't a variable name, try to match against unreserved
     274                 :             :              * plpgsql keywords.  If not one of those either, it's T_WORD.
     275                 :             :              *
     276                 :             :              * Note: we must call plpgsql_parse_word even if we don't want to
     277                 :             :              * do variable lookup, because it sets up aux1.lval.word for the
     278                 :             :              * non-variable cases.
     279                 :             :              */
     280         [ +  + ]:       79626 :             if (plpgsql_parse_word(aux1.lval.str,
     281                 :       79626 :                                    yyextra->core_yy_extra.scanbuf + aux1.lloc,
     282   [ +  +  +  +  :       99144 :                                    (!AT_STMT_START(yyextra->plpgsql_yytoken) ||
          +  +  +  +  +  
                +  +  + ]
     283   [ +  +  +  + ]:       19518 :                                     (tok2 == '=' || tok2 == COLON_EQUALS ||
     284                 :             :                                      tok2 == '[')),
     285                 :             :                                    &aux1.lval.wdatum,
     286                 :       79626 :                                    &aux1.lval.word))
     287                 :        9625 :                 tok1 = T_DATUM;
     288   [ +  +  +  + ]:      139985 :             else if (!aux1.lval.word.quoted &&
     289                 :       69984 :                      (kwnum = ScanKeywordLookup(aux1.lval.word.ident,
     290                 :             :                                                 &UnreservedPLKeywords)) >= 0)
     291                 :             :             {
     292                 :       20768 :                 aux1.lval.keyword = GetScanKeyword(kwnum,
     293                 :             :                                                    &UnreservedPLKeywords);
     294                 :       20768 :                 tok1 = UnreservedPLKeywordTokens[kwnum];
     295                 :             :             }
     296                 :             :             else
     297                 :       49233 :                 tok1 = T_WORD;
     298                 :             :         }
     299                 :             :     }
     300                 :             :     else
     301                 :             :     {
     302                 :             :         /*
     303                 :             :          * Not a potential plpgsql variable name, just return the data.
     304                 :             :          *
     305                 :             :          * Note that we also come through here if the grammar pushed back a
     306                 :             :          * T_DATUM, T_CWORD, T_WORD, or unreserved-keyword token returned by a
     307                 :             :          * previous lookup cycle; thus, pushbacks do not incur extra lookup
     308                 :             :          * work, since we'll never do the above code twice for the same token.
     309                 :             :          * This property also makes it safe to rely on the old value of
     310                 :             :          * plpgsql_yytoken in the is-this-start-of-statement test above.
     311                 :             :          */
     312                 :             :     }
     313                 :             : 
     314                 :      254588 :     *yylvalp = aux1.lval;
     315                 :      254588 :     *yyllocp = aux1.lloc;
     316                 :      254588 :     yyextra->plpgsql_yyleng = aux1.leng;
     317                 :      254588 :     yyextra->plpgsql_yytoken = tok1;
     318                 :      254588 :     return tok1;
     319                 :             : }
     320                 :             : 
     321                 :             : /*
     322                 :             :  * Return the length of the token last returned by plpgsql_yylex().
     323                 :             :  *
     324                 :             :  * In the case of compound tokens, the length includes all the parts.
     325                 :             :  */
     326                 :             : int
     327                 :       79887 : plpgsql_token_length(yyscan_t yyscanner)
     328                 :             : {
     329                 :       79887 :     return yyextra->plpgsql_yyleng;
     330                 :             : }
     331                 :             : 
     332                 :             : /*
     333                 :             :  * Internal yylex function.  This wraps the core lexer and adds one feature:
     334                 :             :  * a token pushback stack.  We also make a couple of trivial single-token
     335                 :             :  * translations from what the core lexer does to what we want, in particular
     336                 :             :  * interfacing from the core_YYSTYPE to YYSTYPE union.
     337                 :             :  */
     338                 :             : static int
     339                 :      355102 : internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
     340                 :             : {
     341                 :             :     int         token;
     342                 :             :     const char *yytext;
     343                 :             : 
     344         [ +  + ]:      355102 :     if (yyextra->num_pushbacks > 0)
     345                 :             :     {
     346                 :      110882 :         yyextra->num_pushbacks--;
     347                 :      110882 :         token = yyextra->pushback_token[yyextra->num_pushbacks];
     348                 :      110882 :         *auxdata = yyextra->pushback_auxdata[yyextra->num_pushbacks];
     349                 :             :     }
     350                 :             :     else
     351                 :             :     {
     352                 :      244220 :         token = core_yylex(&auxdata->lval.core_yystype,
     353                 :             :                            &auxdata->lloc,
     354                 :             :                            yyscanner);
     355                 :             : 
     356                 :             :         /* remember the length of yytext before it gets changed */
     357                 :      244220 :         yytext = yyextra->core_yy_extra.scanbuf + auxdata->lloc;
     358                 :      244220 :         auxdata->leng = strlen(yytext);
     359                 :             : 
     360                 :             :         /* Check for << >> and #, which the core considers operators */
     361         [ +  + ]:      244220 :         if (token == Op)
     362                 :             :         {
     363         [ +  + ]:        1740 :             if (strcmp(auxdata->lval.str, "<<") == 0)
     364                 :          54 :                 token = LESS_LESS;
     365         [ +  + ]:        1686 :             else if (strcmp(auxdata->lval.str, ">>") == 0)
     366                 :          50 :                 token = GREATER_GREATER;
     367         [ +  + ]:        1636 :             else if (strcmp(auxdata->lval.str, "#") == 0)
     368                 :          16 :                 token = '#';
     369                 :             :         }
     370                 :             : 
     371                 :             :         /* The core returns PARAM as ival, but we treat it like IDENT */
     372         [ +  + ]:      242480 :         else if (token == PARAM)
     373                 :             :         {
     374                 :        1085 :             auxdata->lval.str = pstrdup(yytext);
     375                 :             :         }
     376                 :             :     }
     377                 :             : 
     378                 :      355102 :     return token;
     379                 :             : }
     380                 :             : 
     381                 :             : /*
     382                 :             :  * Push back a token to be re-read by next internal_yylex() call.
     383                 :             :  */
     384                 :             : static void
     385                 :      110925 : push_back_token(int token, TokenAuxData *auxdata, yyscan_t yyscanner)
     386                 :             : {
     387         [ -  + ]:      110925 :     if (yyextra->num_pushbacks >= MAX_PUSHBACKS)
     388         [ #  # ]:           0 :         elog(ERROR, "too many tokens pushed back");
     389                 :      110925 :     yyextra->pushback_token[yyextra->num_pushbacks] = token;
     390                 :      110925 :     yyextra->pushback_auxdata[yyextra->num_pushbacks] = *auxdata;
     391                 :      110925 :     yyextra->num_pushbacks++;
     392                 :      110925 : }
     393                 :             : 
     394                 :             : /*
     395                 :             :  * Push back a single token to be re-read by next plpgsql_yylex() call.
     396                 :             :  *
     397                 :             :  * NOTE: this does not cause yylval or yylloc to "back up".  Also, it
     398                 :             :  * is not a good idea to push back a token code other than what you read.
     399                 :             :  */
     400                 :             : void
     401                 :       21505 : plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
     402                 :             : {
     403                 :             :     TokenAuxData auxdata;
     404                 :             : 
     405                 :       21505 :     auxdata.lval = *yylvalp;
     406                 :       21505 :     auxdata.lloc = *yyllocp;
     407                 :       21505 :     auxdata.leng = yyextra->plpgsql_yyleng;
     408                 :       21505 :     push_back_token(token, &auxdata, yyscanner);
     409                 :       21505 : }
     410                 :             : 
     411                 :             : /*
     412                 :             :  * Tell whether a token is an unreserved keyword.
     413                 :             :  *
     414                 :             :  * (If it is, its lowercased form was returned as the token value, so we
     415                 :             :  * do not need to offer that data here.)
     416                 :             :  */
     417                 :             : bool
     418                 :          43 : plpgsql_token_is_unreserved_keyword(int token)
     419                 :             : {
     420         [ +  + ]:        3628 :     for (size_t i = 0; i < lengthof(UnreservedPLKeywordTokens); i++)
     421                 :             :     {
     422         [ +  + ]:        3587 :         if (UnreservedPLKeywordTokens[i] == token)
     423                 :           2 :             return true;
     424                 :             :     }
     425                 :          41 :     return false;
     426                 :             : }
     427                 :             : 
     428                 :             : /*
     429                 :             :  * Append the function text starting at startlocation and extending to
     430                 :             :  * (not including) endlocation onto the existing contents of "buf".
     431                 :             :  */
     432                 :             : void
     433                 :       31308 : plpgsql_append_source_text(StringInfo buf,
     434                 :             :                            int startlocation, int endlocation,
     435                 :             :                            yyscan_t yyscanner)
     436                 :             : {
     437                 :             :     Assert(startlocation <= endlocation);
     438                 :       31308 :     appendBinaryStringInfo(buf, yyextra->scanorig + startlocation,
     439                 :             :                            endlocation - startlocation);
     440                 :       31308 : }
     441                 :             : 
     442                 :             : /*
     443                 :             :  * Peek one token ahead in the input stream.  Only the token code is
     444                 :             :  * made available, not any of the auxiliary info such as location.
     445                 :             :  *
     446                 :             :  * NB: no variable or unreserved keyword lookup is performed here, they will
     447                 :             :  * be returned as IDENT. Reserved keywords are resolved as usual.
     448                 :             :  */
     449                 :             : int
     450                 :        3845 : plpgsql_peek(yyscan_t yyscanner)
     451                 :             : {
     452                 :             :     int         tok1;
     453                 :             :     TokenAuxData aux1;
     454                 :             : 
     455                 :        3845 :     tok1 = internal_yylex(&aux1, yyscanner);
     456                 :        3845 :     push_back_token(tok1, &aux1, yyscanner);
     457                 :        3845 :     return tok1;
     458                 :             : }
     459                 :             : 
     460                 :             : /*
     461                 :             :  * Peek two tokens ahead in the input stream. The first token and its
     462                 :             :  * location in the query are returned in *tok1_p and *tok1_loc, second token
     463                 :             :  * and its location in *tok2_p and *tok2_loc.
     464                 :             :  *
     465                 :             :  * NB: no variable or unreserved keyword lookup is performed here, they will
     466                 :             :  * be returned as IDENT. Reserved keywords are resolved as usual.
     467                 :             :  */
     468                 :             : void
     469                 :          96 : plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
     470                 :             : {
     471                 :             :     int         tok1,
     472                 :             :                 tok2;
     473                 :             :     TokenAuxData aux1,
     474                 :             :                 aux2;
     475                 :             : 
     476                 :          96 :     tok1 = internal_yylex(&aux1, yyscanner);
     477                 :          96 :     tok2 = internal_yylex(&aux2, yyscanner);
     478                 :             : 
     479                 :          96 :     *tok1_p = tok1;
     480         [ +  - ]:          96 :     if (tok1_loc)
     481                 :          96 :         *tok1_loc = aux1.lloc;
     482                 :          96 :     *tok2_p = tok2;
     483         [ -  + ]:          96 :     if (tok2_loc)
     484                 :           0 :         *tok2_loc = aux2.lloc;
     485                 :             : 
     486                 :          96 :     push_back_token(tok2, &aux2, yyscanner);
     487                 :          96 :     push_back_token(tok1, &aux1, yyscanner);
     488                 :          96 : }
     489                 :             : 
     490                 :             : /*
     491                 :             :  * plpgsql_scanner_errposition
     492                 :             :  *      Report an error cursor position, if possible.
     493                 :             :  *
     494                 :             :  * This is expected to be used within an ereport() call.  The return value
     495                 :             :  * is a dummy (always 0, in fact).
     496                 :             :  *
     497                 :             :  * Note that this can only be used for messages emitted during initial
     498                 :             :  * parsing of a plpgsql function, since it requires the scanorig string
     499                 :             :  * to still be available.
     500                 :             :  */
     501                 :             : int
     502                 :         105 : plpgsql_scanner_errposition(int location, yyscan_t yyscanner)
     503                 :             : {
     504                 :             :     int         pos;
     505                 :             : 
     506   [ +  -  -  + ]:         105 :     if (location < 0 || yyextra->scanorig == NULL)
     507                 :           0 :         return 0;               /* no-op if location is unknown */
     508                 :             : 
     509                 :             :     /* Convert byte offset to character number */
     510                 :         105 :     pos = pg_mbstrlen_with_len(yyextra->scanorig, location) + 1;
     511                 :             :     /* And pass it to the ereport mechanism */
     512                 :         105 :     (void) internalerrposition(pos);
     513                 :             :     /* Also pass the function body string */
     514                 :         105 :     return internalerrquery(yyextra->scanorig);
     515                 :             : }
     516                 :             : 
     517                 :             : /*
     518                 :             :  * plpgsql_yyerror
     519                 :             :  *      Report a lexer or grammar error.
     520                 :             :  *
     521                 :             :  * The message's cursor position refers to the current token (the one
     522                 :             :  * last returned by plpgsql_yylex()).
     523                 :             :  * This is OK for syntax error messages from the Bison parser, because Bison
     524                 :             :  * parsers report error as soon as the first unparsable token is reached.
     525                 :             :  * Beware of using yyerror for other purposes, as the cursor position might
     526                 :             :  * be misleading!
     527                 :             :  *
     528                 :             :  * (The second argument is enforced by Bison to match the second argument of
     529                 :             :  * yyparse(), but it is not used here.)
     530                 :             :  */
     531                 :             : void
     532                 :           5 : plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message)
     533                 :             : {
     534                 :           5 :     char       *yytext = yyextra->core_yy_extra.scanbuf + *yyllocp;
     535                 :             : 
     536         [ -  + ]:           5 :     if (*yytext == '\0')
     537                 :             :     {
     538         [ #  # ]:           0 :         ereport(ERROR,
     539                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     540                 :             :         /* translator: %s is typically the translation of "syntax error" */
     541                 :             :                  errmsg("%s at end of input", _(message)),
     542                 :             :                  plpgsql_scanner_errposition(*yyllocp, yyscanner)));
     543                 :             :     }
     544                 :             :     else
     545                 :             :     {
     546                 :             :         /*
     547                 :             :          * If we have done any lookahead then flex will have restored the
     548                 :             :          * character after the end-of-token.  Zap it again so that we report
     549                 :             :          * only the single token here.  This modifies scanbuf but we no longer
     550                 :             :          * care about that.
     551                 :             :          */
     552                 :           5 :         yytext[yyextra->plpgsql_yyleng] = '\0';
     553                 :             : 
     554         [ +  - ]:           5 :         ereport(ERROR,
     555                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
     556                 :             :         /* translator: first %s is typically the translation of "syntax error" */
     557                 :             :                  errmsg("%s at or near \"%s\"", _(message), yytext),
     558                 :             :                  plpgsql_scanner_errposition(*yyllocp, yyscanner)));
     559                 :             :     }
     560                 :             : }
     561                 :             : 
     562                 :             : /*
     563                 :             :  * Given a location (a byte offset in the function source text),
     564                 :             :  * return a line number.
     565                 :             :  *
     566                 :             :  * We expect that this is typically called for a sequence of increasing
     567                 :             :  * location values, so optimize accordingly by tracking the endpoints
     568                 :             :  * of the "current" line.
     569                 :             :  */
     570                 :             : int
     571                 :       36696 : plpgsql_location_to_lineno(int location, yyscan_t yyscanner)
     572                 :             : {
     573                 :             :     const char *loc;
     574                 :             : 
     575   [ +  -  -  + ]:       36696 :     if (location < 0 || yyextra->scanorig == NULL)
     576                 :           0 :         return 0;               /* garbage in, garbage out */
     577                 :       36696 :     loc = yyextra->scanorig + location;
     578                 :             : 
     579                 :             :     /* be correct, but not fast, if input location goes backwards */
     580         [ +  + ]:       36696 :     if (loc < yyextra->cur_line_start)
     581                 :       10768 :         location_lineno_init(yyscanner);
     582                 :             : 
     583   [ +  +  +  + ]:      169340 :     while (yyextra->cur_line_end != NULL && loc > yyextra->cur_line_end)
     584                 :             :     {
     585                 :      132644 :         yyextra->cur_line_start = yyextra->cur_line_end + 1;
     586                 :      132644 :         yyextra->cur_line_num++;
     587                 :      132644 :         yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
     588                 :             :     }
     589                 :             : 
     590                 :       36696 :     return yyextra->cur_line_num;
     591                 :             : }
     592                 :             : 
     593                 :             : /* initialize or reset the state for plpgsql_location_to_lineno */
     594                 :             : static void
     595                 :       16758 : location_lineno_init(yyscan_t yyscanner)
     596                 :             : {
     597                 :       16758 :     yyextra->cur_line_start = yyextra->scanorig;
     598                 :       16758 :     yyextra->cur_line_num = 1;
     599                 :             : 
     600                 :       16758 :     yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
     601                 :       16758 : }
     602                 :             : 
     603                 :             : /* return the most recently computed lineno */
     604                 :             : int
     605                 :          35 : plpgsql_latest_lineno(yyscan_t yyscanner)
     606                 :             : {
     607                 :          35 :     return yyextra->cur_line_num;
     608                 :             : }
     609                 :             : 
     610                 :             : 
     611                 :             : /*
     612                 :             :  * Called before any actual parsing is done
     613                 :             :  *
     614                 :             :  * Note: the passed "str" must remain valid until plpgsql_scanner_finish().
     615                 :             :  * Although it is not fed directly to flex, we need the original string
     616                 :             :  * to cite in error messages.
     617                 :             :  */
     618                 :             : yyscan_t
     619                 :        5990 : plpgsql_scanner_init(const char *str)
     620                 :             : {
     621                 :             :     yyscan_t    yyscanner;
     622                 :        5990 :     struct plpgsql_yy_extra_type *yyext = palloc0_object(struct plpgsql_yy_extra_type);
     623                 :             : 
     624                 :             :     /* Start up the core scanner */
     625                 :        5990 :     yyscanner = scanner_init(str, (core_yy_extra_type *) yyext,
     626                 :             :                              &ReservedPLKeywords, ReservedPLKeywordTokens);
     627                 :             : 
     628                 :             :     /*
     629                 :             :      * scanorig points to the original string, which unlike the scanner's
     630                 :             :      * scanbuf won't be modified on-the-fly by flex.  Notice that although
     631                 :             :      * yytext points into scanbuf, we rely on being able to apply locations
     632                 :             :      * (offsets from string start) to scanorig as well.
     633                 :             :      */
     634                 :        5990 :     yyext->scanorig = str;
     635                 :             : 
     636                 :             :     /* Other setup */
     637                 :        5990 :     plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
     638                 :        5990 :     yyext->plpgsql_yytoken = 0;
     639                 :             : 
     640                 :        5990 :     yyext->num_pushbacks = 0;
     641                 :             : 
     642                 :        5990 :     location_lineno_init(yyscanner);
     643                 :             : 
     644                 :        5990 :     return yyscanner;
     645                 :             : }
     646                 :             : 
     647                 :             : /*
     648                 :             :  * Called after parsing is done to clean up after plpgsql_scanner_init()
     649                 :             :  */
     650                 :             : void
     651                 :        5883 : plpgsql_scanner_finish(yyscan_t yyscanner)
     652                 :             : {
     653                 :             :     /* release storage */
     654                 :        5883 :     scanner_finish(yyscanner);
     655                 :        5883 : }
        

Generated by: LCOV version 2.0-1