Age Owner Branch data TLA Line data Source code
1 : : %top{
2 : : /*-------------------------------------------------------------------------
3 : : *
4 : : * psqlscanslash.l
5 : : * lexical scanner for psql backslash commands
6 : : *
7 : : * XXX Avoid creating backtracking cases --- see the backend lexer for info.
8 : : *
9 : : * See fe_utils/psqlscan_int.h for additional commentary.
10 : : *
11 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
12 : : * Portions Copyright (c) 1994, Regents of the University of California
13 : : *
14 : : * IDENTIFICATION
15 : : * src/bin/psql/psqlscanslash.l
16 : : *
17 : : *-------------------------------------------------------------------------
18 : : */
19 : : #include "postgres_fe.h"
20 : :
21 : : #include <ctype.h>
22 : :
23 : : #include "common.h"
24 : : #include "psqlscanslash.h"
25 : :
26 : : #include "common/logging.h"
27 : : #include "fe_utils/conditional.h"
28 : :
29 : : #include "libpq-fe.h"
30 : : }
31 : :
32 : : %{
33 : : #include "fe_utils/psqlscan_int.h"
34 : :
35 : : /*
36 : : * We must have a typedef YYSTYPE for yylex's first argument, but this lexer
37 : : * doesn't presently make use of that argument, so just declare it as int.
38 : : */
39 : : typedef int YYSTYPE;
40 : :
41 : : /*
42 : : * Set the type of yyextra; we use it as a pointer back to the containing
43 : : * PsqlScanState.
44 : : */
45 : : #define YY_EXTRA_TYPE PsqlScanState
46 : :
47 : : /*
48 : : * These variables do not need to be saved across calls. Yeah, it's a bit
49 : : * of a hack, but putting them into PsqlScanStateData would be klugy too.
50 : : */
51 : : static enum slash_option_type option_type;
52 : : static char *option_quote;
53 : : static int unquoted_option_chars;
54 : : static int backtick_start_offset;
55 : :
56 : :
57 : : /* Return values from yylex() */
58 : : #define LEXRES_EOL 0 /* end of input */
59 : : #define LEXRES_OK 1 /* OK completion of backslash argument */
60 : :
61 : :
62 : : static void evaluate_backtick(PsqlScanState state);
63 : :
64 : : #define ECHO psqlscan_emit(cur_state, yytext, yyleng)
65 : :
66 : : /*
67 : : * Work around a bug in flex 2.5.35: it emits a couple of functions that
68 : : * it forgets to emit declarations for. Since we use -Wmissing-prototypes,
69 : : * this would cause warnings. Providing our own declarations should be
70 : : * harmless even when the bug gets fixed.
71 : : */
72 : : extern int slash_yyget_column(yyscan_t yyscanner);
73 : : extern void slash_yyset_column(int column_no, yyscan_t yyscanner);
74 : :
75 : : /* LCOV_EXCL_START */
76 : :
77 : : %}
78 : :
79 : : /* Except for the prefix, these options should match psqlscan.l */
80 : : %option reentrant
81 : : %option bison-bridge
82 : : %option 8bit
83 : : %option never-interactive
84 : : %option nodefault
85 : : %option noinput
86 : : %option nounput
87 : : %option noyywrap
88 : : %option warn
89 : : %option prefix="slash_yy"
90 : :
91 : : /*
92 : : * OK, here is a short description of lex/flex rules behavior.
93 : : * The longest pattern which matches an input string is always chosen.
94 : : * For equal-length patterns, the first occurring in the rules list is chosen.
95 : : * INITIAL is the starting state, to which all non-conditional rules apply.
96 : : * Exclusive states change parsing rules while the state is active. When in
97 : : * an exclusive state, only those rules defined for that state apply.
98 : : */
99 : :
100 : : /* Exclusive states for lexing backslash commands */
101 : : %x xslashcmd
102 : : %x xslashargstart
103 : : %x xslasharg
104 : : %x xslashquote
105 : : %x xslashbackquote
106 : : %x xslashdquote
107 : : %x xslashwholeline
108 : : %x xslashend
109 : :
110 : : /*
111 : : * Assorted character class definitions that should match psqlscan.l.
112 : : */
113 : : space [ \t\n\r\f\v]
114 : : quote '
115 : : xeoctesc [\\][0-7]{1,3}
116 : : xehexesc [\\]x[0-9A-Fa-f]{1,2}
117 : : xqdouble {quote}{quote}
118 : : dquote \"
119 : : variable_char [A-Za-z\200-\377_0-9]
120 : :
121 : : other .
122 : :
123 : : %%
124 : :
125 : : %{
126 : : /* Declare some local variables inside yylex(), for convenience */
127 : : PsqlScanState cur_state = yyextra;
128 : : PQExpBuffer output_buf = cur_state->output_buf;
129 : :
130 : : /*
131 : : * Force flex into the state indicated by start_state. This has a
132 : : * couple of purposes: it lets some of the functions below set a new
133 : : * starting state without ugly direct access to flex variables, and it
134 : : * allows us to transition from one flex lexer to another so that we
135 : : * can lex different parts of the source string using separate lexers.
136 : : */
137 : : BEGIN(cur_state->start_state);
138 : : %}
139 : :
140 : : /*
141 : : * We don't really expect to be invoked in the INITIAL state in this
142 : : * lexer; but if we are, just spit data to the output_buf until EOF.
143 : : */
144 : :
145 : : {other}|\n { ECHO; }
146 : :
147 : : /*
148 : : * Exclusive lexer states to handle backslash command lexing
149 : : */
150 : :
151 : : <xslashcmd>{
152 : : /* command name ends at whitespace or backslash; eat all else */
153 : :
154 : : {space}|"\\" {
155 : : yyless(0);
156 : : cur_state->start_state = YY_START;
157 : : return LEXRES_OK;
158 : : }
159 : :
160 : : {other} { ECHO; }
161 : :
162 : : }
163 : :
164 : : <xslashargstart>{
165 : : /*
166 : : * Discard any whitespace before argument, then go to xslasharg state.
167 : : * An exception is that "|" is only special at start of argument, so we
168 : : * check for it here.
169 : : */
170 : :
171 : : {space}+ { }
172 : :
173 : : "|" {
174 : : if (option_type == OT_FILEPIPE)
175 : : {
176 : : /* treat like whole-string case */
177 : : ECHO;
178 : : BEGIN(xslashwholeline);
179 : : }
180 : : else
181 : : {
182 : : /* vertical bar is not special otherwise */
183 : : yyless(0);
184 : : BEGIN(xslasharg);
185 : : }
186 : : }
187 : :
188 : : {other} {
189 : : yyless(0);
190 : : BEGIN(xslasharg);
191 : : }
192 : :
193 : : }
194 : :
195 : : <xslasharg>{
196 : : /*
197 : : * Default processing of text in a slash command's argument.
198 : : *
199 : : * Note: unquoted_option_chars counts the number of characters at the
200 : : * end of the argument that were not subject to any form of quoting.
201 : : * psql_scan_slash_option needs this to strip trailing semicolons safely.
202 : : */
203 : :
204 : : {space}|"\\" {
205 : : /*
206 : : * Unquoted space is end of arg; do not eat. Likewise
207 : : * backslash is end of command or next command, do not eat
208 : : *
209 : : * XXX this means we can't conveniently accept options
210 : : * that include unquoted backslashes; therefore, option
211 : : * processing that encourages use of backslashes is rather
212 : : * broken.
213 : : */
214 : : yyless(0);
215 : : cur_state->start_state = YY_START;
216 : : return LEXRES_OK;
217 : : }
218 : :
219 : : {quote} {
220 : : *option_quote = '\'';
221 : : unquoted_option_chars = 0;
222 : : BEGIN(xslashquote);
223 : : }
224 : :
225 : : "`" {
226 : : backtick_start_offset = output_buf->len;
227 : : *option_quote = '`';
228 : : unquoted_option_chars = 0;
229 : : BEGIN(xslashbackquote);
230 : : }
231 : :
232 : : {dquote} {
233 : : ECHO;
234 : : *option_quote = '"';
235 : : unquoted_option_chars = 0;
236 : : BEGIN(xslashdquote);
237 : : }
238 : :
239 : : :{variable_char}+ {
240 : : /* Possible psql variable substitution */
241 : : if (cur_state->callbacks->get_variable == NULL)
242 : : ECHO;
243 : : else
244 : : {
245 : : char *varname;
246 : : char *value;
247 : :
248 : : varname = psqlscan_extract_substring(cur_state,
249 : : yytext + 1,
250 : : yyleng - 1);
251 : : value = cur_state->callbacks->get_variable(varname,
252 : : PQUOTE_PLAIN,
253 : : cur_state->cb_passthrough);
254 : : free(varname);
255 : :
256 : : /*
257 : : * The variable value is just emitted without any
258 : : * further examination. This is consistent with the
259 : : * pre-8.0 code behavior, if not with the way that
260 : : * variables are handled outside backslash commands.
261 : : * Note that we needn't guard against recursion here.
262 : : */
263 : : if (value)
264 : : {
265 : : appendPQExpBufferStr(output_buf, value);
266 : : free(value);
267 : : }
268 : : else
269 : : ECHO;
270 : :
271 : : *option_quote = ':';
272 : : }
273 : : unquoted_option_chars = 0;
274 : : }
275 : :
276 : : :'{variable_char}+' {
277 : : psqlscan_escape_variable(cur_state, yytext, yyleng,
278 : : PQUOTE_SQL_LITERAL);
279 : : *option_quote = ':';
280 : : unquoted_option_chars = 0;
281 : : }
282 : :
283 : :
284 : : :\"{variable_char}+\" {
285 : : psqlscan_escape_variable(cur_state, yytext, yyleng,
286 : : PQUOTE_SQL_IDENT);
287 : : *option_quote = ':';
288 : : unquoted_option_chars = 0;
289 : : }
290 : :
291 : : :\{\?{variable_char}+\} {
292 : : psqlscan_test_variable(cur_state, yytext, yyleng);
293 : : }
294 : :
295 : : :'{variable_char}* {
296 : : /* Throw back everything but the colon */
297 : : yyless(1);
298 : : unquoted_option_chars++;
299 : : ECHO;
300 : : }
301 : :
302 : : :\"{variable_char}* {
303 : : /* Throw back everything but the colon */
304 : : yyless(1);
305 : : unquoted_option_chars++;
306 : : ECHO;
307 : : }
308 : :
309 : : :\{\?{variable_char}* {
310 : : /* Throw back everything but the colon */
311 : : yyless(1);
312 : : unquoted_option_chars++;
313 : : ECHO;
314 : : }
315 : :
316 : : :\{ {
317 : : /* Throw back everything but the colon */
318 : : yyless(1);
319 : : unquoted_option_chars++;
320 : : ECHO;
321 : : }
322 : :
323 : : {other} {
324 : : unquoted_option_chars++;
325 : : ECHO;
326 : : }
327 : :
328 : : }
329 : :
330 : : <xslashquote>{
331 : : /*
332 : : * single-quoted text: copy literally except for '' and backslash
333 : : * sequences
334 : : */
335 : :
336 : : {quote} { BEGIN(xslasharg); }
337 : :
338 : : {xqdouble} { appendPQExpBufferChar(output_buf, '\''); }
339 : :
340 : : "\\n" { appendPQExpBufferChar(output_buf, '\n'); }
341 : : "\\t" { appendPQExpBufferChar(output_buf, '\t'); }
342 : : "\\b" { appendPQExpBufferChar(output_buf, '\b'); }
343 : : "\\r" { appendPQExpBufferChar(output_buf, '\r'); }
344 : : "\\f" { appendPQExpBufferChar(output_buf, '\f'); }
345 : :
346 : : {xeoctesc} {
347 : : /* octal case */
348 : : appendPQExpBufferChar(output_buf,
349 : : (char) strtol(yytext + 1, NULL, 8));
350 : : }
351 : :
352 : : {xehexesc} {
353 : : /* hex case */
354 : : appendPQExpBufferChar(output_buf,
355 : : (char) strtol(yytext + 2, NULL, 16));
356 : : }
357 : :
358 : : "\\". { psqlscan_emit(cur_state, yytext + 1, 1); }
359 : :
360 : : {other}|\n { ECHO; }
361 : :
362 : : }
363 : :
364 : : <xslashbackquote>{
365 : : /*
366 : : * backticked text: copy everything until next backquote (expanding
367 : : * variable references, but doing nought else), then evaluate.
368 : : */
369 : :
370 : : "`" {
371 : : /* In an inactive \if branch, don't evaluate the command */
372 : : if (cur_state->cb_passthrough == NULL ||
373 : : conditional_active((ConditionalStack) cur_state->cb_passthrough))
374 : : evaluate_backtick(cur_state);
375 : : BEGIN(xslasharg);
376 : : }
377 : :
378 : : :{variable_char}+ {
379 : : /* Possible psql variable substitution */
380 : : if (cur_state->callbacks->get_variable == NULL)
381 : : ECHO;
382 : : else
383 : : {
384 : : char *varname;
385 : : char *value;
386 : :
387 : : varname = psqlscan_extract_substring(cur_state,
388 : : yytext + 1,
389 : : yyleng - 1);
390 : : value = cur_state->callbacks->get_variable(varname,
391 : : PQUOTE_PLAIN,
392 : : cur_state->cb_passthrough);
393 : : free(varname);
394 : :
395 : : if (value)
396 : : {
397 : : appendPQExpBufferStr(output_buf, value);
398 : : free(value);
399 : : }
400 : : else
401 : : ECHO;
402 : : }
403 : : }
404 : :
405 : : :'{variable_char}+' {
406 : : psqlscan_escape_variable(cur_state, yytext, yyleng,
407 : : PQUOTE_SHELL_ARG);
408 : : }
409 : :
410 : : :'{variable_char}* {
411 : : /* Throw back everything but the colon */
412 : : yyless(1);
413 : : ECHO;
414 : : }
415 : :
416 : : {other}|\n { ECHO; }
417 : :
418 : : }
419 : :
420 : : <xslashdquote>{
421 : : /* double-quoted text: copy verbatim, including the double quotes */
422 : :
423 : : {dquote} {
424 : : ECHO;
425 : : BEGIN(xslasharg);
426 : : }
427 : :
428 : : {other}|\n { ECHO; }
429 : :
430 : : }
431 : :
432 : : <xslashwholeline>{
433 : : /* copy everything until end of input line */
434 : : /* but suppress leading whitespace */
435 : :
436 : : {space}+ {
437 : : if (output_buf->len > 0)
438 : : ECHO;
439 : : }
440 : :
441 : : {other} { ECHO; }
442 : :
443 : : }
444 : :
445 : : <xslashend>{
446 : : /* at end of command, eat a double backslash, but not anything else */
447 : :
448 : : "\\\\" {
449 : : cur_state->start_state = YY_START;
450 : : return LEXRES_OK;
451 : : }
452 : :
453 : : {other}|\n {
454 : : yyless(0);
455 : : cur_state->start_state = YY_START;
456 : : return LEXRES_OK;
457 : : }
458 : :
459 : : }
460 : :
461 : : <<EOF>> {
462 : : if (cur_state->buffer_stack == NULL)
463 : : {
464 : : cur_state->start_state = YY_START;
465 : : return LEXRES_EOL; /* end of input reached */
466 : : }
467 : :
468 : : /*
469 : : * We were expanding a variable, so pop the inclusion
470 : : * stack and keep lexing
471 : : */
472 : : psqlscan_pop_buffer_stack(cur_state);
473 : : psqlscan_select_top_buffer(cur_state);
474 : : }
475 : :
476 : : %%
477 : :
478 : : /* LCOV_EXCL_STOP */
479 : :
480 : : /*
481 : : * Scan the command name of a psql backslash command. This should be called
482 : : * after psql_scan() returns PSCAN_BACKSLASH. It is assumed that the input
483 : : * has been consumed through the leading backslash.
484 : : *
485 : : * The return value is a malloc'd copy of the command name, as parsed off
486 : : * from the input.
487 : : */
488 : : char *
489 : : psql_scan_slash_command(PsqlScanState state)
2948 tgl@sss.pgh.pa.us 490 :CBC 6598 : {
491 : : PQExpBufferData mybuf;
492 : :
493 : : /* Must be scanning already */
494 : : Assert(state->scanbufhandle != NULL);
495 [ - + ]: 6598 :
496 : : /* Build a local buffer that we'll return the data of */
497 : : initPQExpBuffer(&mybuf);
498 : 6598 :
499 : : /* Set current output target */
500 : : state->output_buf = &mybuf;
501 : 6598 :
502 : : /* Set input source */
503 : : if (state->buffer_stack != NULL)
504 [ - + ]: 6598 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
2948 tgl@sss.pgh.pa.us 505 :UBC 0 : else
506 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
2948 tgl@sss.pgh.pa.us 507 :CBC 6598 :
508 : : /*
509 : : * Set lexer start state. Note that this is sufficient to switch
510 : : * state->scanner over to using the tables in this lexer file.
511 : : */
512 : : state->start_state = xslashcmd;
513 : 6598 :
514 : : /* And lex. */
515 : : yylex(NULL, state->scanner);
516 : 6598 :
517 : : /* There are no possible errors in this lex state... */
518 : :
519 : : /*
520 : : * In case the caller returns to using the regular SQL lexer, reselect the
521 : : * appropriate initial state.
522 : : */
523 : : psql_scan_reselect_sql_lexer(state);
524 : 6598 :
525 : : return mybuf.data;
526 : 6598 : }
527 : :
528 : : /*
529 : : * Parse off the next argument for a backslash command, and return it as a
530 : : * malloc'd string. If there are no more arguments, returns NULL.
531 : : *
532 : : * type tells what processing, if any, to perform on the option string;
533 : : * for example, if it's a SQL identifier, we want to downcase any unquoted
534 : : * letters.
535 : : *
536 : : * if quote is not NULL, *quote is set to 0 if no quoting was found, else
537 : : * the last quote symbol used in the argument.
538 : : *
539 : : * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
540 : : * be taken as part of the option string will be stripped.
541 : : *
542 : : * NOTE: the only possible syntax errors for backslash options are unmatched
543 : : * quotes, which are detected when we run out of input. Therefore, on a
544 : : * syntax error we just throw away the string and return NULL; there is no
545 : : * need to worry about flushing remaining input.
546 : : */
547 : : char *
548 : : psql_scan_slash_option(PsqlScanState state,
549 : 16974 : enum slash_option_type type,
550 : : char *quote,
551 : : bool semicolon)
552 : : {
553 : : PQExpBufferData mybuf;
554 : : int lexresult PG_USED_FOR_ASSERTS_ONLY;
555 : : int final_state;
556 : : char local_quote;
557 : :
558 : : /* Must be scanning already */
559 : : Assert(state->scanbufhandle != NULL);
560 [ - + ]: 16974 :
561 : : if (quote == NULL)
562 [ + + ]: 16974 : quote = &local_quote;
563 : 15421 : *quote = 0;
564 : 16974 :
565 : : /* Build a local buffer that we'll return the data of */
566 : : initPQExpBuffer(&mybuf);
567 : 16974 :
568 : : /* Set up static variables that will be used by yylex */
569 : : option_type = type;
570 : 16974 : option_quote = quote;
571 : 16974 : unquoted_option_chars = 0;
572 : 16974 :
573 : : /* Set current output target */
574 : : state->output_buf = &mybuf;
575 : 16974 :
576 : : /* Set input source */
577 : : if (state->buffer_stack != NULL)
578 [ - + ]: 16974 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
2948 tgl@sss.pgh.pa.us 579 :UBC 0 : else
580 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
2948 tgl@sss.pgh.pa.us 581 :CBC 16974 :
582 : : /* Set lexer start state */
583 : : if (type == OT_WHOLE_LINE)
584 [ + + ]: 16974 : state->start_state = xslashwholeline;
585 : 711 : else
586 : : state->start_state = xslashargstart;
587 : 16263 :
588 : : /* And lex. */
589 : : lexresult = yylex(NULL, state->scanner);
590 : 16974 :
591 : : /* Save final state for a moment... */
592 : : final_state = state->start_state;
593 : 16974 :
594 : : /*
595 : : * In case the caller returns to using the regular SQL lexer, reselect the
596 : : * appropriate initial state.
597 : : */
598 : : psql_scan_reselect_sql_lexer(state);
599 : 16974 :
600 : : /*
601 : : * Check the lex result: we should have gotten back either LEXRES_OK
602 : : * or LEXRES_EOL (the latter indicating end of string). If we were inside
603 : : * a quoted string, as indicated by final_state, EOL is an error.
604 : : */
605 : : Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
606 [ + + - + ]: 16974 :
607 : : switch (final_state)
608 [ + + - + : 16974 : {
- ]
609 : : case xslashargstart:
610 : 8563 : /* empty arg */
611 : : break;
612 : 8563 : case xslasharg:
95 tgl@sss.pgh.pa.us 613 :GNC 7693 : /* Strip any unquoted trailing semicolons if requested */
614 : : if (semicolon)
2948 tgl@sss.pgh.pa.us 615 [ + + ]:CBC 7693 : {
616 : : while (unquoted_option_chars-- > 0 &&
617 : 3302 : mybuf.len > 0 &&
618 [ + + + - ]: 3305 : mybuf.data[mybuf.len - 1] == ';')
619 [ + + ]: 2881 : {
620 : : mybuf.data[--mybuf.len] = '\0';
621 : 3 : }
622 : : }
623 : :
624 : : /*
625 : : * If SQL identifier processing was requested, then we strip out
626 : : * excess double quotes and optionally downcase unquoted letters.
627 : : */
628 : : if (type == OT_SQLID || type == OT_SQLIDHACK)
629 [ + + + + ]: 7693 : {
630 : : dequote_downcase_identifier(mybuf.data,
2919 631 : 113 : (type != OT_SQLIDHACK),
632 : : state->encoding);
633 : : /* update mybuf.len for possible shortening */
634 : : mybuf.len = strlen(mybuf.data);
2948 635 : 113 : }
636 : : break;
637 : 7693 : case xslashquote:
2948 tgl@sss.pgh.pa.us 638 :UBC 0 : case xslashbackquote:
639 : : case xslashdquote:
640 : : /* must have hit EOL inside quotes */
641 : : pg_log_error("unterminated quoted string");
642 : 0 : termPQExpBuffer(&mybuf);
643 : 0 : return NULL;
644 : 0 : case xslashwholeline:
95 tgl@sss.pgh.pa.us 645 :GNC 718 : /*
646 : : * In whole-line mode, we interpret semicolon = true as stripping
647 : : * trailing whitespace as well as semicolons; this gives the
648 : : * nearest equivalent to what semicolon = true does in normal
649 : : * mode. Note there's no concept of quoting in this mode.
650 : : */
651 : : if (semicolon)
652 [ + + ]: 718 : {
653 : : while (mybuf.len > 0 &&
654 [ + + ]: 79 : (mybuf.data[mybuf.len - 1] == ';' ||
655 [ + + ]: 78 : (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
656 [ + - ]: 75 : isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
657 [ - + ]: 75 : {
658 : : mybuf.data[--mybuf.len] = '\0';
659 : 3 : }
660 : : }
661 : : break;
2948 tgl@sss.pgh.pa.us 662 :CBC 718 : default:
2948 tgl@sss.pgh.pa.us 663 :UBC 0 : /* can't get here */
664 : : fprintf(stderr, "invalid YY_START\n");
665 : 0 : exit(1);
666 : 0 : }
667 : :
668 : : /*
669 : : * An unquoted empty argument isn't possible unless we are at end of
670 : : * command. Return NULL instead.
671 : : */
672 : : if (mybuf.len == 0 && *quote == 0)
2948 tgl@sss.pgh.pa.us 673 [ + + + + ]:CBC 16974 : {
674 : : termPQExpBuffer(&mybuf);
675 : 9247 : return NULL;
676 : 9247 : }
677 : :
678 : : /* Else return the completed string. */
679 : : return mybuf.data;
680 : 7727 : }
681 : :
682 : : /*
683 : : * Eat up any unused \\ to complete a backslash command.
684 : : */
685 : : void
686 : : psql_scan_slash_command_end(PsqlScanState state)
687 : 6597 : {
688 : : /* Must be scanning already */
689 : : Assert(state->scanbufhandle != NULL);
690 [ - + ]: 6597 :
691 : : /* Set current output target */
692 : : state->output_buf = NULL; /* we won't output anything */
693 : 6597 :
694 : : /* Set input source */
695 : : if (state->buffer_stack != NULL)
696 [ - + ]: 6597 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
2948 tgl@sss.pgh.pa.us 697 :UBC 0 : else
698 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
2948 tgl@sss.pgh.pa.us 699 :CBC 6597 :
700 : : /* Set lexer start state */
701 : : state->start_state = xslashend;
702 : 6597 :
703 : : /* And lex. */
704 : : yylex(NULL, state->scanner);
705 : 6597 :
706 : : /* There are no possible errors in this lex state... */
707 : :
708 : : /*
709 : : * We expect the caller to return to using the regular SQL lexer, so
710 : : * reselect the appropriate initial state.
711 : : */
712 : : psql_scan_reselect_sql_lexer(state);
713 : 6597 : }
714 : 6597 :
715 : : /*
716 : : * Fetch current paren nesting depth
717 : : */
718 : : int
719 : : psql_scan_get_paren_depth(PsqlScanState state)
2572 720 : 122 : {
721 : : return state->paren_depth;
722 : 122 : }
723 : :
724 : : /*
725 : : * Set paren nesting depth
726 : : */
727 : : void
728 : : psql_scan_set_paren_depth(PsqlScanState state, int depth)
729 : 104 : {
730 : : Assert(depth >= 0);
731 [ - + ]: 104 : state->paren_depth = depth;
732 : 104 : }
733 : 104 :
734 : : /*
735 : : * De-quote and optionally downcase a SQL identifier.
736 : : *
737 : : * The string at *str is modified in-place; it can become shorter,
738 : : * but not longer.
739 : : *
740 : : * If downcase is true then non-quoted letters are folded to lower case.
741 : : * Ideally this behavior will match the backend's downcase_identifier();
742 : : * but note that it could differ if LC_CTYPE is different in the frontend.
743 : : *
744 : : * Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
745 : : * this is somewhat inconsistent with the SQL spec, which would have us
746 : : * parse it as several identifiers. But for psql's purposes, we want a
747 : : * string like "foo"."bar" to be treated as one option, so there's little
748 : : * choice; this routine doesn't get to change the token boundaries.
749 : : */
750 : : void
751 : : dequote_downcase_identifier(char *str, bool downcase, int encoding)
2919 752 : 209 : {
753 : : bool inquotes = false;
754 : 209 : char *cp = str;
755 : 209 :
756 : : while (*cp)
757 [ + + ]: 1367 : {
758 : : if (*cp == '"')
759 [ + + ]: 1158 : {
760 : : if (inquotes && cp[1] == '"')
761 [ + + + + ]: 62 : {
762 : : /* Keep the first quote, remove the second */
763 : : cp++;
764 : 10 : }
765 : : else
766 : : inquotes = !inquotes;
767 : 52 : /* Collapse out quote at *cp */
768 : : memmove(cp, cp + 1, strlen(cp));
769 : 62 : /* do not advance cp */
770 : : }
771 : : else
772 : : {
773 : : if (downcase && !inquotes)
774 [ + + + + ]: 1096 : *cp = pg_tolower((unsigned char) *cp);
1042 775 : 138 : cp += PQmblenBounded(cp, encoding);
2919 776 : 1096 : }
777 : : }
778 : : }
779 : 209 :
780 : : /*
781 : : * Evaluate a backticked substring of a slash command's argument.
782 : : *
783 : : * The portion of output_buf starting at backtick_start_offset is evaluated
784 : : * as a shell command and then replaced by the command's output.
785 : : */
786 : : static void
787 : : evaluate_backtick(PsqlScanState state)
2948 tgl@sss.pgh.pa.us 788 :UBC 0 : {
789 : : PQExpBuffer output_buf = state->output_buf;
790 : 0 : char *cmd = output_buf->data + backtick_start_offset;
791 : 0 : PQExpBufferData cmd_output;
792 : : FILE *fd;
793 : : bool error = false;
390 794 : 0 : int exit_code = 0;
2948 795 : 0 : char buf[512];
796 : : size_t result;
797 : :
798 : : initPQExpBuffer(&cmd_output);
799 : 0 :
800 : : fflush(NULL);
1264 801 : 0 : fd = popen(cmd, "r");
2948 802 : 0 : if (!fd)
803 [ # # ]: 0 : {
804 : : pg_log_error("%s: %m", cmd);
805 : 0 : error = true;
390 806 : 0 : exit_code = -1;
2948 807 : 0 : }
808 : :
809 : : if (!error)
810 [ # # ]: 0 : {
811 : : do
812 : : {
813 : : result = fread(buf, 1, sizeof(buf), fd);
814 : 0 : if (ferror(fd))
815 [ # # ]: 0 : {
816 : : pg_log_error("%s: %m", cmd);
817 : 0 : error = true;
818 : 0 : break;
819 : 0 : }
820 : : appendBinaryPQExpBuffer(&cmd_output, buf, result);
821 : 0 : } while (!feof(fd));
822 [ # # ]: 0 : }
823 : :
824 : : if (fd)
825 [ # # ]: 0 : {
826 : : /*
827 : : * Although pclose's result always sets the shell result variables, we
828 : : * historically have abandoned the backtick substitution only if it
829 : : * returns -1.
830 : : */
831 : : exit_code = pclose(fd);
390 832 : 0 : if (exit_code == -1)
833 [ # # ]: 0 : {
834 : : pg_log_error("%s: %m", cmd);
835 : 0 : error = true;
836 : 0 : }
837 : : }
838 : :
839 : : if (PQExpBufferDataBroken(cmd_output))
2948 840 [ # # ]: 0 : {
841 : : pg_log_error("%s: out of memory", cmd);
842 : 0 : error = true;
843 : 0 : }
844 : :
845 : : /* Now done with cmd, delete it from output_buf */
846 : : output_buf->len = backtick_start_offset;
847 : 0 : output_buf->data[output_buf->len] = '\0';
848 : 0 :
849 : : /* If no error, transfer result to output_buf */
850 : : if (!error)
851 [ # # ]: 0 : {
852 : : /* strip any trailing newline (but only one) */
853 : : if (cmd_output.len > 0 &&
854 [ # # ]: 0 : cmd_output.data[cmd_output.len - 1] == '\n')
855 [ # # ]: 0 : cmd_output.len--;
856 : 0 : appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
857 : 0 : }
858 : :
859 : : /* And finally, set the shell result variables */
860 : : SetShellResultVariables(exit_code);
390 861 : 0 :
862 : : termPQExpBuffer(&cmd_output);
2948 863 : 0 : }
|