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 : }
|