Branch data Line data Source code
1 : : %top{
2 : : /*-------------------------------------------------------------------------
3 : : *
4 : : * psqlscan.l
5 : : * lexical scanner for SQL commands
6 : : *
7 : : * This lexer used to be part of psql, and that heritage is reflected in
8 : : * the file name as well as function and typedef names, though it can now
9 : : * be used by other frontend programs as well. It's also possible to extend
10 : : * this lexer with a compatible add-on lexer to handle program-specific
11 : : * backslash commands.
12 : : *
13 : : * This code is mainly concerned with determining where the end of a SQL
14 : : * statement is: we are looking for semicolons that are not within quotes,
15 : : * comments, or parentheses. The most reliable way to handle this is to
16 : : * borrow the backend's flex lexer rules, lock, stock, and barrel. The rules
17 : : * below are (except for a few) the same as the backend's, but their actions
18 : : * are just ECHO whereas the backend's actions generally do other things.
19 : : *
20 : : * XXX The rules in this file must be kept in sync with the backend lexer!!!
21 : : *
22 : : * XXX Avoid creating backtracking cases --- see the backend lexer for info.
23 : : *
24 : : * See psqlscan_int.h for additional commentary.
25 : : *
26 : : *
27 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
28 : : * Portions Copyright (c) 1994, Regents of the University of California
29 : : *
30 : : * IDENTIFICATION
31 : : * src/fe_utils/psqlscan.l
32 : : *
33 : : *-------------------------------------------------------------------------
34 : : */
35 : : #include "postgres_fe.h"
36 : :
37 : : #include "common/logging.h"
38 : : #include "fe_utils/psqlscan.h"
39 : :
40 : : #include "libpq-fe.h"
41 : : }
42 : :
43 : : %{
44 : :
45 : : /* LCOV_EXCL_START */
46 : :
47 : : #include "fe_utils/psqlscan_int.h"
48 : :
49 : : /*
50 : : * We must have a typedef YYSTYPE for yylex's first argument, but this lexer
51 : : * doesn't presently make use of that argument, so just declare it as int.
52 : : */
53 : : typedef int YYSTYPE;
54 : :
55 : :
56 : : /* Return values from yylex() */
57 : : #define LEXRES_EOL 0 /* end of input */
58 : : #define LEXRES_SEMI 1 /* command-terminating semicolon found */
59 : : #define LEXRES_BACKSLASH 2 /* backslash command start */
60 : :
61 : :
62 : : #define ECHO psqlscan_emit(cur_state, yytext, yyleng)
63 : :
64 : : static bool psqlscan_is_copy_from_stdin(PsqlScanState state);
65 : : static void psqlscan_track_identifier(PsqlScanState state,
66 : : const char *identifier);
67 : :
68 : : %}
69 : :
70 : : %option reentrant
71 : : %option bison-bridge
72 : : %option 8bit
73 : : %option never-interactive
74 : : %option nodefault
75 : : %option noinput
76 : : %option nounput
77 : : %option noyywrap
78 : : %option warn
79 : : %option prefix="psql_yy"
80 : :
81 : : /*
82 : : * Set the type of yyextra; we use it as a pointer back to the containing
83 : : * PsqlScanState.
84 : : */
85 : : %option extra-type="PsqlScanState"
86 : :
87 : : /*
88 : : * All of the following definitions and rules should exactly match
89 : : * src/backend/parser/scan.l so far as the flex patterns are concerned.
90 : : * The rule bodies are just ECHO as opposed to what the backend does,
91 : : * however. (But be sure to duplicate code that affects the lexing process,
92 : : * such as BEGIN() and yyless().) Also, psqlscan uses a single <<EOF>> rule
93 : : * whereas scan.l has a separate one for each exclusive state.
94 : : */
95 : :
96 : : /*
97 : : * OK, here is a short description of lex/flex rules behavior.
98 : : * The longest pattern which matches an input string is always chosen.
99 : : * For equal-length patterns, the first occurring in the rules list is chosen.
100 : : * INITIAL is the starting state, to which all non-conditional rules apply.
101 : : * Exclusive states change parsing rules while the state is active. When in
102 : : * an exclusive state, only those rules defined for that state apply.
103 : : *
104 : : * We use exclusive states for quoted strings, extended comments,
105 : : * and to eliminate parsing troubles for numeric strings.
106 : : * Exclusive states:
107 : : * <xb> bit string literal
108 : : * <xc> extended C-style comments
109 : : * <xd> delimited identifiers (double-quoted identifiers)
110 : : * <xh> hexadecimal byte string
111 : : * <xq> standard quoted strings
112 : : * <xqs> quote stop (detect continued strings)
113 : : * <xe> extended quoted strings (support backslash escape sequences)
114 : : * <xdolq> $foo$ quoted strings
115 : : * <xui> quoted identifier with Unicode escapes
116 : : * <xus> quoted string with Unicode escapes
117 : : *
118 : : * Note: we intentionally don't mimic the backend's <xeu> state; we have
119 : : * no need to distinguish it from <xe> state, and no good way to get out
120 : : * of it in error cases. The backend just throws yyerror() in those
121 : : * cases, but that's not an option here.
122 : : */
123 : :
124 : : %x xb
125 : : %x xc
126 : : %x xd
127 : : %x xh
128 : : %x xq
129 : : %x xqs
130 : : %x xe
131 : : %x xdolq
132 : : %x xui
133 : : %x xus
134 : :
135 : : /*
136 : : * In order to make the world safe for Windows and Mac clients as well as
137 : : * Unix ones, we accept either \n or \r as a newline. A DOS-style \r\n
138 : : * sequence will be seen as two successive newlines, but that doesn't cause
139 : : * any problems. Comments that start with -- and extend to the next
140 : : * newline are treated as equivalent to a single whitespace character.
141 : : *
142 : : * NOTE a fine point: if there is no newline following --, we will absorb
143 : : * everything to the end of the input as a comment. This is correct. Older
144 : : * versions of Postgres failed to recognize -- as a comment if the input
145 : : * did not end with a newline.
146 : : *
147 : : * non_newline_space tracks all space characters except newlines.
148 : : *
149 : : * XXX if you change the set of whitespace characters, fix scanner_isspace()
150 : : * to agree.
151 : : */
152 : :
153 : : space [ \t\n\r\f\v]
154 : : non_newline_space [ \t\f\v]
155 : : newline [\n\r]
156 : : non_newline [^\n\r]
157 : :
158 : : comment ("--"{non_newline}*)
159 : :
160 : : whitespace ({space}+|{comment})
161 : :
162 : : /*
163 : : * SQL requires at least one newline in the whitespace separating
164 : : * string literals that are to be concatenated. Silly, but who are we
165 : : * to argue? Note that {whitespace_with_newline} should not have * after
166 : : * it, whereas {whitespace} should generally have a * after it...
167 : : */
168 : :
169 : : special_whitespace ({space}+|{comment}{newline})
170 : : non_newline_whitespace ({non_newline_space}|{comment})
171 : : whitespace_with_newline ({non_newline_whitespace}*{newline}{special_whitespace}*)
172 : :
173 : : quote '
174 : : /* If we see {quote} then {quotecontinue}, the quoted string continues */
175 : : quotecontinue {whitespace_with_newline}{quote}
176 : :
177 : : /*
178 : : * {quotecontinuefail} is needed to avoid lexer backup when we fail to match
179 : : * {quotecontinue}. It might seem that this could just be {whitespace}*,
180 : : * but if there's a dash after {whitespace_with_newline}, it must be consumed
181 : : * to see if there's another dash --- which would start a {comment} and thus
182 : : * allow continuation of the {quotecontinue} token.
183 : : */
184 : : quotecontinuefail {whitespace}*"-"?
185 : :
186 : : /* Bit string
187 : : * It is tempting to scan the string for only those characters
188 : : * which are allowed. However, this leads to silently swallowed
189 : : * characters if illegal characters are included in the string.
190 : : * For example, if xbinside is [01] then B'ABCD' is interpreted
191 : : * as a zero-length string, and the ABCD' is lost!
192 : : * Better to pass the string forward and let the input routines
193 : : * validate the contents.
194 : : */
195 : : xbstart [bB]{quote}
196 : : xbinside [^']*
197 : :
198 : : /* Hexadecimal byte string */
199 : : xhstart [xX]{quote}
200 : : xhinside [^']*
201 : :
202 : : /* National character */
203 : : xnstart [nN]{quote}
204 : :
205 : : /* Quoted string that allows backslash escapes */
206 : : xestart [eE]{quote}
207 : : xeinside [^\\']+
208 : : xeescape [\\][^0-7]
209 : : xeoctesc [\\][0-7]{1,3}
210 : : xehexesc [\\]x[0-9A-Fa-f]{1,2}
211 : : xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
212 : : xeunicodefail [\\](u[0-9A-Fa-f]{0,3}|U[0-9A-Fa-f]{0,7})
213 : :
214 : : /* Extended quote
215 : : * xqdouble implements embedded quote, ''''
216 : : */
217 : : xqstart {quote}
218 : : xqdouble {quote}{quote}
219 : : xqinside [^']+
220 : :
221 : : /* $foo$ style quotes ("dollar quoting")
222 : : * The quoted string starts with $foo$ where "foo" is an optional string
223 : : * in the form of an identifier, except that it may not contain "$",
224 : : * and extends to the first occurrence of an identical string.
225 : : * There is *no* processing of the quoted text.
226 : : *
227 : : * {dolqfailed} is an error rule to avoid scanner backup when {dolqdelim}
228 : : * fails to match its trailing "$".
229 : : */
230 : : dolq_start [A-Za-z\200-\377_]
231 : : dolq_cont [A-Za-z\200-\377_0-9]
232 : : dolqdelim \$({dolq_start}{dolq_cont}*)?\$
233 : : dolqfailed \${dolq_start}{dolq_cont}*
234 : : dolqinside [^$]+
235 : :
236 : : /* Double quote
237 : : * Allows embedded spaces and other special characters into identifiers.
238 : : */
239 : : dquote \"
240 : : xdstart {dquote}
241 : : xdstop {dquote}
242 : : xddouble {dquote}{dquote}
243 : : xdinside [^"]+
244 : :
245 : : /* Quoted identifier with Unicode escapes */
246 : : xuistart [uU]&{dquote}
247 : :
248 : : /* Quoted string with Unicode escapes */
249 : : xusstart [uU]&{quote}
250 : :
251 : : /* error rule to avoid backup */
252 : : xufailed [uU]&
253 : :
254 : :
255 : : /* C-style comments
256 : : *
257 : : * The "extended comment" syntax closely resembles allowable operator syntax.
258 : : * The tricky part here is to get lex to recognize a string starting with
259 : : * slash-star as a comment, when interpreting it as an operator would produce
260 : : * a longer match --- remember lex will prefer a longer match! Also, if we
261 : : * have something like plus-slash-star, lex will think this is a 3-character
262 : : * operator whereas we want to see it as a + operator and a comment start.
263 : : * The solution is two-fold:
264 : : * 1. append {op_chars}* to xcstart so that it matches as much text as
265 : : * {operator} would. Then the tie-breaker (first matching rule of same
266 : : * length) ensures xcstart wins. We put back the extra stuff with yyless()
267 : : * in case it contains a star-slash that should terminate the comment.
268 : : * 2. In the operator rule, check for slash-star within the operator, and
269 : : * if found throw it back with yyless(). This handles the plus-slash-star
270 : : * problem.
271 : : * Dash-dash comments have similar interactions with the operator rule.
272 : : */
273 : : xcstart \/\*{op_chars}*
274 : : xcstop \*+\/
275 : : xcinside [^*/]+
276 : :
277 : : ident_start [A-Za-z\200-\377_]
278 : : ident_cont [A-Za-z\200-\377_0-9\$]
279 : :
280 : : identifier {ident_start}{ident_cont}*
281 : :
282 : : /* Assorted special-case operators and operator-like tokens */
283 : : typecast "::"
284 : : dot_dot \.\.
285 : : colon_equals ":="
286 : :
287 : : /*
288 : : * These operator-like tokens (unlike the above ones) also match the {operator}
289 : : * rule, which means that they might be overridden by a longer match if they
290 : : * are followed by a comment start or a + or - character. Accordingly, if you
291 : : * add to this list, you must also add corresponding code to the {operator}
292 : : * block to return the correct token in such cases. (This is not needed in
293 : : * psqlscan.l since the token value is ignored there.)
294 : : */
295 : : equals_greater "=>"
296 : : less_equals "<="
297 : : greater_equals ">="
298 : : less_greater "<>"
299 : : not_equals "!="
300 : : /* Note there is no need for left_arrow, since "<-" is not a single operator. */
301 : : right_arrow "->"
302 : :
303 : : /*
304 : : * "self" is the set of chars that should be returned as single-character
305 : : * tokens. "op_chars" is the set of chars that can make up "Op" tokens,
306 : : * which can be one or more characters long (but if a single-char token
307 : : * appears in the "self" set, it is not to be returned as an Op). Note
308 : : * that the sets overlap, but each has some chars that are not in the other.
309 : : *
310 : : * If you change either set, adjust the character lists appearing in the
311 : : * rule for "operator"!
312 : : */
313 : : self [,()\[\].;\:\|\+\-\*\/\%\^\<\>\=]
314 : : op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
315 : : operator {op_chars}+
316 : :
317 : : /*
318 : : * Numbers
319 : : *
320 : : * Unary minus is not part of a number here. Instead we pass it separately to
321 : : * the parser, and there it gets coerced via doNegate().
322 : : *
323 : : * {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
324 : : *
325 : : * {realfail} is added to prevent the need for scanner
326 : : * backup when the {real} rule fails to match completely.
327 : : */
328 : : decdigit [0-9]
329 : : hexdigit [0-9A-Fa-f]
330 : : octdigit [0-7]
331 : : bindigit [0-1]
332 : :
333 : : decinteger {decdigit}(_?{decdigit})*
334 : : hexinteger 0[xX](_?{hexdigit})+
335 : : octinteger 0[oO](_?{octdigit})+
336 : : bininteger 0[bB](_?{bindigit})+
337 : :
338 : : hexfail 0[xX]_?
339 : : octfail 0[oO]_?
340 : : binfail 0[bB]_?
341 : :
342 : : numeric (({decinteger}\.{decinteger}?)|(\.{decinteger}))
343 : : numericfail {decinteger}\.\.
344 : :
345 : : real ({decinteger}|{numeric})[Ee][-+]?{decinteger}
346 : : realfail ({decinteger}|{numeric})[Ee][-+]
347 : :
348 : : /* Positional parameters don't accept underscores. */
349 : : param \${decdigit}+
350 : :
351 : : /*
352 : : * An identifier immediately following an integer literal is disallowed because
353 : : * in some cases it's ambiguous what is meant: for example, 0x1234 could be
354 : : * either a hexinteger or a decinteger "0" and an identifier "x1234". We can
355 : : * detect such problems by seeing if integer_junk matches a longer substring
356 : : * than any of the XXXinteger patterns (decinteger, hexinteger, octinteger,
357 : : * bininteger). One "junk" pattern is sufficient because
358 : : * {decinteger}{identifier} will match all the same strings we'd match with
359 : : * {hexinteger}{identifier} etc.
360 : : *
361 : : * Note that the rule for integer_junk must appear after the ones for
362 : : * XXXinteger to make this work correctly: 0x1234 will match both hexinteger
363 : : * and integer_junk, and we need hexinteger to be chosen in that case.
364 : : *
365 : : * Also disallow strings matched by numeric_junk, real_junk and param_junk
366 : : * for consistency.
367 : : */
368 : : integer_junk {decinteger}{identifier}
369 : : numeric_junk {numeric}{identifier}
370 : : real_junk {real}{identifier}
371 : : param_junk \${decdigit}+{identifier}
372 : :
373 : : /* psql-specific: characters allowed in variable names */
374 : : variable_char [A-Za-z\200-\377_0-9]
375 : :
376 : : other .
377 : :
378 : : /*
379 : : * Dollar quoted strings are totally opaque, and no escaping is done on them.
380 : : * Other quoted strings must allow some special characters such as single-quote
381 : : * and newline.
382 : : * Embedded single-quotes are implemented both in the SQL standard
383 : : * style of two adjacent single quotes "''" and in the Postgres/Java style
384 : : * of escaped-quote "\'".
385 : : * Other embedded escaped characters are matched explicitly and the leading
386 : : * backslash is dropped from the string.
387 : : * Note that xcstart must appear before operator, as explained above!
388 : : * Also whitespace (comment) must appear before operator.
389 : : */
390 : :
391 : : %%
392 : :
393 : : %{
394 : : /* Declare some local variables inside yylex(), for convenience */
395 : : PsqlScanState cur_state = yyextra;
396 : 815155 : PQExpBuffer output_buf = cur_state->output_buf;
397 : 815155 :
398 : : /*
399 : : * Force flex into the state indicated by start_state. This has a
400 : : * couple of purposes: it lets some of the functions below set a new
401 : : * starting state without ugly direct access to flex variables, and it
402 : : * allows us to transition from one flex lexer to another so that we
403 : : * can lex different parts of the source string using separate lexers.
404 : : */
405 : : BEGIN(cur_state->start_state);
406 : 815155 : %}
407 : :
408 : : {whitespace} {
409 : : /*
410 : : * Note that the whitespace rule includes both true
411 : : * whitespace and single-line ("--" style) comments.
412 : : * We suppress whitespace until we have collected some
413 : : * non-whitespace data. (This interacts with some
414 : : * decisions in MainLoop(); see there for details.)
415 : : */
416 : : if (output_buf->len > 0)
417 [ + + ]: 1961231 : ECHO;
418 : 1846069 : }
419 : :
420 : 1961231 : {xcstart} {
421 : 486 : cur_state->xcdepth = 0;
422 : 486 : BEGIN(xc);
423 : 486 : /* Put back any characters past slash-star; see above */
424 : : yyless(2);
425 : 486 : ECHO;
426 : 486 : }
427 : :
428 : 486 : <xc>{
429 : : {xcstart} {
430 : 12 : cur_state->xcdepth++;
431 : 12 : /* Put back any characters past slash-star; see above */
432 : : yyless(2);
433 : 12 : ECHO;
434 : 12 : }
435 : :
436 : 12 : {xcstop} {
437 : 498 : if (cur_state->xcdepth <= 0)
438 [ + + ]: 498 : BEGIN(INITIAL);
439 : 486 : else
440 : : cur_state->xcdepth--;
441 : 12 : ECHO;
442 : 498 : }
443 : :
444 : 498 : {xcinside} {
445 : 1150 : ECHO;
446 : 1150 : }
447 : :
448 : 1150 : {op_chars} {
449 : 352 : ECHO;
450 : 352 : }
451 : :
452 : 352 : \*+ {
453 : 0 : ECHO;
454 : 0 : }
455 : : } /* <xc> */
456 : 0 :
457 : : {xbstart} {
458 : 508 : BEGIN(xb);
459 : 508 : ECHO;
460 : 508 : }
461 : : <xh>{xhinside} |
462 : 508 : <xb>{xbinside} {
463 : 2703 : ECHO;
464 : 2703 : }
465 : :
466 : 2703 : {xhstart} {
467 : 2215 : /* Hexadecimal bit type.
468 : : * At some point we should simply pass the string
469 : : * forward to the parser and label it there.
470 : : * In the meantime, place a leading "x" on the string
471 : : * to mark it for the input routine as a hex string.
472 : : */
473 : : BEGIN(xh);
474 : 2215 : ECHO;
475 : 2215 : }
476 : :
477 : 2215 : {xnstart} {
478 : 0 : yyless(1); /* eat only 'n' this time */
479 : 0 : ECHO;
480 : 0 : }
481 : :
482 : 0 : {xqstart} {
483 : 163105 : if (cur_state->std_strings)
484 [ + - ]: 163105 : BEGIN(xq);
485 : 163105 : else
486 : : BEGIN(xe);
487 : 0 : ECHO;
488 : 163105 : }
489 : : {xestart} {
490 : 163105 : BEGIN(xe);
491 : 933 : ECHO;
492 : 933 : }
493 : : {xusstart} {
494 : 933 : BEGIN(xus);
495 : 468 : ECHO;
496 : 468 : }
497 : :
498 : 468 : <xb,xh,xq,xe,xus>{quote} {
499 : 167229 : /*
500 : : * When we are scanning a quoted string and see an end
501 : : * quote, we must look ahead for a possible continuation.
502 : : * If we don't see one, we know the end quote was in fact
503 : : * the end of the string. To reduce the lexer table size,
504 : : * we use a single "xqs" state to do the lookahead for all
505 : : * types of strings.
506 : : */
507 : : cur_state->state_before_str_stop = YYSTATE;
508 : 167229 : BEGIN(xqs);
509 : 167229 : ECHO;
510 : 167229 : }
511 : : <xqs>{quotecontinue} {
512 : 167229 : /*
513 : 0 : * Found a quote continuation, so return to the in-quote
514 : : * state and continue scanning the literal. Nothing is
515 : : * added to the literal's contents.
516 : : */
517 : : BEGIN(cur_state->state_before_str_stop);
518 : 0 : ECHO;
519 : 0 : }
520 : : <xqs>{quotecontinuefail} |
521 : 0 : <xqs>{other} {
522 : 166398 : /*
523 : : * Failed to see a quote continuation. Throw back
524 : : * everything after the end quote, and handle the string
525 : : * according to the state we were in previously.
526 : : */
527 : : yyless(0);
528 : 166398 : BEGIN(INITIAL);
529 : 166398 : /* There's nothing to echo ... */
530 : : }
531 : :
532 : 166398 : <xq,xe,xus>{xqdouble} {
533 : 4137 : ECHO;
534 : 4137 : }
535 : : <xq,xus>{xqinside} {
536 : 4137 : ECHO;
537 : 170917 : }
538 : : <xe>{xeinside} {
539 : 170917 : ECHO;
540 : 1691 : }
541 : : <xe>{xeunicode} {
542 : 1691 : ECHO;
543 : 132 : }
544 : : <xe>{xeunicodefail} {
545 : 132 : ECHO;
546 : 8 : }
547 : : <xe>{xeescape} {
548 : 8 : ECHO;
549 : 1018 : }
550 : : <xe>{xeoctesc} {
551 : 1018 : ECHO;
552 : 14 : }
553 : : <xe>{xehexesc} {
554 : 14 : ECHO;
555 : 6 : }
556 : : <xe>. {
557 : 6 : /* This is only needed for \ just before EOF */
558 : 0 : ECHO;
559 : 0 : }
560 : :
561 : 0 : {dolqdelim} {
562 : 4646 : cur_state->dolqstart = pg_strdup(yytext);
563 : 4646 : BEGIN(xdolq);
564 : 4646 : ECHO;
565 : 4646 : }
566 : : {dolqfailed} {
567 : 4646 : /* throw back all but the initial "$" */
568 : 0 : yyless(1);
569 : 0 : ECHO;
570 : 0 : }
571 : : <xdolq>{dolqdelim} {
572 : 0 : if (strcmp(yytext, cur_state->dolqstart) == 0)
573 [ + + ]: 4862 : {
574 : : free(cur_state->dolqstart);
575 : 4646 : cur_state->dolqstart = NULL;
576 : 4646 : BEGIN(INITIAL);
577 : 4646 : }
578 : : else
579 : : {
580 : : /*
581 : : * When we fail to match $...$ to dolqstart, transfer
582 : : * the $... part to the output, but put back the final
583 : : * $ for rescanning. Consider $delim$...$junk$delim$
584 : : */
585 : : yyless(yyleng - 1);
586 : 216 : }
587 : : ECHO;
588 : 4862 : }
589 : : <xdolq>{dolqinside} {
590 : 4862 : ECHO;
591 : 24523 : }
592 : : <xdolq>{dolqfailed} {
593 : 24523 : ECHO;
594 : 574 : }
595 : : <xdolq>. {
596 : 574 : /* This is only needed for $ inside the quoted text */
597 : 1653 : ECHO;
598 : 1653 : }
599 : :
600 : 1653 : {xdstart} {
601 : 7581 : BEGIN(xd);
602 : 7581 : ECHO;
603 : 7581 : }
604 : : {xuistart} {
605 : 7581 : BEGIN(xui);
606 : 16 : ECHO;
607 : 16 : }
608 : : <xd>{xdstop} {
609 : 16 : BEGIN(INITIAL);
610 : 7581 : ECHO;
611 : 7581 : }
612 : : <xui>{dquote} {
613 : 7581 : BEGIN(INITIAL);
614 : 16 : ECHO;
615 : 16 : }
616 : : <xd,xui>{xddouble} {
617 : 16 : ECHO;
618 : 75 : }
619 : : <xd,xui>{xdinside} {
620 : 75 : ECHO;
621 : 7662 : }
622 : :
623 : 7662 : {xufailed} {
624 : 0 : /* throw back all but the initial u/U */
625 : : yyless(1);
626 : 0 : ECHO;
627 : 0 : }
628 : :
629 : 0 : {typecast} {
630 : 38117 : ECHO;
631 : 38117 : }
632 : :
633 : 38117 : {dot_dot} {
634 : 0 : ECHO;
635 : 0 : }
636 : :
637 : 0 : {colon_equals} {
638 : 1673 : ECHO;
639 : 1673 : }
640 : :
641 : 1673 : {equals_greater} {
642 : 1375 : ECHO;
643 : 1375 : }
644 : :
645 : 1375 : {less_equals} {
646 : 1455 : ECHO;
647 : 1455 : }
648 : :
649 : 1455 : {greater_equals} {
650 : 4253 : ECHO;
651 : 4253 : }
652 : :
653 : 4253 : {less_greater} {
654 : 969 : ECHO;
655 : 969 : }
656 : :
657 : 969 : {not_equals} {
658 : 1490 : ECHO;
659 : 1490 : }
660 : :
661 : 1490 : {right_arrow} {
662 : 789 : ECHO;
663 : 789 : }
664 : :
665 : 789 : /*
666 : : * These rules are specific to psql --- they implement parenthesis
667 : : * counting and detection of command-ending semicolon. These must
668 : : * appear before the {self} rule so that they take precedence over it.
669 : : */
670 : :
671 : 263520 : "(" {
672 : : cur_state->paren_depth++;
673 : 263520 : ECHO;
674 : 263520 : }
675 : :
676 : 263520 : ")" {
677 : 263511 : if (cur_state->paren_depth > 0)
678 [ + - ]: 263511 : cur_state->paren_depth--;
679 : 263511 : ECHO;
680 : 263511 : }
681 : :
682 : 263511 : ";" {
683 : 249505 : ECHO;
684 : 249505 : if (cur_state->paren_depth == 0 &&
685 [ + + ]: 249505 : cur_state->begin_depth == 0)
686 [ + + ]: 249469 : {
687 : : /* Remember if this subcommand was COPY FROM STDIN */
688 : : if (psqlscan_is_copy_from_stdin(cur_state))
689 [ + + ]: 249326 : cur_state->copy_stdin_count++;
690 : 882 : /* Terminate lexing temporarily */
691 : : cur_state->start_state = YY_START;
692 : 249326 : cur_state->init_idents_count = 0;
693 : 249326 : return LEXRES_SEMI;
694 : 249326 : }
695 : : }
696 : :
697 : 179 : /*
698 : : * psql-specific rules to handle backslash commands and variable
699 : : * substitution. We want these before {self}, also.
700 : : */
701 : :
702 : 512 : "\\"[;:] {
703 : : /* Force a semi-colon or colon into the query buffer */
704 : : psqlscan_emit(cur_state, yytext + 1, 1);
705 : 512 : /* Reset BEGIN/END/COPY tracking if semi at outer level */
706 : : if (yytext[1] == ';' &&
707 [ + - ]: 512 : cur_state->paren_depth == 0 &&
708 [ + - ]: 512 : cur_state->begin_depth == 0)
709 [ + - ]: 512 : {
710 : : /* Remember if this subcommand was COPY FROM STDIN */
711 : : if (psqlscan_is_copy_from_stdin(cur_state))
712 [ + + ]: 512 : cur_state->copy_stdin_count++;
713 : 12 : cur_state->init_idents_count = 0;
714 : 512 : }
715 : : }
716 : :
717 : 512 : "\\" {
718 : 33001 : /* Terminate lexing temporarily */
719 : : cur_state->start_state = YY_START;
720 : 33001 : return LEXRES_BACKSLASH;
721 : 33001 : }
722 : :
723 : : :{variable_char}+ {
724 : 1896 : /* Possible psql variable substitution */
725 : : char *varname;
726 : : char *value;
727 : :
728 : : varname = psqlscan_extract_substring(cur_state,
729 : 1896 : yytext + 1,
730 : 1896 : yyleng - 1);
731 : 1896 : if (cur_state->callbacks->get_variable)
732 [ + + ]: 1896 : value = cur_state->callbacks->get_variable(varname,
733 : 1300 : PQUOTE_PLAIN,
734 : : cur_state->cb_passthrough);
735 : : else
736 : : value = NULL;
737 : 596 :
738 : : if (value)
739 [ + + ]: 1896 : {
740 : : /* It is a variable, check for recursion */
741 : : if (psqlscan_var_is_current_source(cur_state, varname))
742 [ - + ]: 992 : {
743 : : /* Recursive expansion --- don't go there */
744 : : pg_log_warning("skipping recursive expansion of variable \"%s\"",
745 : 0 : varname);
746 : : /* Instead copy the string as is */
747 : : ECHO;
748 : 0 : }
749 : : else
750 : : {
751 : : /* OK, perform substitution */
752 : : psqlscan_push_new_buffer(cur_state, value, varname);
753 : 992 : /* yy_scan_string already made buffer active */
754 : : }
755 : : free(value);
756 : 992 : }
757 : : else
758 : : {
759 : : /*
760 : : * if the variable doesn't exist we'll copy the string
761 : : * as is
762 : : */
763 : : ECHO;
764 : 904 : }
765 : :
766 : : free(varname);
767 : 1896 : }
768 : :
769 : 1896 : :'{variable_char}+' {
770 : 660 : psqlscan_escape_variable(cur_state, yytext, yyleng,
771 : 660 : PQUOTE_SQL_LITERAL);
772 : : }
773 : :
774 : 660 : :\"{variable_char}+\" {
775 : 21 : psqlscan_escape_variable(cur_state, yytext, yyleng,
776 : 21 : PQUOTE_SQL_IDENT);
777 : : }
778 : :
779 : 21 : :\{\?{variable_char}+\} {
780 : 8 : psqlscan_test_variable(cur_state, yytext, yyleng);
781 : 8 : }
782 : :
783 : 8 : /*
784 : : * These rules just avoid the need for scanner backup if one of the
785 : : * three rules above fails to match completely.
786 : : */
787 : :
788 : 0 : :'{variable_char}* {
789 : : /* Throw back everything but the colon */
790 : : yyless(1);
791 : 0 : ECHO;
792 : 0 : }
793 : :
794 : 0 : :\"{variable_char}* {
795 : 0 : /* Throw back everything but the colon */
796 : : yyless(1);
797 : 0 : ECHO;
798 : 0 : }
799 : :
800 : 0 : :\{\?{variable_char}* {
801 : 0 : /* Throw back everything but the colon */
802 : : yyless(1);
803 : 0 : ECHO;
804 : 0 : }
805 : : :\{ {
806 : 0 : /* Throw back everything but the colon */
807 : 0 : yyless(1);
808 : 0 : ECHO;
809 : 0 : }
810 : :
811 : 0 : /*
812 : : * Back to backend-compatible rules.
813 : : */
814 : :
815 : 461801 : {self} {
816 : : ECHO;
817 : 461801 : }
818 : :
819 : 461801 : {operator} {
820 : 13046 : /*
821 : : * Check for embedded slash-star or dash-dash; those
822 : : * are comment starts, so operator must stop there.
823 : : * Note that slash-star or dash-dash at the first
824 : : * character will match a prior rule, not this one.
825 : : */
826 : : int nchars = yyleng;
827 : 13046 : char *slashstar = strstr(yytext, "/*");
828 : 13046 : char *dashdash = strstr(yytext, "--");
829 : 13046 :
830 : : if (slashstar && dashdash)
831 [ + + - + ]: 13046 : {
832 : : /* if both appear, take the first one */
833 : : if (slashstar > dashdash)
834 [ # # ]: 0 : slashstar = dashdash;
835 : 0 : }
836 : : else if (!slashstar)
837 [ + + ]: 13046 : slashstar = dashdash;
838 : 13006 : if (slashstar)
839 [ + + ]: 13046 : nchars = slashstar - yytext;
840 : 48 :
841 : : /*
842 : : * For SQL compatibility, '+' and '-' cannot be the
843 : : * last char of a multi-char operator unless the operator
844 : : * contains chars that are not in SQL operators.
845 : : * The idea is to lex '=-' as two operators, but not
846 : : * to forbid operator names like '?-' that could not be
847 : : * sequences of SQL operators.
848 : : */
849 : : if (nchars > 1 &&
850 [ + + ]: 13046 : (yytext[nchars - 1] == '+' ||
851 [ + + ]: 11994 : yytext[nchars - 1] == '-'))
852 [ + + ]: 11990 : {
853 : : int ic;
854 : :
855 : : for (ic = nchars - 2; ic >= 0; ic--)
856 [ + + ]: 385 : {
857 : : char c = yytext[ic];
858 : 326 : if (c == '~' || c == '!' || c == '@' ||
859 [ + - + + : 326 : c == '#' || c == '^' || c == '&' ||
+ - + + ]
860 [ + - + - : 270 : c == '|' || c == '`' || c == '?' ||
+ + ]
861 [ + - + + : 106 : c == '%')
+ - ]
862 : : break;
863 : : }
864 : : if (ic < 0)
865 [ + + ]: 291 : {
866 : : /*
867 : : * didn't find a qualifying character, so remove
868 : : * all trailing [+-]
869 : : */
870 : : do {
871 : : nchars--;
872 : 59 : } while (nchars > 1 &&
873 [ + + ]: 59 : (yytext[nchars - 1] == '+' ||
874 [ - + ]: 23 : yytext[nchars - 1] == '-'));
875 [ - + ]: 23 : }
876 : : }
877 : :
878 : : if (nchars < yyleng)
879 [ + + ]: 13046 : {
880 : : /* Strip the unwanted chars from the token */
881 : : yyless(nchars);
882 : 107 : }
883 : : ECHO;
884 : 13046 : }
885 : :
886 : 13046 : {param} {
887 : 1390 : ECHO;
888 : 1390 : }
889 : : {param_junk} {
890 : 1390 : ECHO;
891 : 8 : }
892 : :
893 : 8 : {decinteger} {
894 : 145110 : ECHO;
895 : 145110 : }
896 : : {hexinteger} {
897 : 145110 : ECHO;
898 : 83 : }
899 : : {octinteger} {
900 : 83 : ECHO;
901 : 40 : }
902 : : {bininteger} {
903 : 40 : ECHO;
904 : 40 : }
905 : : {hexfail} {
906 : 40 : ECHO;
907 : 4 : }
908 : : {octfail} {
909 : 4 : ECHO;
910 : 4 : }
911 : : {binfail} {
912 : 4 : ECHO;
913 : 4 : }
914 : : {numeric} {
915 : 4 : ECHO;
916 : 5487 : }
917 : : {numericfail} {
918 : 5487 : /* throw back the .., and treat as integer */
919 : 0 : yyless(yyleng - 2);
920 : 0 : ECHO;
921 : 0 : }
922 : : {real} {
923 : 0 : ECHO;
924 : 506 : }
925 : : {realfail} {
926 : 506 : ECHO;
927 : 4 : }
928 : : {integer_junk} {
929 : 4 : ECHO;
930 : 44 : }
931 : : {numeric_junk} {
932 : 44 : ECHO;
933 : 32 : }
934 : : {real_junk} {
935 : 32 : ECHO;
936 : 0 : }
937 : :
938 : 0 :
939 : 1908518 : {identifier} {
940 : : psqlscan_track_identifier(cur_state, yytext);
941 : 1908518 : ECHO;
942 : 1908518 : }
943 : :
944 : 1908518 : {other} {
945 : 8 : ECHO;
946 : 8 : }
947 : :
948 : 8 : <<EOF>> {
949 : 533820 : if (cur_state->buffer_stack == NULL)
950 [ + + ]: 533820 : {
951 : : cur_state->start_state = YY_START;
952 : 532828 : return LEXRES_EOL; /* end of input reached */
953 : 532828 : }
954 : :
955 : : /*
956 : : * We were expanding a variable, so pop the inclusion
957 : : * stack and keep lexing
958 : : */
959 : : psqlscan_pop_buffer_stack(cur_state);
960 : 992 : psqlscan_select_top_buffer(cur_state);
961 : 992 : }
962 : :
963 : 992 : %%
964 : 0 :
965 : : /* LCOV_EXCL_STOP */
966 : :
967 : : /*
968 : : * Record the first few keywords/identifiers of a statement or CREATE
969 : : * SCHEMA sub-statement in the idents[] array, of length idents_size.
970 : : * *idents_count is the number of entries filled so far.
971 : : *
972 : : * We record the interesting keywords using their first character, which
973 : : * works so long as those are all different. We could switch to an enum
974 : : * if that stops being true, but for now this is easy and compact.
975 : : */
976 : : static void
977 : : psqlscan_record_initial_keyword(const char *identifier,
978 : 1434083 : char *idents,
979 : : int idents_size,
980 : : int *idents_count)
981 : : {
982 : : if (*idents_count < idents_size)
983 [ + + ]: 1434083 : {
984 : : /*
985 : : * What we need to recognize is CREATE [OR REPLACE] FUNCTION/PROCEDURE
986 : : * and CREATE SCHEMA. Checking for SCHEMA is useless but not harmful
987 : : * in the CREATE SCHEMA sub-statement case. We record these keywords
988 : : * in lower case. We also need to recognize COPY ... FROM STDIN.
989 : : * (Note: the backend grammar doesn't distinguish STDIN from STDOUT,
990 : : * so we should not do so here either.) We record these keywords in
991 : : * upper case, to avoid conflicting with the first set.
992 : : */
993 : : if (pg_strcasecmp(identifier, "create") == 0 ||
994 [ + + + + ]: 2325994 : pg_strcasecmp(identifier, "function") == 0 ||
995 [ + + ]: 2274226 : pg_strcasecmp(identifier, "procedure") == 0 ||
996 [ + + ]: 2266797 : pg_strcasecmp(identifier, "or") == 0 ||
997 [ + + ]: 2264019 : pg_strcasecmp(identifier, "replace") == 0 ||
998 [ + + ]: 2260123 : pg_strcasecmp(identifier, "schema") == 0)
999 : 1129303 : idents[*idents_count] = pg_tolower((unsigned char) identifier[0]);
1000 : 58656 : else if (pg_strcasecmp(identifier, "copy") == 0 ||
1001 [ + + + + ]: 2251514 : pg_strcasecmp(identifier, "from") == 0 ||
1002 [ + + ]: 2184761 : pg_strcasecmp(identifier, "stdin") == 0 ||
1003 [ + + ]: 2119022 : pg_strcasecmp(identifier, "stdout") == 0)
1004 : 1059065 : idents[*idents_count] = pg_toupper((unsigned char) identifier[0]);
1005 : 68389 : /* For other keywords or identifiers, leave '\0' in the array entry */
1006 : : (*idents_count)++;
1007 : 1185366 : }
1008 : : }
1009 : 1434083 :
1010 : : /*
1011 : : * Does the current input match CREATE [OR REPLACE] {FUNCTION|PROCEDURE}?
1012 : : */
1013 : : static bool
1014 : : psqlscan_is_create_routine(const char *idents)
1015 : 1434103 : {
1016 : : return idents[0] == 'c' &&
1017 [ + + ]: 1741732 : (idents[1] == 'f' || idents[1] == 'p' ||
1018 [ + + + + ]: 307629 : (idents[1] == 'o' && idents[2] == 'r' &&
1019 [ + + + + ]: 276623 : (idents[3] == 'f' || idents[3] == 'p')));
1020 [ + + + + ]: 12674 : }
1021 : :
1022 : : /*
1023 : : * Does the current input match COPY ... FROM STDIN?
1024 : : */
1025 : : static bool
1026 : : psqlscan_is_copy_from_stdin(PsqlScanState state)
1027 : 257491 : {
1028 : : const char *idents = state->init_idents;
1029 : 257491 :
1030 : : /*
1031 : : * The first word must be COPY, but after that there could be up to four
1032 : : * identifiers (BINARY database.schema.table) before FROM. Since some of
1033 : : * the words we track are not reserved words, don't assume the intervening
1034 : : * array entries are '\0'. Life is simplified here by the fact that
1035 : : * psqlscan_track_identifier ignores everything within parens: we won't
1036 : : * see column lists nor the query in COPY (query).
1037 : : */
1038 : : if (idents[0] != 'C')
1039 [ + + ]: 257491 : return false;
1040 : 255596 : for (int i = 1; i < lengthof(state->init_idents) - 1; i++)
1041 [ + + ]: 7749 : {
1042 : : /* Scan to find FROM; if not seen within range, it's not valid COPY */
1043 : : if (idents[i] != 'F')
1044 [ + + ]: 6958 : continue;
1045 : 5854 : /* It's COPY FROM STDIN only if the next word is STDIN */
1046 : : return (idents[i + 1] == 'S');
1047 : 1104 : }
1048 : : return false;
1049 : 791 : }
1050 : :
1051 : : /*
1052 : : * This function is called each time the lexer recognizes an unquoted
1053 : : * identifier (which could also be a keyword, and indeed keywords are the
1054 : : * only case we really care about here). It presently has two tasks:
1055 : : *
1056 : : * 1. Track whether we are inside BEGIN .. END in a function definition,
1057 : : * so that semicolons contained therein don't terminate the whole statement.
1058 : : * Short of writing a full parser here, the following heuristic should work.
1059 : : *
1060 : : * We track whether the beginning of the statement matches CREATE [OR REPLACE]
1061 : : * {FUNCTION|PROCEDURE}. For CREATE SCHEMA, track BEGIN .. END blocks only
1062 : : * after recognizing an embedded CREATE [OR REPLACE] {FUNCTION|PROCEDURE}
1063 : : * subcommand. Once one of these conditions holds, count BEGIN and END
1064 : : * pairs. We also have to account for CASE ... END.
1065 : : *
1066 : : * 2. Record enough information for psqlscan_is_copy_from_stdin() to recognize
1067 : : * COPY FROM STDIN commands.
1068 : : */
1069 : : static void
1070 : : psqlscan_track_identifier(PsqlScanState state, const char *identifier)
1071 : 1908518 : {
1072 : : bool is_create_schema;
1073 : :
1074 : : /* None of this needs to happen when we're inside parentheses */
1075 : : if (state->paren_depth != 0)
1076 [ + + ]: 1908518 : return;
1077 : 479092 :
1078 : : /* Reset all my state at the start of each new statement */
1079 : : if (state->init_idents_count == 0)
1080 [ + + ]: 1429426 : {
1081 : : memset(state->init_idents, 0, sizeof(state->init_idents));
1082 : 258314 : state->sub_idents_count = 0;
1083 : 258314 : memset(state->sub_idents, 0, sizeof(state->sub_idents));
1084 : 258314 : }
1085 : :
1086 : : /* Record initial keywords if init_idents_count is small enough */
1087 : : psqlscan_record_initial_keyword(identifier,
1088 : 1429426 : state->init_idents,
1089 : 1429426 : lengthof(state->init_idents),
1090 : : &state->init_idents_count);
1091 : :
1092 : : /*
1093 : : * In CREATE SCHEMA, track identifiers from each top-level CREATE schema
1094 : : * element separately, so that BEGIN/END tracking is enabled only within
1095 : : * CREATE [OR REPLACE] {FUNCTION|PROCEDURE} clauses.
1096 : : */
1097 : : is_create_schema = (state->init_idents[0] == 'c' &&
1098 [ + + ]: 1733910 : state->init_idents[1] == 's');
1099 [ + + ]: 304484 : if (is_create_schema &&
1100 [ + + ]: 1429426 : state->begin_depth == 0)
1101 [ + + ]: 4677 : {
1102 : : /* Reset sub-clause state at each top-level CREATE keyword */
1103 : : if (pg_strcasecmp(identifier, "create") == 0)
1104 [ + + ]: 4657 : {
1105 : : state->sub_idents_count = 0;
1106 : 500 : memset(state->sub_idents, 0, sizeof(state->sub_idents));
1107 : 500 : }
1108 : : /* ... and record the first few keywords following that */
1109 : : psqlscan_record_initial_keyword(identifier,
1110 : 4657 : state->sub_idents,
1111 : 4657 : lengthof(state->sub_idents),
1112 : : &state->sub_idents_count);
1113 : : }
1114 : :
1115 : : /*
1116 : : * Track BEGIN/CASE/END only when within an appropriate (sub) statement.
1117 : : */
1118 : : if (psqlscan_is_create_routine(state->init_idents) ||
1119 [ + + + + ]: 1429426 : (is_create_schema &&
1120 [ + + ]: 4677 : psqlscan_is_create_routine(state->sub_idents)))
1121 : 4677 : {
1122 : : if (pg_strcasecmp(identifier, "begin") == 0)
1123 [ + + ]: 39119 : state->begin_depth++;
1124 : 127 : else if (pg_strcasecmp(identifier, "case") == 0)
1125 [ + + ]: 38992 : {
1126 : : /*
1127 : : * CASE also ends with END. We only need to track this if we are
1128 : : * already inside a BEGIN.
1129 : : */
1130 : : if (state->begin_depth >= 1)
1131 [ + - ]: 4 : state->begin_depth++;
1132 : 4 : }
1133 : : else if (pg_strcasecmp(identifier, "end") == 0)
1134 [ + + ]: 38988 : {
1135 : : if (state->begin_depth > 0)
1136 [ + - ]: 135 : state->begin_depth--;
1137 : 135 : }
1138 : : }
1139 : : }
1140 : :
1141 : : /*
1142 : : * Create a lexer working state struct.
1143 : : *
1144 : : * callbacks is a struct of function pointers that encapsulate some
1145 : : * behavior we need from the surrounding program. This struct must
1146 : : * remain valid for the lifespan of the PsqlScanState.
1147 : : */
1148 : : PsqlScanState
1149 : : psql_scan_create(const PsqlScanCallbacks *callbacks)
1150 : 13127 : {
1151 : : PsqlScanState state;
1152 : :
1153 : : state = pg_malloc0_object(PsqlScanStateData);
1154 : 13127 :
1155 : : state->callbacks = callbacks;
1156 : 13127 :
1157 : : yylex_init(&state->scanner);
1158 : 13127 :
1159 : : yyset_extra(state, state->scanner);
1160 : 13127 :
1161 : : psql_scan_reset(state);
1162 : 13127 :
1163 : : return state;
1164 : 13127 : }
1165 : :
1166 : : /*
1167 : : * Destroy a lexer working state struct, releasing all resources.
1168 : : */
1169 : : void
1170 : : psql_scan_destroy(PsqlScanState state)
1171 : 13071 : {
1172 : : psql_scan_finish(state);
1173 : 13071 :
1174 : : psql_scan_reset(state);
1175 : 13071 :
1176 : : yylex_destroy(state->scanner);
1177 : 13071 :
1178 : : free(state);
1179 : 13071 : }
1180 : 13071 :
1181 : : /*
1182 : : * Set the callback passthrough pointer for the lexer.
1183 : : *
1184 : : * This could have been integrated into psql_scan_create, but keeping it
1185 : : * separate allows the application to change the pointer later, which might
1186 : : * be useful.
1187 : : */
1188 : : void
1189 : : psql_scan_set_passthrough(PsqlScanState state, void *passthrough)
1190 : 10389 : {
1191 : : state->cb_passthrough = passthrough;
1192 : 10389 : }
1193 : 10389 :
1194 : : /*
1195 : : * Set up to perform lexing of the given input line.
1196 : : *
1197 : : * The text at *line, extending for line_len bytes, will be scanned by
1198 : : * subsequent calls to the psql_scan routines. psql_scan_finish should
1199 : : * be called when scanning is complete. Note that the lexer retains
1200 : : * a pointer to the storage at *line --- this string must not be altered
1201 : : * or freed until after psql_scan_finish is called.
1202 : : *
1203 : : * encoding is the libpq identifier for the character encoding in use,
1204 : : * and std_strings says whether standard_conforming_strings is on.
1205 : : */
1206 : : void
1207 : : psql_scan_setup(PsqlScanState state,
1208 : 533189 : const char *line, int line_len,
1209 : : int encoding, bool std_strings)
1210 : : {
1211 : : /* Mustn't be scanning already */
1212 : : Assert(state->scanbufhandle == NULL);
1213 : : Assert(state->buffer_stack == NULL);
1214 : :
1215 : : /* Do we need to hack the character set encoding? */
1216 : : state->encoding = encoding;
1217 : 533189 : state->safe_encoding = pg_valid_server_encoding_id(encoding);
1218 : 533189 :
1219 : : /* Save standard-strings flag as well */
1220 : : state->std_strings = std_strings;
1221 : 533189 :
1222 : : /* Set up flex input buffer with appropriate translation and padding */
1223 : : state->scanbufhandle = psqlscan_prepare_buffer(state, line, line_len,
1224 : 533189 : &state->scanbuf);
1225 : : state->scanline = line;
1226 : 533189 :
1227 : : /* Set lookaside data in case we have to map unsafe encoding */
1228 : : state->curline = state->scanbuf;
1229 : 533189 : state->refline = state->scanline;
1230 : 533189 :
1231 : : /* Initialize state for psql_scan_get_location() */
1232 : : state->cur_line_no = 0; /* yylex not called yet */
1233 : 533189 : state->cur_line_ptr = state->scanbuf;
1234 : 533189 : }
1235 : 533189 :
1236 : : /*
1237 : : * Do lexical analysis of SQL command text.
1238 : : *
1239 : : * The text previously passed to psql_scan_setup is scanned, and appended
1240 : : * (possibly with transformation) to query_buf.
1241 : : *
1242 : : * The return value indicates the condition that stopped scanning:
1243 : : *
1244 : : * PSCAN_SEMICOLON: found a command-ending semicolon. (The semicolon is
1245 : : * transferred to query_buf.) The command accumulated in query_buf should
1246 : : * be executed, then clear query_buf and call again to scan the remainder
1247 : : * of the line.
1248 : : *
1249 : : * PSCAN_BACKSLASH: found a backslash that starts a special command.
1250 : : * Any previous data on the line has been transferred to query_buf.
1251 : : * The caller will typically next apply a separate flex lexer to scan
1252 : : * the special command.
1253 : : *
1254 : : * PSCAN_INCOMPLETE: the end of the line was reached, but we have an
1255 : : * incomplete SQL command. *prompt is set to the appropriate prompt type.
1256 : : *
1257 : : * PSCAN_EOL: the end of the line was reached, and there is no lexical
1258 : : * reason to consider the command incomplete. The caller may or may not
1259 : : * choose to send it. *prompt is set to the appropriate prompt type if
1260 : : * the caller chooses to collect more input.
1261 : : *
1262 : : * In the PSCAN_INCOMPLETE and PSCAN_EOL cases, psql_scan_finish() should
1263 : : * be called next, then the cycle may be repeated with a fresh input line.
1264 : : *
1265 : : * In all cases, *prompt is set to an appropriate prompt type code for the
1266 : : * next line-input operation.
1267 : : */
1268 : : PsqlScanResult
1269 : : psql_scan(PsqlScanState state,
1270 : 815155 : PQExpBuffer query_buf,
1271 : : promptStatus_t *prompt)
1272 : : {
1273 : : PsqlScanResult result;
1274 : : int lexresult;
1275 : :
1276 : : /* Must be scanning already */
1277 : : Assert(state->scanbufhandle != NULL);
1278 : :
1279 : : /* Set current output target */
1280 : : state->output_buf = query_buf;
1281 : 815155 :
1282 : : /* Set input source */
1283 : : if (state->buffer_stack != NULL)
1284 [ + + ]: 815155 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
1285 : 60 : else
1286 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
1287 : 815095 :
1288 : : /* And lex. */
1289 : : lexresult = yylex(NULL, state->scanner);
1290 : 815155 :
1291 : : /* Notify psql_scan_get_location() that a yylex call has been made. */
1292 : : if (state->cur_line_no == 0)
1293 [ + + ]: 815155 : state->cur_line_no = 1;
1294 : 533187 :
1295 : : /*
1296 : : * Check termination state and return appropriate result info.
1297 : : */
1298 : : switch (lexresult)
1299 [ + + + - ]: 815155 : {
1300 : : case LEXRES_EOL: /* end of input */
1301 : 532828 : switch (state->start_state)
1302 [ + - + + : 532828 : {
- + + + -
- - ]
1303 : : case INITIAL:
1304 : 500783 : case xqs: /* we treat this like INITIAL */
1305 : : if (state->paren_depth > 0)
1306 [ + + ]: 500783 : {
1307 : : result = PSCAN_INCOMPLETE;
1308 : 45102 : *prompt = PROMPT_PAREN;
1309 : 45102 : }
1310 : : else if (state->begin_depth > 0)
1311 [ + + ]: 455681 : {
1312 : : result = PSCAN_INCOMPLETE;
1313 : 713 : *prompt = PROMPT_CONTINUE;
1314 : 713 : }
1315 : : else if (query_buf->len > 0)
1316 [ + + ]: 454968 : {
1317 : : result = PSCAN_EOL;
1318 : 98463 : *prompt = PROMPT_CONTINUE;
1319 : 98463 : }
1320 : : else
1321 : : {
1322 : : /* never bother to send an empty buffer */
1323 : : result = PSCAN_INCOMPLETE;
1324 : 356505 : *prompt = PROMPT_READY;
1325 : 356505 : }
1326 : : break;
1327 : 500783 : case xb:
1328 : 0 : result = PSCAN_INCOMPLETE;
1329 : 0 : *prompt = PROMPT_SINGLEQUOTE;
1330 : 0 : break;
1331 : 0 : case xc:
1332 : 525 : result = PSCAN_INCOMPLETE;
1333 : 525 : *prompt = PROMPT_COMMENT;
1334 : 525 : break;
1335 : 525 : case xd:
1336 : 23 : result = PSCAN_INCOMPLETE;
1337 : 23 : *prompt = PROMPT_DOUBLEQUOTE;
1338 : 23 : break;
1339 : 23 : case xh:
1340 : 0 : result = PSCAN_INCOMPLETE;
1341 : 0 : *prompt = PROMPT_SINGLEQUOTE;
1342 : 0 : break;
1343 : 0 : case xe:
1344 : 301 : result = PSCAN_INCOMPLETE;
1345 : 301 : *prompt = PROMPT_SINGLEQUOTE;
1346 : 301 : break;
1347 : 301 : case xq:
1348 : 7087 : result = PSCAN_INCOMPLETE;
1349 : 7087 : *prompt = PROMPT_SINGLEQUOTE;
1350 : 7087 : break;
1351 : 7087 : case xdolq:
1352 : 24109 : result = PSCAN_INCOMPLETE;
1353 : 24109 : *prompt = PROMPT_DOLLARQUOTE;
1354 : 24109 : break;
1355 : 24109 : case xui:
1356 : 0 : result = PSCAN_INCOMPLETE;
1357 : 0 : *prompt = PROMPT_DOUBLEQUOTE;
1358 : 0 : break;
1359 : 0 : case xus:
1360 : 0 : result = PSCAN_INCOMPLETE;
1361 : 0 : *prompt = PROMPT_SINGLEQUOTE;
1362 : 0 : break;
1363 : 0 : default:
1364 : 0 : /* can't get here */
1365 : : fprintf(stderr, "invalid YY_START\n");
1366 : 0 : exit(1);
1367 : 0 : }
1368 : : break;
1369 : 532828 : case LEXRES_SEMI: /* semicolon */
1370 : 249326 : result = PSCAN_SEMICOLON;
1371 : 249326 : *prompt = PROMPT_READY;
1372 : 249326 : break;
1373 : 249326 : case LEXRES_BACKSLASH: /* backslash */
1374 : 33001 : result = PSCAN_BACKSLASH;
1375 : 33001 : *prompt = PROMPT_READY;
1376 : 33001 : break;
1377 : 33001 : default:
1378 : 0 : /* can't get here */
1379 : : fprintf(stderr, "invalid yylex result\n");
1380 : 0 : exit(1);
1381 : 0 : }
1382 : :
1383 : : return result;
1384 : 815155 : }
1385 : :
1386 : : /*
1387 : : * Clean up after scanning a string. This flushes any unread input and
1388 : : * releases resources (but not the PsqlScanState itself). Note however
1389 : : * that this does not reset the lexer scan state; that can be done by
1390 : : * psql_scan_reset(), which is an orthogonal operation.
1391 : : *
1392 : : * It is legal to call this when not scanning anything (makes it easier
1393 : : * to deal with error recovery).
1394 : : */
1395 : : void
1396 : : psql_scan_finish(PsqlScanState state)
1397 : 543750 : {
1398 : : /* Drop any incomplete variable expansions. */
1399 : : while (state->buffer_stack != NULL)
1400 [ - + ]: 543750 : psqlscan_pop_buffer_stack(state);
1401 : 0 :
1402 : : /* Done with the outer scan buffer, too */
1403 : : if (state->scanbufhandle)
1404 [ + + ]: 543750 : yy_delete_buffer(state->scanbufhandle, state->scanner);
1405 : 533134 : state->scanbufhandle = NULL;
1406 : 543750 : if (state->scanbuf)
1407 [ + + ]: 543750 : free(state->scanbuf);
1408 : 533134 : state->scanbuf = NULL;
1409 : 543750 : }
1410 : 543750 :
1411 : : /*
1412 : : * Reset lexer scanning state to start conditions. This is appropriate
1413 : : * for executing \r psql commands (or any other time that we discard the
1414 : : * prior contents of query_buf). Do not call this between psql_scan()
1415 : : * calls that are scanning successive chunks of a single query string;
1416 : : * do call it when preparing to process a new query string.
1417 : : *
1418 : : * Note that this is unrelated to flushing unread input; that task is
1419 : : * done by psql_scan_finish().
1420 : : */
1421 : : void
1422 : : psql_scan_reset(PsqlScanState state)
1423 : 276671 : {
1424 : : state->start_state = INITIAL;
1425 : 276671 : state->paren_depth = 0;
1426 : 276671 : state->xcdepth = 0; /* not really necessary */
1427 : 276671 : if (state->dolqstart)
1428 [ - + ]: 276671 : free(state->dolqstart);
1429 : 0 : state->dolqstart = NULL;
1430 : 276671 : state->begin_depth = 0;
1431 : 276671 : state->copy_stdin_count = 0;
1432 : 276671 : state->init_idents_count = 0;
1433 : 276671 : }
1434 : 276671 :
1435 : : /*
1436 : : * Reselect this lexer (psqlscan.l) after using another one.
1437 : : *
1438 : : * Currently and for foreseeable uses, it's sufficient to reset to INITIAL
1439 : : * state, because we'd never switch to another lexer in a different state.
1440 : : * However, we don't want to reset e.g. paren_depth, so this can't be
1441 : : * the same as psql_scan_reset().
1442 : : *
1443 : : * Note: psql setjmp error recovery just calls psql_scan_reset(), so that
1444 : : * must be a superset of this.
1445 : : *
1446 : : * Note: it seems likely that other lexers could just assign INITIAL for
1447 : : * themselves, since that probably has the value zero in every flex-generated
1448 : : * lexer. But let's not assume that.
1449 : : */
1450 : : void
1451 : : psql_scan_reselect_sql_lexer(PsqlScanState state)
1452 : 156470 : {
1453 : : state->start_state = INITIAL;
1454 : 156470 : }
1455 : 156470 :
1456 : : /*
1457 : : * Return the number of COPY ... FROM STDIN commands in the input string.
1458 : : *
1459 : : * This should be called only after we've finished parsing a complete
1460 : : * string and are ready to send it to the backend.
1461 : : */
1462 : : int
1463 : : psql_scan_count_copy_from_stdin(PsqlScanState state)
1464 : 256003 : {
1465 : : if (state->init_idents_count > 0)
1466 [ + + ]: 256003 : {
1467 : : /* Count any COPY FROM STDIN following the last semicolon */
1468 : : if (psqlscan_is_copy_from_stdin(state))
1469 [ + + ]: 7653 : state->copy_stdin_count++;
1470 : 1 : /* ... but do so only once */
1471 : : state->init_idents_count = 0;
1472 : 7653 : }
1473 : : return state->copy_stdin_count;
1474 : 256003 : }
1475 : :
1476 : : /*
1477 : : * Return true if lexer is currently in an "inside quotes" state.
1478 : : *
1479 : : * This is pretty grotty but is needed to preserve the old behavior
1480 : : * that mainloop.c drops blank lines not inside quotes without even
1481 : : * echoing them.
1482 : : */
1483 : : bool
1484 : : psql_scan_in_quote(PsqlScanState state)
1485 : 101557 : {
1486 : : return state->start_state != INITIAL &&
1487 [ + + ]: 102182 : state->start_state != xqs;
1488 [ + + ]: 625 : }
1489 : :
1490 : : /*
1491 : : * Return the current scanning location (end+1 of last scanned token),
1492 : : * as a line number counted from 1 and an offset from string start.
1493 : : *
1494 : : * This considers only the outermost input string, and therefore is of
1495 : : * limited use for programs that use psqlscan_push_new_buffer().
1496 : : *
1497 : : * It would be a bit easier probably to use "%option yylineno" to count
1498 : : * lines, but the flex manual says that has a performance cost, and only
1499 : : * a minority of programs using psqlscan have need for this functionality.
1500 : : * So we implement it ourselves without adding overhead to the lexer itself.
1501 : : */
1502 : : void
1503 : : psql_scan_get_location(PsqlScanState state,
1504 : 1737 : int *lineno, int *offset)
1505 : : {
1506 : : const char *line_end;
1507 : :
1508 : : /*
1509 : : * We rely on flex's having stored a NUL after the current token in
1510 : : * scanbuf. Therefore we must specially handle the state before yylex()
1511 : : * has been called, when obviously that won't have happened yet.
1512 : : */
1513 : : if (state->cur_line_no == 0)
1514 [ - + ]: 1737 : {
1515 : : *lineno = 1;
1516 : 0 : *offset = 0;
1517 : 0 : return;
1518 : 0 : }
1519 : :
1520 : : /*
1521 : : * Advance cur_line_no/cur_line_ptr past whatever has been lexed so far.
1522 : : * Doing this prevents repeated calls from being O(N^2) for long inputs.
1523 : : */
1524 : : while ((line_end = strchr(state->cur_line_ptr, '\n')) != NULL)
1525 [ + + ]: 2210 : {
1526 : : state->cur_line_no++;
1527 : 473 : state->cur_line_ptr = line_end + 1;
1528 : 473 : }
1529 : : state->cur_line_ptr += strlen(state->cur_line_ptr);
1530 : 1737 :
1531 : : /* Report current location. */
1532 : : *lineno = state->cur_line_no;
1533 : 1737 : *offset = state->cur_line_ptr - state->scanbuf;
1534 : 1737 : }
1535 : :
1536 : : /*
1537 : : * Push the given string onto the stack of stuff to scan.
1538 : : *
1539 : : * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer.
1540 : : */
1541 : : void
1542 : : psqlscan_push_new_buffer(PsqlScanState state, const char *newstr,
1543 : 992 : const char *varname)
1544 : : {
1545 : : StackElem *stackelem;
1546 : :
1547 : : stackelem = pg_malloc_object(StackElem);
1548 : 992 :
1549 : : /*
1550 : : * In current usage, the passed varname points at the current flex input
1551 : : * buffer; we must copy it before calling psqlscan_prepare_buffer()
1552 : : * because that will change the buffer state.
1553 : : */
1554 : : stackelem->varname = varname ? pg_strdup(varname) : NULL;
1555 [ + - ]: 992 :
1556 : : stackelem->buf = psqlscan_prepare_buffer(state, newstr, strlen(newstr),
1557 : 992 : &stackelem->bufstring);
1558 : : state->curline = stackelem->bufstring;
1559 : 992 : if (state->safe_encoding)
1560 [ + - ]: 992 : {
1561 : : stackelem->origstring = NULL;
1562 : 992 : state->refline = stackelem->bufstring;
1563 : 992 : }
1564 : : else
1565 : : {
1566 : : stackelem->origstring = pg_strdup(newstr);
1567 : 0 : state->refline = stackelem->origstring;
1568 : 0 : }
1569 : : stackelem->next = state->buffer_stack;
1570 : 992 : state->buffer_stack = stackelem;
1571 : 992 : }
1572 : 992 :
1573 : : /*
1574 : : * Pop the topmost buffer stack item (there must be one!)
1575 : : *
1576 : : * NB: after this, the flex input state is unspecified; caller must
1577 : : * switch to an appropriate buffer to continue lexing.
1578 : : * See psqlscan_select_top_buffer().
1579 : : */
1580 : : void
1581 : : psqlscan_pop_buffer_stack(PsqlScanState state)
1582 : 992 : {
1583 : : StackElem *stackelem = state->buffer_stack;
1584 : 992 :
1585 : : state->buffer_stack = stackelem->next;
1586 : 992 : yy_delete_buffer(stackelem->buf, state->scanner);
1587 : 992 : free(stackelem->bufstring);
1588 : 992 : if (stackelem->origstring)
1589 [ - + ]: 992 : free(stackelem->origstring);
1590 : 0 : if (stackelem->varname)
1591 [ + - ]: 992 : free(stackelem->varname);
1592 : 992 : free(stackelem);
1593 : 992 : }
1594 : 992 :
1595 : : /*
1596 : : * Select the topmost surviving buffer as the active input.
1597 : : */
1598 : : void
1599 : : psqlscan_select_top_buffer(PsqlScanState state)
1600 : 992 : {
1601 : : StackElem *stackelem = state->buffer_stack;
1602 : 992 :
1603 : : if (stackelem != NULL)
1604 [ - + ]: 992 : {
1605 : : yy_switch_to_buffer(stackelem->buf, state->scanner);
1606 : 0 : state->curline = stackelem->bufstring;
1607 : 0 : state->refline = stackelem->origstring ? stackelem->origstring : stackelem->bufstring;
1608 [ # # ]: 0 : }
1609 : : else
1610 : : {
1611 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
1612 : 992 : state->curline = state->scanbuf;
1613 : 992 : state->refline = state->scanline;
1614 : 992 : }
1615 : : }
1616 : 992 :
1617 : : /*
1618 : : * Check if specified variable name is the source for any string
1619 : : * currently being scanned
1620 : : */
1621 : : bool
1622 : : psqlscan_var_is_current_source(PsqlScanState state, const char *varname)
1623 : 992 : {
1624 : : StackElem *stackelem;
1625 : :
1626 : : for (stackelem = state->buffer_stack;
1627 : 992 : stackelem != NULL;
1628 [ - + ]: 992 : stackelem = stackelem->next)
1629 : 0 : {
1630 : : if (stackelem->varname && strcmp(stackelem->varname, varname) == 0)
1631 [ # # # # ]: 0 : return true;
1632 : 0 : }
1633 : : return false;
1634 : 992 : }
1635 : :
1636 : : /*
1637 : : * Set up a flex input buffer to scan the given data. We always make a
1638 : : * copy of the data. If working in an unsafe encoding, the copy has
1639 : : * multibyte sequences replaced by FFs to avoid fooling the lexer rules.
1640 : : *
1641 : : * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer.
1642 : : */
1643 : : YY_BUFFER_STATE
1644 : : psqlscan_prepare_buffer(PsqlScanState state, const char *txt, int len,
1645 : 534181 : char **txtcopy)
1646 : : {
1647 : : char *newtxt;
1648 : :
1649 : : /* Flex wants two \0 characters after the actual data */
1650 : : newtxt = pg_malloc_array(char, (len + 2));
1651 : 534181 : *txtcopy = newtxt;
1652 : 534181 : newtxt[len] = newtxt[len + 1] = YY_END_OF_BUFFER_CHAR;
1653 : 534181 :
1654 : : if (state->safe_encoding)
1655 [ + + ]: 534181 : memcpy(newtxt, txt, len);
1656 : 534041 : else
1657 : : {
1658 : : /* Gotta do it the hard way */
1659 : : int i = 0;
1660 : 140 :
1661 : : while (i < len)
1662 [ + + ]: 808 : {
1663 : : int thislen = PQmblen(txt + i, state->encoding);
1664 : 668 :
1665 : : /* first byte should always be okay... */
1666 : : newtxt[i] = txt[i];
1667 : 668 : i++;
1668 : 668 : while (--thislen > 0 && i < len)
1669 [ + + + - ]: 808 : newtxt[i++] = (char) 0xFF;
1670 : 140 : }
1671 : : }
1672 : :
1673 : : return yy_scan_buffer(newtxt, len + 2, state->scanner);
1674 : 534181 : }
1675 : :
1676 : : /*
1677 : : * psqlscan_emit() --- body for ECHO macro
1678 : : *
1679 : : * NB: this must be used for ALL and ONLY the text copied from the flex
1680 : : * input data. If you pass it something that is not part of the yytext
1681 : : * string, you are making a mistake. Internally generated text can be
1682 : : * appended directly to state->output_buf.
1683 : : */
1684 : : void
1685 : : psqlscan_emit(PsqlScanState state, const char *txt, int len)
1686 : 6776701 : {
1687 : : PQExpBuffer output_buf = state->output_buf;
1688 : 6776701 :
1689 : : if (state->safe_encoding)
1690 [ + + ]: 6776701 : appendBinaryPQExpBuffer(output_buf, txt, len);
1691 : 6776225 : else
1692 : : {
1693 : : /* Gotta do it the hard way */
1694 : : const char *reference = state->refline;
1695 : 476 : int i;
1696 : :
1697 : : reference += (txt - state->curline);
1698 : 476 :
1699 : : for (i = 0; i < len; i++)
1700 [ + + ]: 1277 : {
1701 : : char ch = txt[i];
1702 : 801 :
1703 : : if (ch == (char) 0xFF)
1704 [ + + ]: 801 : ch = reference[i];
1705 : 140 : appendPQExpBufferChar(output_buf, ch);
1706 : 801 : }
1707 : : }
1708 : : }
1709 : 6776701 :
1710 : : /*
1711 : : * psqlscan_extract_substring --- fetch value of (part of) the current token
1712 : : *
1713 : : * This is like psqlscan_emit(), except that the data is returned as a
1714 : : * malloc'd string rather than being pushed directly to state->output_buf.
1715 : : */
1716 : : char *
1717 : : psqlscan_extract_substring(PsqlScanState state, const char *txt, int len)
1718 : 3504 : {
1719 : : char *result = pg_malloc_array(char, (len + 1));
1720 : 3504 :
1721 : : if (state->safe_encoding)
1722 [ + - ]: 3504 : memcpy(result, txt, len);
1723 : 3504 : else
1724 : : {
1725 : : /* Gotta do it the hard way */
1726 : : const char *reference = state->refline;
1727 : 0 : int i;
1728 : :
1729 : : reference += (txt - state->curline);
1730 : 0 :
1731 : : for (i = 0; i < len; i++)
1732 [ # # ]: 0 : {
1733 : : char ch = txt[i];
1734 : 0 :
1735 : : if (ch == (char) 0xFF)
1736 [ # # ]: 0 : ch = reference[i];
1737 : 0 : result[i] = ch;
1738 : 0 : }
1739 : : }
1740 : : result[len] = '\0';
1741 : 3504 : return result;
1742 : 3504 : }
1743 : :
1744 : : /*
1745 : : * psqlscan_escape_variable --- process :'VARIABLE' or :"VARIABLE"
1746 : : *
1747 : : * If the variable name is found, escape its value using the appropriate
1748 : : * quoting method and emit the value to output_buf. (Since the result is
1749 : : * surely quoted, there is never any reason to rescan it.) If we don't
1750 : : * find the variable or escaping fails, emit the token as-is.
1751 : : */
1752 : : void
1753 : : psqlscan_escape_variable(PsqlScanState state, const char *txt, int len,
1754 : 725 : PsqlScanQuoteType quote)
1755 : : {
1756 : : char *varname;
1757 : : char *value;
1758 : :
1759 : : /* Variable lookup. */
1760 : : varname = psqlscan_extract_substring(state, txt + 2, len - 3);
1761 : 725 : if (state->callbacks->get_variable)
1762 [ + - ]: 725 : value = state->callbacks->get_variable(varname, quote,
1763 : 725 : state->cb_passthrough);
1764 : : else
1765 : : value = NULL;
1766 : 0 : free(varname);
1767 : 725 :
1768 : : if (value)
1769 [ + + ]: 725 : {
1770 : : /* Emit the suitably-escaped value */
1771 : : appendPQExpBufferStr(state->output_buf, value);
1772 : 688 : free(value);
1773 : 688 : }
1774 : : else
1775 : : {
1776 : : /* Emit original token as-is */
1777 : : psqlscan_emit(state, txt, len);
1778 : 37 : }
1779 : : }
1780 : 725 :
1781 : : void
1782 : : psqlscan_test_variable(PsqlScanState state, const char *txt, int len)
1783 : 21 : {
1784 : : char *varname;
1785 : : char *value;
1786 : :
1787 : : varname = psqlscan_extract_substring(state, txt + 3, len - 4);
1788 : 21 : if (state->callbacks->get_variable)
1789 [ + - ]: 21 : value = state->callbacks->get_variable(varname, PQUOTE_PLAIN,
1790 : 21 : state->cb_passthrough);
1791 : : else
1792 : : value = NULL;
1793 : 0 : free(varname);
1794 : 21 :
1795 : : if (value != NULL)
1796 [ + + ]: 21 : {
1797 : : appendPQExpBufferStr(state->output_buf, "TRUE");
1798 : 9 : free(value);
1799 : 9 : }
1800 : : else
1801 : : {
1802 : : appendPQExpBufferStr(state->output_buf, "FALSE");
1803 : 12 : }
1804 : : }
1805 : 21 : /* END: function "psqlscan_test_variable" */
|