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 416282 : plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
161 : {
162 : int tok1;
163 : TokenAuxData aux1;
164 : int kwnum;
165 :
166 416282 : tok1 = internal_yylex(&aux1, yyscanner);
167 416282 : if (tok1 == IDENT || tok1 == PARAM)
168 : {
169 : int tok2;
170 : TokenAuxData aux2;
171 :
172 139602 : tok2 = internal_yylex(&aux2, yyscanner);
173 139602 : if (tok2 == '.')
174 : {
175 : int tok3;
176 : TokenAuxData aux3;
177 :
178 8690 : tok3 = internal_yylex(&aux3, yyscanner);
179 8690 : if (tok3 == IDENT)
180 : {
181 : int tok4;
182 : TokenAuxData aux4;
183 :
184 8488 : tok4 = internal_yylex(&aux4, yyscanner);
185 8488 : if (tok4 == '.')
186 : {
187 : int tok5;
188 : TokenAuxData aux5;
189 :
190 60 : tok5 = internal_yylex(&aux5, yyscanner);
191 60 : if (tok5 == IDENT)
192 : {
193 60 : if (plpgsql_parse_tripword(aux1.lval.str,
194 : aux3.lval.str,
195 : aux5.lval.str,
196 : &aux1.lval.wdatum,
197 : &aux1.lval.cword))
198 42 : tok1 = T_DATUM;
199 : else
200 18 : tok1 = T_CWORD;
201 : /* Adjust token length to include A.B.C */
202 60 : 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 8428 : push_back_token(tok4, &aux4, yyscanner);
224 8428 : if (plpgsql_parse_dblword(aux1.lval.str,
225 : aux3.lval.str,
226 : &aux1.lval.wdatum,
227 : &aux1.lval.cword))
228 7436 : tok1 = T_DATUM;
229 : else
230 992 : tok1 = T_CWORD;
231 : /* Adjust token length to include A.B */
232 8428 : aux1.leng = aux3.lloc - aux1.lloc + aux3.leng;
233 : }
234 : }
235 : else
236 : {
237 : /* not A.B, so just process A */
238 202 : push_back_token(tok3, &aux3, yyscanner);
239 202 : push_back_token(tok2, &aux2, yyscanner);
240 202 : if (plpgsql_parse_word(aux1.lval.str,
241 202 : yyextra->core_yy_extra.scanbuf + aux1.lloc,
242 : true,
243 : &aux1.lval.wdatum,
244 : &aux1.lval.word))
245 0 : tok1 = T_DATUM;
246 404 : else if (!aux1.lval.word.quoted &&
247 202 : (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 202 : tok1 = T_WORD;
256 : }
257 : }
258 : else
259 : {
260 : /* not A.B, so just process A */
261 130912 : 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 130912 : if (plpgsql_parse_word(aux1.lval.str,
281 130912 : yyextra->core_yy_extra.scanbuf + aux1.lloc,
282 162624 : (!AT_STMT_START(yyextra->plpgsql_yytoken) ||
283 31712 : (tok2 == '=' || tok2 == COLON_EQUALS ||
284 : tok2 == '[')),
285 : &aux1.lval.wdatum,
286 : &aux1.lval.word))
287 15962 : tok1 = T_DATUM;
288 229876 : else if (!aux1.lval.word.quoted &&
289 114926 : (kwnum = ScanKeywordLookup(aux1.lval.word.ident,
290 : &UnreservedPLKeywords)) >= 0)
291 : {
292 33724 : aux1.lval.keyword = GetScanKeyword(kwnum,
293 : &UnreservedPLKeywords);
294 33724 : tok1 = UnreservedPLKeywordTokens[kwnum];
295 : }
296 : else
297 81226 : 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 416282 : *yylvalp = aux1.lval;
315 416282 : *yyllocp = aux1.lloc;
316 416282 : yyextra->plpgsql_yyleng = aux1.leng;
317 416282 : yyextra->plpgsql_yytoken = tok1;
318 416282 : 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 130736 : plpgsql_token_length(yyscan_t yyscanner)
328 : {
329 130736 : 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 579470 : internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
340 : {
341 : int token;
342 : const char *yytext;
343 :
344 579470 : if (yyextra->num_pushbacks > 0)
345 : {
346 181448 : yyextra->num_pushbacks--;
347 181448 : token = yyextra->pushback_token[yyextra->num_pushbacks];
348 181448 : *auxdata = yyextra->pushback_auxdata[yyextra->num_pushbacks];
349 : }
350 : else
351 : {
352 398022 : token = core_yylex(&auxdata->lval.core_yystype,
353 : &auxdata->lloc,
354 : yyscanner);
355 :
356 : /* remember the length of yytext before it gets changed */
357 398022 : yytext = yyextra->core_yy_extra.scanbuf + auxdata->lloc;
358 398022 : auxdata->leng = strlen(yytext);
359 :
360 : /* Check for << >> and #, which the core considers operators */
361 398022 : if (token == Op)
362 : {
363 2818 : if (strcmp(auxdata->lval.str, "<<") == 0)
364 100 : token = LESS_LESS;
365 2718 : else if (strcmp(auxdata->lval.str, ">>") == 0)
366 92 : token = GREATER_GREATER;
367 2626 : else if (strcmp(auxdata->lval.str, "#") == 0)
368 24 : token = '#';
369 : }
370 :
371 : /* The core returns PARAM as ival, but we treat it like IDENT */
372 395204 : else if (token == PARAM)
373 : {
374 1638 : auxdata->lval.str = pstrdup(yytext);
375 : }
376 : }
377 :
378 579470 : return token;
379 : }
380 :
381 : /*
382 : * Push back a token to be re-read by next internal_yylex() call.
383 : */
384 : static void
385 181522 : push_back_token(int token, TokenAuxData *auxdata, yyscan_t yyscanner)
386 : {
387 181522 : if (yyextra->num_pushbacks >= MAX_PUSHBACKS)
388 0 : elog(ERROR, "too many tokens pushed back");
389 181522 : yyextra->pushback_token[yyextra->num_pushbacks] = token;
390 181522 : yyextra->pushback_auxdata[yyextra->num_pushbacks] = *auxdata;
391 181522 : yyextra->num_pushbacks++;
392 181522 : }
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 35430 : plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
402 : {
403 : TokenAuxData auxdata;
404 :
405 35430 : auxdata.lval = *yylvalp;
406 35430 : auxdata.lloc = *yyllocp;
407 35430 : auxdata.leng = yyextra->plpgsql_yyleng;
408 35430 : push_back_token(token, &auxdata, yyscanner);
409 35430 : }
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 76 : plpgsql_token_is_unreserved_keyword(int token)
419 : {
420 : int i;
421 :
422 6396 : for (i = 0; i < lengthof(UnreservedPLKeywordTokens); i++)
423 : {
424 6324 : if (UnreservedPLKeywordTokens[i] == token)
425 4 : return true;
426 : }
427 72 : return false;
428 : }
429 :
430 : /*
431 : * Append the function text starting at startlocation and extending to
432 : * (not including) endlocation onto the existing contents of "buf".
433 : */
434 : void
435 52472 : plpgsql_append_source_text(StringInfo buf,
436 : int startlocation, int endlocation,
437 : yyscan_t yyscanner)
438 : {
439 : Assert(startlocation <= endlocation);
440 52472 : appendBinaryStringInfo(buf, yyextra->scanorig + startlocation,
441 : endlocation - startlocation);
442 52472 : }
443 :
444 : /*
445 : * Peek one token ahead in the input stream. Only the token code is
446 : * made available, not any of the auxiliary info such as location.
447 : *
448 : * NB: no variable or unreserved keyword lookup is performed here, they will
449 : * be returned as IDENT. Reserved keywords are resolved as usual.
450 : */
451 : int
452 6060 : plpgsql_peek(yyscan_t yyscanner)
453 : {
454 : int tok1;
455 : TokenAuxData aux1;
456 :
457 6060 : tok1 = internal_yylex(&aux1, yyscanner);
458 6060 : push_back_token(tok1, &aux1, yyscanner);
459 6060 : return tok1;
460 : }
461 :
462 : /*
463 : * Peek two tokens ahead in the input stream. The first token and its
464 : * location in the query are returned in *tok1_p and *tok1_loc, second token
465 : * and its location in *tok2_p and *tok2_loc.
466 : *
467 : * NB: no variable or unreserved keyword lookup is performed here, they will
468 : * be returned as IDENT. Reserved keywords are resolved as usual.
469 : */
470 : void
471 144 : plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
472 : {
473 : int tok1,
474 : tok2;
475 : TokenAuxData aux1,
476 : aux2;
477 :
478 144 : tok1 = internal_yylex(&aux1, yyscanner);
479 144 : tok2 = internal_yylex(&aux2, yyscanner);
480 :
481 144 : *tok1_p = tok1;
482 144 : if (tok1_loc)
483 144 : *tok1_loc = aux1.lloc;
484 144 : *tok2_p = tok2;
485 144 : if (tok2_loc)
486 0 : *tok2_loc = aux2.lloc;
487 :
488 144 : push_back_token(tok2, &aux2, yyscanner);
489 144 : push_back_token(tok1, &aux1, yyscanner);
490 144 : }
491 :
492 : /*
493 : * plpgsql_scanner_errposition
494 : * Report an error cursor position, if possible.
495 : *
496 : * This is expected to be used within an ereport() call. The return value
497 : * is a dummy (always 0, in fact).
498 : *
499 : * Note that this can only be used for messages emitted during initial
500 : * parsing of a plpgsql function, since it requires the scanorig string
501 : * to still be available.
502 : */
503 : int
504 174 : plpgsql_scanner_errposition(int location, yyscan_t yyscanner)
505 : {
506 : int pos;
507 :
508 174 : if (location < 0 || yyextra->scanorig == NULL)
509 0 : return 0; /* no-op if location is unknown */
510 :
511 : /* Convert byte offset to character number */
512 174 : pos = pg_mbstrlen_with_len(yyextra->scanorig, location) + 1;
513 : /* And pass it to the ereport mechanism */
514 174 : (void) internalerrposition(pos);
515 : /* Also pass the function body string */
516 174 : return internalerrquery(yyextra->scanorig);
517 : }
518 :
519 : /*
520 : * plpgsql_yyerror
521 : * Report a lexer or grammar error.
522 : *
523 : * The message's cursor position refers to the current token (the one
524 : * last returned by plpgsql_yylex()).
525 : * This is OK for syntax error messages from the Bison parser, because Bison
526 : * parsers report error as soon as the first unparsable token is reached.
527 : * Beware of using yyerror for other purposes, as the cursor position might
528 : * be misleading!
529 : *
530 : * (The second argument is enforced by Bison to match the second argument of
531 : * yyparse(), but it is not used here.)
532 : */
533 : void
534 8 : plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message)
535 : {
536 8 : char *yytext = yyextra->core_yy_extra.scanbuf + *yyllocp;
537 :
538 8 : if (*yytext == '\0')
539 : {
540 0 : ereport(ERROR,
541 : (errcode(ERRCODE_SYNTAX_ERROR),
542 : /* translator: %s is typically the translation of "syntax error" */
543 : errmsg("%s at end of input", _(message)),
544 : plpgsql_scanner_errposition(*yyllocp, yyscanner)));
545 : }
546 : else
547 : {
548 : /*
549 : * If we have done any lookahead then flex will have restored the
550 : * character after the end-of-token. Zap it again so that we report
551 : * only the single token here. This modifies scanbuf but we no longer
552 : * care about that.
553 : */
554 8 : yytext[yyextra->plpgsql_yyleng] = '\0';
555 :
556 8 : ereport(ERROR,
557 : (errcode(ERRCODE_SYNTAX_ERROR),
558 : /* translator: first %s is typically the translation of "syntax error" */
559 : errmsg("%s at or near \"%s\"", _(message), yytext),
560 : plpgsql_scanner_errposition(*yyllocp, yyscanner)));
561 : }
562 : }
563 :
564 : /*
565 : * Given a location (a byte offset in the function source text),
566 : * return a line number.
567 : *
568 : * We expect that this is typically called for a sequence of increasing
569 : * location values, so optimize accordingly by tracking the endpoints
570 : * of the "current" line.
571 : */
572 : int
573 60376 : plpgsql_location_to_lineno(int location, yyscan_t yyscanner)
574 : {
575 : const char *loc;
576 :
577 60376 : if (location < 0 || yyextra->scanorig == NULL)
578 0 : return 0; /* garbage in, garbage out */
579 60376 : loc = yyextra->scanorig + location;
580 :
581 : /* be correct, but not fast, if input location goes backwards */
582 60376 : if (loc < yyextra->cur_line_start)
583 17528 : location_lineno_init(yyscanner);
584 :
585 276362 : while (yyextra->cur_line_end != NULL && loc > yyextra->cur_line_end)
586 : {
587 215986 : yyextra->cur_line_start = yyextra->cur_line_end + 1;
588 215986 : yyextra->cur_line_num++;
589 215986 : yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
590 : }
591 :
592 60376 : return yyextra->cur_line_num;
593 : }
594 :
595 : /* initialize or reset the state for plpgsql_location_to_lineno */
596 : static void
597 27306 : location_lineno_init(yyscan_t yyscanner)
598 : {
599 27306 : yyextra->cur_line_start = yyextra->scanorig;
600 27306 : yyextra->cur_line_num = 1;
601 :
602 27306 : yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
603 27306 : }
604 :
605 : /* return the most recently computed lineno */
606 : int
607 60 : plpgsql_latest_lineno(yyscan_t yyscanner)
608 : {
609 60 : return yyextra->cur_line_num;
610 : }
611 :
612 :
613 : /*
614 : * Called before any actual parsing is done
615 : *
616 : * Note: the passed "str" must remain valid until plpgsql_scanner_finish().
617 : * Although it is not fed directly to flex, we need the original string
618 : * to cite in error messages.
619 : */
620 : yyscan_t
621 9778 : plpgsql_scanner_init(const char *str)
622 : {
623 : yyscan_t yyscanner;
624 9778 : struct plpgsql_yy_extra_type *yyext = palloc0_object(struct plpgsql_yy_extra_type);
625 :
626 : /* Start up the core scanner */
627 9778 : yyscanner = scanner_init(str, (core_yy_extra_type *) yyext,
628 : &ReservedPLKeywords, ReservedPLKeywordTokens);
629 :
630 : /*
631 : * scanorig points to the original string, which unlike the scanner's
632 : * scanbuf won't be modified on-the-fly by flex. Notice that although
633 : * yytext points into scanbuf, we rely on being able to apply locations
634 : * (offsets from string start) to scanorig as well.
635 : */
636 9778 : yyext->scanorig = str;
637 :
638 : /* Other setup */
639 9778 : plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
640 9778 : yyext->plpgsql_yytoken = 0;
641 :
642 9778 : yyext->num_pushbacks = 0;
643 :
644 9778 : location_lineno_init(yyscanner);
645 :
646 9778 : return yyscanner;
647 : }
648 :
649 : /*
650 : * Called after parsing is done to clean up after plpgsql_scanner_init()
651 : */
652 : void
653 9600 : plpgsql_scanner_finish(yyscan_t yyscanner)
654 : {
655 : /* release storage */
656 9600 : scanner_finish(yyscanner);
657 9600 : }
|