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