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-2026, 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;
3813 tgl@sss.pgh.pa.us 119 :CBC 154677 : PQExpBuffer output_buf = cur_state->output_buf;
120 : 154677 :
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 : 154677 : %}
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; }
3813 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 */
3813 tgl@sss.pgh.pa.us 144 :CBC 29197 :
145 : : {space}|"\\" {
146 : : yyless(0);
147 : 29197 : cur_state->start_state = YY_START;
148 : 29197 : return LEXRES_OK;
149 : 29197 : }
150 : :
151 : : {other} { ECHO; }
152 : 128908 :
153 : 128908 : }
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 : 32751 :
162 : : {space}+ { }
163 : :
164 : 32751 : "|" {
165 : 20 : if (option_type == OT_FILEPIPE)
166 [ + + ]: 20 : {
167 : : /* treat like whole-string case */
168 : : ECHO;
169 : 8 : BEGIN(xslashwholeline);
170 : 8 : }
171 : : else
172 : : {
173 : : /* vertical bar is not special otherwise */
174 : : yyless(0);
175 : 12 : BEGIN(xslasharg);
176 : 12 : }
177 : : }
178 : :
179 : 20 : {other} {
180 : 33427 : yyless(0);
181 : 33427 : BEGIN(xslasharg);
182 : 33427 : }
183 : :
184 : 33427 : }
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 : 5338 :
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 : 5338 : cur_state->start_state = YY_START;
207 : 5338 : return LEXRES_OK;
208 : 5338 : }
209 : :
210 : : {quote} {
211 : 20958 : *option_quote = '\'';
212 : 20958 : unquoted_option_chars = 0;
213 : 20958 : BEGIN(xslashquote);
214 : 20958 : }
215 : :
216 : 20958 : "`" {
217 : 12 : backtick_start_offset = output_buf->len;
218 : 12 : *option_quote = '`';
219 : 12 : unquoted_option_chars = 0;
220 : 12 : BEGIN(xslashbackquote);
221 : 12 : }
222 : :
223 : 12 : {dquote} {
224 : 991 : ECHO;
225 : 991 : *option_quote = '"';
226 : 991 : unquoted_option_chars = 0;
227 : 991 : BEGIN(xslashdquote);
228 : 991 : }
229 : :
230 : 991 : :{variable_char}+ {
231 : 862 : /* Possible psql variable substitution */
232 : : if (cur_state->callbacks->get_variable == NULL)
233 [ - + ]: 862 : ECHO;
3813 tgl@sss.pgh.pa.us 234 :UBC 0 : else
235 : : {
236 : : char *varname;
237 : : char *value;
238 : :
239 : : varname = psqlscan_extract_substring(cur_state,
3813 tgl@sss.pgh.pa.us 240 :CBC 862 : yytext + 1,
241 : 862 : yyleng - 1);
242 : 862 : value = cur_state->callbacks->get_variable(varname,
3435 243 : 862 : PQUOTE_PLAIN,
244 : : cur_state->cb_passthrough);
245 : : free(varname);
3813 246 : 862 :
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 [ + + ]: 862 : {
256 : : appendPQExpBufferStr(output_buf, value);
257 : 838 : free(value);
258 : 838 : }
259 : : else
260 : : ECHO;
261 : 24 :
262 : : *option_quote = ':';
263 : 862 : }
264 : : unquoted_option_chars = 0;
265 : 862 : }
266 : :
267 : 862 : :'{variable_char}+' {
3435 268 : 28 : psqlscan_escape_variable(cur_state, yytext, yyleng,
269 : 28 : PQUOTE_SQL_LITERAL);
270 : : *option_quote = ':';
3813 271 : 28 : unquoted_option_chars = 0;
272 : 28 : }
273 : :
274 : 28 :
275 : 16 : :\"{variable_char}+\" {
276 : : psqlscan_escape_variable(cur_state, yytext, yyleng,
3435 277 : 16 : PQUOTE_SQL_IDENT);
278 : : *option_quote = ':';
3813 279 : 16 : unquoted_option_chars = 0;
280 : 16 : }
281 : :
3262 andrew@dunslane.net 282 : 16 : :\{\?{variable_char}+\} {
283 : 13 : psqlscan_test_variable(cur_state, yytext, yyleng);
284 : 13 : }
285 : :
3813 tgl@sss.pgh.pa.us 286 : 13 : :'{variable_char}* {
3813 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 : :
3262 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 : :
3813 tgl@sss.pgh.pa.us 314 : 0 : {other} {
3813 tgl@sss.pgh.pa.us 315 :CBC 92231 : unquoted_option_chars++;
316 : 92231 : ECHO;
317 : 92231 : }
318 : :
319 : 92231 : }
320 : :
321 : : <xslashquote>{
322 : : /*
323 : : * single-quoted text: copy literally except for '' and backslash
324 : : * sequences
325 : : */
326 : 20958 :
327 : : {quote} { BEGIN(xslasharg); }
328 : 20958 :
329 : 20958 : {xqdouble} { appendPQExpBufferChar(output_buf, '\''); }
330 : 58 :
331 : 58 : "\\n" { appendPQExpBufferChar(output_buf, '\n'); }
332 : 4 : "\\t" { appendPQExpBufferChar(output_buf, '\t'); }
333 : 4 : "\\b" { appendPQExpBufferChar(output_buf, '\b'); }
334 : 4 : "\\r" { appendPQExpBufferChar(output_buf, '\r'); }
3813 tgl@sss.pgh.pa.us 335 :UBC 0 : "\\f" { appendPQExpBufferChar(output_buf, '\f'); }
3813 tgl@sss.pgh.pa.us 336 :CBC 4 :
3813 tgl@sss.pgh.pa.us 337 :UBC 0 : {xeoctesc} {
3813 tgl@sss.pgh.pa.us 338 :CBC 4 : /* octal case */
339 : : appendPQExpBufferChar(output_buf,
340 : 4 : (char) strtol(yytext + 1, NULL, 8));
341 : 4 : }
342 : :
343 : 4 : {xehexesc} {
3813 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); }
3813 tgl@sss.pgh.pa.us 350 :CBC 12 :
351 : 12 : {other}|\n { ECHO; }
352 : 736989 :
353 : 736989 : }
354 : :
355 : : <xslashbackquote>{
356 : : /*
357 : : * backticked text: copy everything until next backquote (expanding
358 : : * variable references, but doing nought else), then evaluate.
359 : : */
360 : 12 :
361 : : "`" {
362 : : /* In an inactive \if branch, don't evaluate the command */
363 : : if (cur_state->cb_passthrough == NULL ||
3437 364 [ + - - + ]: 24 : conditional_active((ConditionalStack) cur_state->cb_passthrough))
3813 365 : 12 : evaluate_backtick(cur_state);
3813 tgl@sss.pgh.pa.us 366 :UBC 0 : BEGIN(xslasharg);
3813 tgl@sss.pgh.pa.us 367 :CBC 12 : }
368 : :
3435 369 : 12 : :{variable_char}+ {
3435 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 : :
3813 407 : 0 : {other}|\n { ECHO; }
3813 tgl@sss.pgh.pa.us 408 :CBC 156 :
409 : 156 : }
410 : :
411 : : <xslashdquote>{
412 : : /* double-quoted text: copy verbatim, including the double quotes */
413 : 991 :
414 : : {dquote} {
415 : : ECHO;
416 : 991 : BEGIN(xslasharg);
417 : 991 : }
418 : :
419 : 991 : {other}|\n { ECHO; }
420 : 18127 :
421 : 18127 : }
422 : :
423 : : <xslashwholeline>{
424 : : /* copy everything until end of input line */
425 : : /* but suppress leading whitespace */
426 : 808 :
427 : : {space}+ {
428 : : if (output_buf->len > 0)
429 [ + + ]: 808 : ECHO;
430 : 464 : }
431 : :
432 : 808 : {other} { ECHO; }
433 : 10065 :
434 : 10065 : }
435 : :
436 : : <xslashend>{
437 : : /* at end of command, eat a double backslash, but not anything else */
438 : 46 :
439 : : "\\\\" {
440 : : cur_state->start_state = YY_START;
441 : 46 : return LEXRES_OK;
442 : 46 : }
443 : :
444 : : {other}|\n {
445 : 698 : yyless(0);
446 : 698 : cur_state->start_state = YY_START;
447 : 698 : return LEXRES_OK;
448 : 698 : }
449 : :
450 : : }
451 : :
452 : 119398 : <<EOF>> {
453 : : if (cur_state->buffer_stack == NULL)
454 [ + - ]: 119398 : {
455 : : cur_state->start_state = YY_START;
456 : 119398 : return LEXRES_EOL; /* end of input reached */
457 : 119398 : }
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);
3813 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, or NULL on out-of-memory.
478 : : */
479 : : char *
480 : : psql_scan_slash_command(PsqlScanState state)
3813 tgl@sss.pgh.pa.us 481 :CBC 32450 : {
482 : : PQExpBufferData mybuf;
483 : :
484 : : /* Must be scanning already */
485 : : Assert(state->scanbufhandle != NULL);
486 [ - + ]: 32450 :
487 : : /* Build a local buffer that we'll return the data of */
488 : : initPQExpBuffer(&mybuf);
489 : 32450 :
490 : : /* Set current output target */
491 : : state->output_buf = &mybuf;
492 : 32450 :
493 : : /* Set input source */
494 : : if (state->buffer_stack != NULL)
495 [ - + ]: 32450 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
3813 tgl@sss.pgh.pa.us 496 :UBC 0 : else
497 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
3813 tgl@sss.pgh.pa.us 498 :CBC 32450 :
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 : 32450 :
505 : : /* And lex. */
506 : : yylex(NULL, state->scanner);
507 : 32450 :
508 : : /* There are no possible syntax 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 : 32450 :
516 : : /*
517 : : * yylex() appends command-name text to mybuf, so a buffer enlargement
518 : : * failure during lexing can leave mybuf broken even if initialization
519 : : * succeeded.
520 : : */
521 : : if (PQExpBufferDataBroken(mybuf))
8 fujii@postgresql.org 522 [ - + ]: 32450 : {
523 : : pg_log_error("out of memory");
8 fujii@postgresql.org 524 :UBC 0 : return NULL;
525 : 0 : }
526 : :
527 : : return mybuf.data;
3813 tgl@sss.pgh.pa.us 528 :CBC 32450 : }
529 : :
530 : : /*
531 : : * Parse off the next argument for a backslash command, and return it as a
532 : : * malloc'd string. If there are no more arguments or on out-of-memory,
533 : : * returns NULL.
534 : : *
535 : : * type tells what processing, if any, to perform on the option string;
536 : : * for example, if it's a SQL identifier, we want to downcase any unquoted
537 : : * letters.
538 : : *
539 : : * if quote is not NULL, *quote is set to 0 if no quoting was found, else
540 : : * the last quote symbol used in the argument.
541 : : *
542 : : * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
543 : : * be taken as part of the option string will be stripped.
544 : : *
545 : : * NOTE: the only possible syntax errors for backslash options are unmatched
546 : : * quotes, which are detected when we run out of input. Therefore, on a
547 : : * syntax error we just throw away the string and return NULL; there is no
548 : : * need to worry about flushing remaining input.
549 : : */
550 : : char *
551 : : psql_scan_slash_option(PsqlScanState state,
552 : 89779 : enum slash_option_type type,
553 : : char *quote,
554 : : bool semicolon)
555 : : {
556 : : PQExpBufferData mybuf;
557 : : int lexresult PG_USED_FOR_ASSERTS_ONLY;
558 : : int final_state;
559 : : char local_quote;
560 : :
561 : : /* Must be scanning already */
562 : : Assert(state->scanbufhandle != NULL);
563 [ - + ]: 89779 :
564 : : if (quote == NULL)
565 [ + + ]: 89779 : quote = &local_quote;
566 : 47462 : *quote = 0;
567 : 89779 :
568 : : /* Build a local buffer that we'll return the data of */
569 : : initPQExpBuffer(&mybuf);
570 : 89779 :
571 : : /* Set up static variables that will be used by yylex */
572 : : option_type = type;
573 : 89779 : option_quote = quote;
574 : 89779 : unquoted_option_chars = 0;
575 : 89779 :
576 : : /* Set current output target */
577 : : state->output_buf = &mybuf;
578 : 89779 :
579 : : /* Set input source */
580 : : if (state->buffer_stack != NULL)
581 [ - + ]: 89779 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
3813 tgl@sss.pgh.pa.us 582 :UBC 0 : else
583 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
3813 tgl@sss.pgh.pa.us 584 :CBC 89779 :
585 : : /* Set lexer start state */
586 : : if (type == OT_WHOLE_LINE)
587 [ + + ]: 89779 : state->start_state = xslashwholeline;
588 : 1142 : else
589 : : state->start_state = xslashargstart;
590 : 88637 :
591 : : /* And lex. */
592 : : lexresult = yylex(NULL, state->scanner);
593 : 89779 :
594 : : /* Save final state for a moment... */
595 : : final_state = state->start_state;
596 : 89779 :
597 : : /*
598 : : * In case the caller returns to using the regular SQL lexer, reselect the
599 : : * appropriate initial state.
600 : : */
601 : : psql_scan_reselect_sql_lexer(state);
602 : 89779 :
603 : : /*
604 : : * Check the lex result: we should have gotten back either LEXRES_OK or
605 : : * LEXRES_EOL (the latter indicating end of string). If we were inside a
606 : : * quoted string, as indicated by final_state, EOL is an error.
607 : : */
608 : : Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
609 [ + + - + ]: 89779 :
610 : : /*
611 : : * yylex() appends option text to mybuf, so a buffer enlargement failure
612 : : * during lexing can leave mybuf broken even if initialization succeeded.
613 : : */
614 : : if (PQExpBufferDataBroken(mybuf))
8 fujii@postgresql.org 615 [ - + ]: 89779 : {
616 : : pg_log_error("out of memory");
8 fujii@postgresql.org 617 :UBC 0 : return NULL;
618 : 0 : }
619 : :
620 : : switch (final_state)
3813 tgl@sss.pgh.pa.us 621 [ + + - + :CBC 89779 : {
- ]
622 : : case xslashargstart:
623 : 55190 : /* empty arg */
624 : : break;
625 : 55190 : case xslasharg:
960 626 : 33439 : /* Strip any unquoted trailing semicolons if requested */
627 : : if (semicolon)
3813 628 [ + + ]: 33439 : {
629 : : while (unquoted_option_chars-- > 0 &&
630 : 4997 : mybuf.len > 0 &&
631 [ + + + - ]: 5005 : mybuf.data[mybuf.len - 1] == ';')
632 [ + + ]: 4435 : {
633 : : mybuf.data[--mybuf.len] = '\0';
634 : 8 : }
635 : : }
636 : :
637 : : /*
638 : : * If SQL identifier processing was requested, then we strip out
639 : : * excess double quotes and optionally downcase unquoted letters.
640 : : */
641 : : if (type == OT_SQLID || type == OT_SQLIDHACK)
642 [ + + + + ]: 33439 : {
643 : : dequote_downcase_identifier(mybuf.data,
3784 644 : 174 : (type != OT_SQLIDHACK),
645 : : state->encoding);
646 : : /* update mybuf.len for possible shortening */
647 : : mybuf.len = strlen(mybuf.data);
3813 648 : 174 : }
649 : : break;
650 : 33439 : case xslashquote:
3813 tgl@sss.pgh.pa.us 651 :UBC 0 : case xslashbackquote:
652 : : case xslashdquote:
653 : : /* must have hit EOL inside quotes */
654 : : pg_log_error("unterminated quoted string");
655 : 0 : termPQExpBuffer(&mybuf);
656 : 0 : return NULL;
657 : 0 : case xslashwholeline:
610 peter@eisentraut.org 658 :CBC 1150 :
659 : : /*
660 : : * In whole-line mode, we interpret semicolon = true as stripping
661 : : * trailing whitespace as well as semicolons; this gives the
662 : : * nearest equivalent to what semicolon = true does in normal
663 : : * mode. Note there's no concept of quoting in this mode.
664 : : */
665 : : if (semicolon)
960 tgl@sss.pgh.pa.us 666 [ + + ]: 1150 : {
667 : : while (mybuf.len > 0 &&
668 [ + + ]: 229 : (mybuf.data[mybuf.len - 1] == ';' ||
669 [ + + ]: 228 : (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
670 [ + - ]: 204 : isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
671 [ - + ]: 204 : {
672 : : mybuf.data[--mybuf.len] = '\0';
673 : 24 : }
674 : : }
675 : : break;
3813 676 : 1150 : default:
3813 tgl@sss.pgh.pa.us 677 :UBC 0 : /* can't get here */
678 : : fprintf(stderr, "invalid YY_START\n");
679 : 0 : exit(1);
680 : 0 : }
681 : :
682 : : /*
683 : : * An unquoted empty argument isn't possible unless we are at end of
684 : : * command. Return NULL instead.
685 : : */
686 : : if (mybuf.len == 0 && *quote == 0)
3813 tgl@sss.pgh.pa.us 687 [ + + + + ]:CBC 89779 : {
688 : : termPQExpBuffer(&mybuf);
689 : 57396 : return NULL;
690 : 57396 : }
691 : :
692 : : /* Else return the completed string. */
693 : : return mybuf.data;
694 : 32383 : }
695 : :
696 : : /*
697 : : * Eat up any unused \\ to complete a backslash command.
698 : : */
699 : : void
700 : : psql_scan_slash_command_end(PsqlScanState state)
701 : 32448 : {
702 : : /* Must be scanning already */
703 : : Assert(state->scanbufhandle != NULL);
704 [ - + ]: 32448 :
705 : : /* Set current output target */
706 : : state->output_buf = NULL; /* we won't output anything */
707 : 32448 :
708 : : /* Set input source */
709 : : if (state->buffer_stack != NULL)
710 [ - + ]: 32448 : yy_switch_to_buffer(state->buffer_stack->buf, state->scanner);
3813 tgl@sss.pgh.pa.us 711 :UBC 0 : else
712 : : yy_switch_to_buffer(state->scanbufhandle, state->scanner);
3813 tgl@sss.pgh.pa.us 713 :CBC 32448 :
714 : : /* Set lexer start state */
715 : : state->start_state = xslashend;
716 : 32448 :
717 : : /* And lex. */
718 : : yylex(NULL, state->scanner);
719 : 32448 :
720 : : /* There are no possible errors in this lex state... */
721 : :
722 : : /*
723 : : * We expect the caller to return to using the regular SQL lexer, so
724 : : * reselect the appropriate initial state.
725 : : */
726 : : psql_scan_reselect_sql_lexer(state);
727 : 32448 : }
728 : 32448 :
729 : : /*
730 : : * Save current lexer state
731 : : *
732 : : * Relevant parts of the state are returned in a pg_malloc'd struct.
733 : : * It is caller's responsibility to free the struct eventually.
734 : : */
735 : : PsqlScanStateSave *
736 : : psql_scan_get_lex_state(PsqlScanState state)
3437 737 : 200 : {
738 : : PsqlScanStateSave *lex_state = pg_malloc_object(PsqlScanStateSave);
17 739 : 200 : StaticAssertDecl(sizeof(lex_state->init_idents) == sizeof(state->init_idents),
740 : : "init_idents array lengths must match");
741 : : StaticAssertDecl(sizeof(lex_state->sub_idents) == sizeof(state->sub_idents),
742 : : "sub_idents array lengths must match");
743 : :
744 : : lex_state->paren_depth = state->paren_depth;
745 : 200 : lex_state->begin_depth = state->begin_depth;
746 : 200 : lex_state->copy_stdin_count = state->copy_stdin_count;
747 : 200 : lex_state->init_idents_count = state->init_idents_count;
748 : 200 : memcpy(lex_state->init_idents, state->init_idents,
749 : 200 : sizeof(lex_state->init_idents));
750 : : lex_state->sub_idents_count = state->sub_idents_count;
751 : 200 : memcpy(lex_state->sub_idents, state->sub_idents,
752 : 200 : sizeof(lex_state->sub_idents));
753 : : return lex_state;
3437 754 : 200 : }
755 : :
756 : : /*
757 : : * Restore lexer state to what it was when saved
758 : : */
759 : : void
760 : : psql_scan_set_lex_state(PsqlScanState state,
17 761 : 169 : const PsqlScanStateSave *lex_state)
762 : : {
763 : : state->paren_depth = lex_state->paren_depth;
764 : 169 : state->begin_depth = lex_state->begin_depth;
765 : 169 : state->copy_stdin_count = lex_state->copy_stdin_count;
766 : 169 : state->init_idents_count = lex_state->init_idents_count;
767 : 169 : memcpy(state->init_idents, lex_state->init_idents,
768 : 169 : sizeof(state->init_idents));
769 : : state->sub_idents_count = lex_state->sub_idents_count;
770 : 169 : memcpy(state->sub_idents, lex_state->sub_idents,
771 : 169 : sizeof(state->sub_idents));
772 : : }
3437 773 : 169 :
774 : : /*
775 : : * De-quote and optionally downcase a SQL identifier.
776 : : *
777 : : * The string at *str is modified in-place; it can become shorter,
778 : : * but not longer.
779 : : *
780 : : * If downcase is true then non-quoted letters are folded to lower case.
781 : : * Ideally this behavior will match the backend's downcase_identifier();
782 : : * but note that it could differ if LC_CTYPE is different in the frontend.
783 : : *
784 : : * Note that a string like FOO"BAR"BAZ will be converted to fooBARbaz;
785 : : * this is somewhat inconsistent with the SQL spec, which would have us
786 : : * parse it as several identifiers. But for psql's purposes, we want a
787 : : * string like "foo"."bar" to be treated as one option, so there's little
788 : : * choice; this routine doesn't get to change the token boundaries.
789 : : */
790 : : void
791 : : dequote_downcase_identifier(char *str, bool downcase, int encoding)
3784 792 : 322 : {
793 : : bool inquotes = false;
794 : 322 : char *cp = str;
795 : 322 :
796 : : while (*cp)
797 [ + + ]: 2124 : {
798 : : if (*cp == '"')
799 [ + + ]: 1802 : {
800 : : if (inquotes && cp[1] == '"')
801 [ + + + + ]: 82 : {
802 : : /* Keep the first quote, remove the second */
803 : : cp++;
804 : 12 : }
805 : : else
806 : : inquotes = !inquotes;
807 : 70 : /* Collapse out quote at *cp */
808 : : memmove(cp, cp + 1, strlen(cp));
809 : 82 : /* do not advance cp */
810 : : }
811 : : else
812 : : {
813 : : if (downcase && !inquotes)
814 [ + + + + ]: 1720 : *cp = pg_tolower((unsigned char) *cp);
1907 815 : 295 : cp += PQmblenBounded(cp, encoding);
3784 816 : 1720 : }
817 : : }
818 : : }
819 : 322 :
820 : : /*
821 : : * Evaluate a backticked substring of a slash command's argument.
822 : : *
823 : : * The portion of output_buf starting at backtick_start_offset is evaluated
824 : : * as a shell command and then replaced by the command's output.
825 : : */
826 : : static void
827 : : evaluate_backtick(PsqlScanState state)
3813 tgl@sss.pgh.pa.us 828 :UBC 0 : {
829 : : PQExpBuffer output_buf = state->output_buf;
8 fujii@postgresql.org 830 : 0 : char *cmd;
831 : : PQExpBufferData cmd_output;
832 : : FILE *fd;
833 : : bool error = false;
1255 tgl@sss.pgh.pa.us 834 : 0 : int exit_code = 0;
3813 835 : 0 : char buf[512];
836 : : size_t result;
837 : :
838 : : /*
839 : : * The option buffer is already broken; avoid touching the static
840 : : * oom_buffer and let psql_scan_slash_option() return NULL.
841 : : */
842 : : if (PQExpBufferBroken(output_buf))
8 fujii@postgresql.org 843 [ # # # # ]: 0 : return;
844 : 0 :
845 : : cmd = output_buf->data + backtick_start_offset;
846 : 0 :
847 : : initPQExpBuffer(&cmd_output);
3813 tgl@sss.pgh.pa.us 848 : 0 :
849 : : fflush(NULL);
2129 850 : 0 : fd = popen(cmd, "r");
3813 851 : 0 : if (!fd)
852 [ # # ]: 0 : {
853 : : pg_log_error("%s: %m", cmd);
854 : 0 : error = true;
1255 855 : 0 : exit_code = -1;
3813 856 : 0 : }
857 : :
858 : : if (!error)
859 [ # # ]: 0 : {
860 : : do
861 : : {
862 : : result = fread(buf, 1, sizeof(buf), fd);
863 : 0 : if (ferror(fd))
864 [ # # ]: 0 : {
865 : : pg_log_error("%s: %m", cmd);
866 : 0 : error = true;
867 : 0 : break;
868 : 0 : }
869 : : appendBinaryPQExpBuffer(&cmd_output, buf, result);
870 : 0 : } while (!feof(fd));
871 [ # # ]: 0 : }
872 : :
873 : : if (fd)
874 [ # # ]: 0 : {
875 : : /*
876 : : * Although pclose's result always sets the shell result variables, we
877 : : * historically have abandoned the backtick substitution only if it
878 : : * returns -1.
879 : : */
880 : : exit_code = pclose(fd);
1255 881 : 0 : if (exit_code == -1)
882 [ # # ]: 0 : {
883 : : pg_log_error("%s: %m", cmd);
884 : 0 : error = true;
885 : 0 : }
886 : : }
887 : :
888 : : if (PQExpBufferDataBroken(cmd_output))
3813 889 [ # # ]: 0 : {
890 : : pg_log_error("%s: out of memory", cmd);
891 : 0 : error = true;
892 : 0 : }
893 : :
894 : : /* Now done with cmd, delete it from output_buf */
895 : : output_buf->len = backtick_start_offset;
896 : 0 : output_buf->data[output_buf->len] = '\0';
897 : 0 :
898 : : /* If no error, transfer result to output_buf */
899 : : if (!error)
900 [ # # ]: 0 : {
901 : : /* strip any trailing newline (but only one) */
902 : : if (cmd_output.len > 0 &&
903 [ # # ]: 0 : cmd_output.data[cmd_output.len - 1] == '\n')
904 [ # # ]: 0 : cmd_output.len--;
905 : 0 : appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
906 : 0 : }
907 : :
908 : : /* And finally, set the shell result variables */
909 : : SetShellResultVariables(exit_code);
1255 910 : 0 :
911 : : termPQExpBuffer(&cmd_output);
3813 912 : 0 : }
913 : : /* END: function "evaluate_backtick" */
|