Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * psql - the PostgreSQL interactive terminal
3 : : *
4 : : * Copyright (c) 2000-2025, PostgreSQL Global Development Group
5 : : *
6 : : * src/bin/psql/mainloop.c
7 : : */
8 : : #include "postgres_fe.h"
9 : :
10 : : #include "command.h"
11 : : #include "common.h"
12 : : #include "common/logging.h"
13 : : #include "input.h"
14 : : #include "mainloop.h"
15 : : #include "mb/pg_wchar.h"
16 : : #include "prompt.h"
17 : : #include "settings.h"
18 : :
19 : : /* callback functions for our flex lexer */
20 : : const PsqlScanCallbacks psqlscan_callbacks = {
21 : : psql_get_variable,
22 : : };
23 : :
24 : :
25 : : /*
26 : : * Main processing loop for reading lines of input
27 : : * and sending them to the backend.
28 : : *
29 : : * This loop is re-entrant. May be called by \i command
30 : : * which reads input from a file.
31 : : */
32 : : int
9414 peter_e@gmx.net 33 :CBC 8043 : MainLoop(FILE *source)
34 : : {
35 : : PsqlScanState scan_state; /* lexer working state */
36 : : ConditionalStack cond_stack; /* \if status stack */
37 : : volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
38 : : volatile PQExpBuffer previous_buf; /* if there isn't anything in the new
39 : : * buffer yet, use this one for \e,
40 : : * etc. */
41 : : PQExpBuffer history_buf; /* earlier lines of a multi-line command, not
42 : : * yet saved to readline history */
43 : : char *line; /* current line of input */
44 : : int added_nl_pos;
45 : : bool success;
46 : : bool line_saved_in_history;
9371 47 : 8043 : volatile int successResult = EXIT_SUCCESS;
7253 48 : 8043 : volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN;
7921 tgl@sss.pgh.pa.us 49 : 8043 : volatile promptStatus_t prompt_status = PROMPT_READY;
2166 50 : 8043 : volatile bool need_redisplay = false;
9329 bruce@momjian.us 51 : 8043 : volatile int count_eof = 0;
7921 tgl@sss.pgh.pa.us 52 : 8043 : volatile bool die_on_error = false;
53 : : FILE *prev_cmd_source;
54 : : bool prev_cmd_interactive;
55 : : uint64 prev_lineno;
56 : :
57 : : /* Save the prior command source */
9418 peter_e@gmx.net 58 : 8043 : prev_cmd_source = pset.cur_cmd_source;
59 : 8043 : prev_cmd_interactive = pset.cur_cmd_interactive;
7921 tgl@sss.pgh.pa.us 60 : 8043 : prev_lineno = pset.lineno;
61 : : /* pset.stmt_lineno does not need to be saved and restored */
62 : :
63 : : /* Establish new source */
9418 peter_e@gmx.net 64 : 8043 : pset.cur_cmd_source = source;
65 [ + + + + ]: 8043 : pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
7921 tgl@sss.pgh.pa.us 66 : 8043 : pset.lineno = 0;
4073 andres@anarazel.de 67 : 8043 : pset.stmt_lineno = 1;
68 : :
69 : : /* Create working state */
3510 tgl@sss.pgh.pa.us 70 : 8043 : scan_state = psql_scan_create(&psqlscan_callbacks);
3133 71 : 8043 : cond_stack = conditional_stack_create();
333 peter@eisentraut.org 72 : 8043 : psql_scan_set_passthrough(scan_state, cond_stack);
73 : :
9489 bruce@momjian.us 74 : 8043 : query_buf = createPQExpBuffer();
9329 75 : 8043 : previous_buf = createPQExpBuffer();
7198 76 : 8043 : history_buf = createPQExpBuffer();
6179 tgl@sss.pgh.pa.us 77 [ + - + - ]: 8043 : if (PQExpBufferBroken(query_buf) ||
78 [ + - + - : 8043 : PQExpBufferBroken(previous_buf) ||
+ - ]
79 [ - + ]: 8043 : PQExpBufferBroken(history_buf))
1298 tgl@sss.pgh.pa.us 80 :UBC 0 : pg_fatal("out of memory");
81 : :
82 : : /* main loop to get queries and execute them */
8257 bruce@momjian.us 83 [ + + ]:CBC 410362 : while (successResult == EXIT_SUCCESS)
84 : : {
85 : : /*
86 : : * Clean up after a previous Control-C
87 : : */
9329 88 [ - + ]: 410316 : if (cancel_pressed)
89 : : {
9329 bruce@momjian.us 90 [ # # ]:UBC 0 : if (!pset.cur_cmd_interactive)
91 : : {
92 : : /*
93 : : * You get here if you stopped a script with Ctrl-C.
94 : : */
95 : 0 : successResult = EXIT_USER;
96 : 0 : break;
97 : : }
98 : :
99 : 0 : cancel_pressed = false;
100 : : }
101 : :
102 : : /*
103 : : * Establish longjmp destination for exiting from wait-for-input. We
104 : : * must re-do this each time through the loop for safety, since the
105 : : * jmpbuf might get changed during command execution.
106 : : */
7075 tgl@sss.pgh.pa.us 107 [ - + ]:CBC 410316 : if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
108 : : {
109 : : /* got here with longjmp */
110 : :
111 : : /* reset parsing state */
7921 tgl@sss.pgh.pa.us 112 :UBC 0 : psql_scan_finish(scan_state);
113 : 0 : psql_scan_reset(scan_state);
7075 114 : 0 : resetPQExpBuffer(query_buf);
115 : 0 : resetPQExpBuffer(history_buf);
7921 116 : 0 : count_eof = 0;
7253 peter_e@gmx.net 117 : 0 : slashCmdStatus = PSQL_CMD_UNKNOWN;
7921 tgl@sss.pgh.pa.us 118 : 0 : prompt_status = PROMPT_READY;
2166 119 : 0 : need_redisplay = false;
4073 andres@anarazel.de 120 : 0 : pset.stmt_lineno = 1;
7075 tgl@sss.pgh.pa.us 121 : 0 : cancel_pressed = false;
122 : :
9329 bruce@momjian.us 123 [ # # ]: 0 : if (pset.cur_cmd_interactive)
124 : : {
8257 125 : 0 : putc('\n', stdout);
126 : :
127 : : /*
128 : : * if interactive user is in an \if block, then Ctrl-C will
129 : : * exit from the innermost \if.
130 : : */
3133 tgl@sss.pgh.pa.us 131 [ # # ]: 0 : if (!conditional_stack_empty(cond_stack))
132 : : {
2401 peter@eisentraut.org 133 : 0 : pg_log_error("\\if: escaped");
3133 tgl@sss.pgh.pa.us 134 : 0 : conditional_stack_pop(cond_stack);
135 : : }
136 : : }
137 : : else
138 : : {
9329 bruce@momjian.us 139 : 0 : successResult = EXIT_USER;
140 : 0 : break;
141 : : }
142 : : }
143 : :
7950 tgl@sss.pgh.pa.us 144 :CBC 410316 : fflush(stdout);
145 : :
146 : : /*
147 : : * get another line
148 : : */
7078 149 [ + + ]: 410316 : if (pset.cur_cmd_interactive)
150 : : {
151 : : /* May need to reset prompt, eg after \r command */
7921 152 [ + + ]: 67 : if (query_buf->len == 0)
8120 bruce@momjian.us 153 : 65 : prompt_status = PROMPT_READY;
154 : : /* If query buffer came from \e, redisplay it with a prompt */
2166 tgl@sss.pgh.pa.us 155 [ - + ]: 67 : if (need_redisplay)
156 : : {
2166 tgl@sss.pgh.pa.us 157 [ # # ]:UBC 0 : if (query_buf->len > 0)
158 : : {
159 : 0 : fputs(get_prompt(PROMPT_READY, cond_stack), stdout);
160 : 0 : fputs(query_buf->data, stdout);
161 : 0 : fflush(stdout);
162 : : }
163 : 0 : need_redisplay = false;
164 : : }
165 : : /* Now we can fetch a line */
3133 tgl@sss.pgh.pa.us 166 :CBC 67 : line = gets_interactive(get_prompt(prompt_status, cond_stack),
167 : : query_buf);
168 : : }
169 : : else
170 : : {
8120 bruce@momjian.us 171 : 410249 : line = gets_fromFile(source);
6543 peter_e@gmx.net 172 [ + + - + ]: 410249 : if (!line && ferror(source))
6543 peter_e@gmx.net 173 :UBC 0 : successResult = EXIT_FAILURE;
174 : : }
175 : :
176 : : /*
177 : : * query_buf holds query already accumulated. line is the malloc'd
178 : : * new line of input (note it must be freed before looping around!)
179 : : */
180 : :
181 : : /* No more input. Time to quit, or \i done */
9486 bruce@momjian.us 182 [ + + ]:CBC 410316 : if (line == NULL)
183 : : {
9418 peter_e@gmx.net 184 [ - + ]: 7899 : if (pset.cur_cmd_interactive)
185 : : {
186 : : /* This tries to mimic bash's IGNOREEOF feature. */
8257 bruce@momjian.us 187 :UBC 0 : count_eof++;
188 : :
3189 tgl@sss.pgh.pa.us 189 [ # # ]: 0 : if (count_eof < pset.ignoreeof)
190 : : {
6999 191 [ # # ]: 0 : if (!pset.quiet)
7552 bruce@momjian.us 192 : 0 : printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
9329 193 : 0 : continue;
194 : : }
195 : :
6999 tgl@sss.pgh.pa.us 196 [ # # ]: 0 : puts(pset.quiet ? "" : "\\q");
197 : : }
8120 bruce@momjian.us 198 :CBC 7899 : break;
199 : : }
200 : :
201 : 402417 : count_eof = 0;
202 : :
9329 203 : 402417 : pset.lineno++;
204 : :
205 : : /* ignore UTF-8 Unicode byte-order mark */
5819 peter_e@gmx.net 206 [ + + + + : 402417 : if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0)
- + ]
5819 peter_e@gmx.net 207 :UBC 0 : memmove(line, line + 3, strlen(line + 3) + 1);
208 : :
209 : : /* Detect attempts to run custom-format dumps as SQL scripts */
4021 alvherre@alvh.no-ip. 210 [ + + + + ]:CBC 402417 : if (pset.lineno == 1 && !pset.cur_cmd_interactive &&
211 [ - + ]: 7901 : strncmp(line, "PGDMP", 5) == 0)
212 : : {
4021 alvherre@alvh.no-ip. 213 :UBC 0 : free(line);
214 : 0 : puts(_("The input is a PostgreSQL custom-format dump.\n"
215 : : "Use the pg_restore command-line client to restore this dump to a database.\n"));
216 : 0 : fflush(stdout);
217 : 0 : successResult = EXIT_FAILURE;
218 : 0 : break;
219 : : }
220 : :
221 : : /* no further processing of empty lines, unless within a literal */
7921 tgl@sss.pgh.pa.us 222 [ + + + + ]:CBC 402417 : if (line[0] == '\0' && !psql_scan_in_quote(scan_state))
223 : : {
9489 bruce@momjian.us 224 : 69123 : free(line);
225 : 69123 : continue;
226 : : }
227 : :
228 : : /* Recognize "help", "quit", "exit" only in interactive mode */
2825 229 [ + + ]: 333294 : if (pset.cur_cmd_interactive)
230 : : {
231 : 61 : char *first_word = line;
232 : 61 : char *rest_of_line = NULL;
233 : 61 : bool found_help = false;
234 : 61 : bool found_exit_or_quit = false;
2814 235 : 61 : bool found_q = false;
236 : :
237 : : /*
238 : : * The assistance words, help/exit/quit, must have no whitespace
239 : : * before them, and only whitespace after, with an optional
240 : : * semicolon. This prevents indented use of these words, perhaps
241 : : * as identifiers, from invoking the assistance behavior.
242 : : */
2825 243 [ - + ]: 61 : if (pg_strncasecmp(first_word, "help", 4) == 0)
244 : : {
2825 bruce@momjian.us 245 :UBC 0 : rest_of_line = first_word + 4;
246 : 0 : found_help = true;
247 : : }
2825 bruce@momjian.us 248 [ + - ]:CBC 61 : else if (pg_strncasecmp(first_word, "exit", 4) == 0 ||
249 [ - + ]: 61 : pg_strncasecmp(first_word, "quit", 4) == 0)
250 : : {
2825 bruce@momjian.us 251 :UBC 0 : rest_of_line = first_word + 4;
252 : 0 : found_exit_or_quit = true;
253 : : }
2814 bruce@momjian.us 254 [ + + ]:CBC 61 : else if (strncmp(first_word, "\\q", 2) == 0)
255 : : {
256 : 3 : rest_of_line = first_word + 2;
257 : 3 : found_q = true;
258 : : }
259 : :
260 : : /*
261 : : * If we found a command word, check whether the rest of the line
262 : : * contains only whitespace plus maybe one semicolon. If not,
263 : : * ignore the command word after all. These commands are only for
264 : : * compatibility with other SQL clients and are not documented.
265 : : */
2825 266 [ + + ]: 61 : if (rest_of_line != NULL)
267 : : {
268 : : /*
269 : : * Ignore unless rest of line is whitespace, plus maybe one
270 : : * semicolon
271 : : */
272 [ - + ]: 3 : while (isspace((unsigned char) *rest_of_line))
2825 bruce@momjian.us 273 :UBC 0 : ++rest_of_line;
2825 bruce@momjian.us 274 [ - + ]:CBC 3 : if (*rest_of_line == ';')
2825 bruce@momjian.us 275 :UBC 0 : ++rest_of_line;
2825 bruce@momjian.us 276 [ - + ]:CBC 3 : while (isspace((unsigned char) *rest_of_line))
2825 bruce@momjian.us 277 :UBC 0 : ++rest_of_line;
2825 bruce@momjian.us 278 [ - + ]:CBC 3 : if (*rest_of_line != '\0')
279 : : {
2825 bruce@momjian.us 280 :UBC 0 : found_help = false;
281 : 0 : found_exit_or_quit = false;
282 : : }
283 : : }
284 : :
285 : : /*
286 : : * "help" is only a command when the query buffer is empty, but we
287 : : * emit a one-line message even when it isn't to help confused
288 : : * users. The text is still added to the query buffer in that
289 : : * case.
290 : : */
2825 bruce@momjian.us 291 [ - + ]:CBC 61 : if (found_help)
292 : : {
2825 bruce@momjian.us 293 [ # # ]:UBC 0 : if (query_buf->len != 0)
294 : : #ifndef WIN32
295 : 0 : puts(_("Use \\? for help or press control-C to clear the input buffer."));
296 : : #else
297 : : puts(_("Use \\? for help."));
298 : : #endif
299 : : else
300 : : {
301 : 0 : puts(_("You are using psql, the command-line interface to PostgreSQL."));
302 : 0 : printf(_("Type: \\copyright for distribution terms\n"
303 : : " \\h for help with SQL commands\n"
304 : : " \\? for help with psql commands\n"
305 : : " \\g or terminate with semicolon to execute query\n"
306 : : " \\q to quit\n"));
307 : 0 : free(line);
308 : 0 : fflush(stdout);
309 : 0 : continue;
310 : : }
311 : : }
312 : :
313 : : /*
314 : : * "quit" and "exit" are only commands when the query buffer is
315 : : * empty, but we emit a one-line message even when it isn't to
316 : : * help confused users. The text is still added to the query
317 : : * buffer in that case.
318 : : */
2825 bruce@momjian.us 319 [ - + ]:CBC 61 : if (found_exit_or_quit)
320 : : {
2825 bruce@momjian.us 321 [ # # ]:UBC 0 : if (query_buf->len != 0)
322 : : {
323 [ # # ]: 0 : if (prompt_status == PROMPT_READY ||
324 [ # # ]: 0 : prompt_status == PROMPT_CONTINUE ||
325 [ # # ]: 0 : prompt_status == PROMPT_PAREN)
326 : 0 : puts(_("Use \\q to quit."));
327 : : else
328 : : #ifndef WIN32
329 : 0 : puts(_("Use control-D to quit."));
330 : : #else
331 : : puts(_("Use control-C to quit."));
332 : : #endif
333 : : }
334 : : else
335 : : {
336 : : /* exit app */
337 : 0 : free(line);
338 : 0 : fflush(stdout);
339 : 0 : successResult = EXIT_SUCCESS;
340 : 0 : break;
341 : : }
342 : : }
343 : :
344 : : /*
345 : : * If they typed "\q" in a place where "\q" is not active, supply
346 : : * a hint. The text is still added to the query buffer.
347 : : */
2814 bruce@momjian.us 348 [ + + - + ]:CBC 61 : if (found_q && query_buf->len != 0 &&
2814 bruce@momjian.us 349 [ # # ]:UBC 0 : prompt_status != PROMPT_READY &&
350 [ # # ]: 0 : prompt_status != PROMPT_CONTINUE &&
351 [ # # ]: 0 : prompt_status != PROMPT_PAREN)
352 : : #ifndef WIN32
2741 tgl@sss.pgh.pa.us 353 : 0 : puts(_("Use control-D to quit."));
354 : : #else
355 : : puts(_("Use control-C to quit."));
356 : : #endif
357 : : }
358 : :
359 : : /* echo back if flag is set, unless interactive */
6999 tgl@sss.pgh.pa.us 360 [ + + + - ]:CBC 333294 : if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive)
361 : : {
9329 bruce@momjian.us 362 : 316304 : puts(line);
3922 tgl@sss.pgh.pa.us 363 : 316304 : fflush(stdout);
364 : : }
365 : :
366 : : /* insert newlines into query buffer between source lines */
7921 367 [ + + ]: 333294 : if (query_buf->len > 0)
368 : : {
369 : 100436 : appendPQExpBufferChar(query_buf, '\n');
370 : 100436 : added_nl_pos = query_buf->len;
371 : : }
372 : : else
373 : 232858 : added_nl_pos = -1; /* flag we didn't add one */
374 : :
375 : : /* Setting this will not have effect until next line. */
6999 376 : 333294 : die_on_error = pset.on_error_stop;
377 : :
378 : : /*
379 : : * Parse line, looking for command separators.
380 : : */
3510 381 : 333294 : psql_scan_setup(scan_state, line, strlen(line),
382 : 333294 : pset.encoding, standard_strings());
9489 bruce@momjian.us 383 : 333294 : success = true;
7078 tgl@sss.pgh.pa.us 384 : 333294 : line_saved_in_history = false;
385 : :
7921 386 [ + + + + ]: 509954 : while (success || !die_on_error)
387 : : {
388 : : PsqlScanResult scan_result;
389 : 509908 : promptStatus_t prompt_tmp = prompt_status;
390 : : size_t pos_in_query;
391 : : char *tmp_line;
392 : :
4073 andres@anarazel.de 393 : 509908 : pos_in_query = query_buf->len;
7921 tgl@sss.pgh.pa.us 394 : 509908 : scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
395 : 509908 : prompt_status = prompt_tmp;
396 : :
6179 397 [ + - - + ]: 509908 : if (PQExpBufferBroken(query_buf))
1298 tgl@sss.pgh.pa.us 398 :UBC 0 : pg_fatal("out of memory");
399 : :
400 : : /*
401 : : * Increase statement line number counter for each linebreak added
402 : : * to the query buffer by the last psql_scan() call. There only
403 : : * will be ones to add when navigating to a statement in
404 : : * readline's history containing newlines.
405 : : */
4073 andres@anarazel.de 406 :CBC 509908 : tmp_line = query_buf->data + pos_in_query;
407 [ + + ]: 12719325 : while (*tmp_line != '\0')
408 : : {
409 [ + + ]: 12209417 : if (*(tmp_line++) == '\n')
410 : 90 : pset.stmt_lineno++;
411 : : }
412 : :
413 [ + + ]: 509908 : if (scan_result == PSCAN_EOL)
414 : 57095 : pset.stmt_lineno++;
415 : :
416 : : /*
417 : : * Send command if semicolon found, or if end of line and we're in
418 : : * single-line mode.
419 : : */
7921 tgl@sss.pgh.pa.us 420 [ + + + + ]: 509908 : if (scan_result == PSCAN_SEMICOLON ||
6999 421 [ - + ]: 57095 : (scan_result == PSCAN_EOL && pset.singleline))
422 : : {
423 : : /*
424 : : * Save line in history. We use history_buf to accumulate
425 : : * multi-line queries into a single history entry. Note that
426 : : * history accumulation works on input lines, so it doesn't
427 : : * matter whether the query will be ignored due to \if.
428 : : */
7078 429 [ + + + - ]: 167099 : if (pset.cur_cmd_interactive && !line_saved_in_history)
430 : : {
431 : 8 : pg_append_history(line, history_buf);
432 : 8 : pg_send_history(history_buf);
433 : 8 : line_saved_in_history = true;
434 : : }
435 : :
436 : : /* execute query unless we're in an inactive \if branch */
3133 437 [ + + ]: 334192 : if (conditional_active(cond_stack))
438 : : {
439 : 167090 : success = SendQuery(query_buf->data);
440 [ + + ]: 167084 : slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
441 : 167084 : pset.stmt_lineno = 1;
442 : :
443 : : /* transfer query to previous_buf by pointer-swapping */
444 : : {
445 : 167084 : PQExpBuffer swap_buf = previous_buf;
446 : :
447 : 167084 : previous_buf = query_buf;
448 : 167084 : query_buf = swap_buf;
449 : : }
450 : 167084 : resetPQExpBuffer(query_buf);
451 : :
452 : 167084 : added_nl_pos = -1;
453 : : /* we need not do psql_scan_reset() here */
454 : : }
455 : : else
456 : : {
457 : : /* if interactive, warn about non-executed query */
458 [ - + ]: 9 : if (pset.cur_cmd_interactive)
2401 peter@eisentraut.org 459 :UBC 0 : pg_log_error("query ignored; use \\endif or Ctrl-C to exit current \\if block");
460 : : /* fake an OK result for purposes of loop checks */
3133 tgl@sss.pgh.pa.us 461 :CBC 9 : success = true;
462 : 9 : slashCmdStatus = PSQL_CMD_SEND;
463 : 9 : pset.stmt_lineno = 1;
464 : : /* note that query_buf doesn't change state */
465 : : }
466 : : }
7921 467 [ + + ]: 342809 : else if (scan_result == PSCAN_BACKSLASH)
468 : : {
469 : : /* handle backslash command */
470 : :
471 : : /*
472 : : * If we added a newline to query_buf, and nothing else has
473 : : * been inserted in query_buf by the lexer, then strip off the
474 : : * newline again. This avoids any change to query_buf when a
475 : : * line contains only a backslash command. Also, in this
476 : : * situation we force out any previous lines as a separate
477 : : * history entry; we don't want SQL and backslash commands
478 : : * intermixed in history if at all possible.
479 : : */
480 [ + + ]: 9659 : if (query_buf->len == added_nl_pos)
481 : : {
482 : 79 : query_buf->data[--query_buf->len] = '\0';
7078 483 : 79 : pg_send_history(history_buf);
484 : : }
7921 485 : 9659 : added_nl_pos = -1;
486 : :
487 : : /* save backslash command in history */
7078 488 [ + + + + ]: 9659 : if (pset.cur_cmd_interactive && !line_saved_in_history)
489 : : {
490 : 51 : pg_append_history(line, history_buf);
491 : 51 : pg_send_history(history_buf);
492 : 51 : line_saved_in_history = true;
493 : : }
494 : :
495 : : /* execute backslash command */
7921 496 : 9659 : slashCmdStatus = HandleSlashCmds(scan_state,
497 : : cond_stack,
498 : : query_buf,
499 : : previous_buf);
500 : :
7253 peter_e@gmx.net 501 : 9659 : success = slashCmdStatus != PSQL_CMD_ERROR;
502 : :
503 : : /*
504 : : * Resetting stmt_lineno after a backslash command isn't
505 : : * always appropriate, but it's what we've done historically
506 : : * and there have been few complaints.
507 : : */
3133 tgl@sss.pgh.pa.us 508 : 9659 : pset.stmt_lineno = 1;
509 : :
7253 peter_e@gmx.net 510 [ + + ]: 9659 : if (slashCmdStatus == PSQL_CMD_SEND)
511 : : {
512 : : /* should not see this in inactive branch */
3133 tgl@sss.pgh.pa.us 513 [ - + ]: 1468 : Assert(conditional_active(cond_stack));
514 : :
9418 peter_e@gmx.net 515 : 1468 : success = SendQuery(query_buf->data);
516 : :
517 : : /* transfer query to previous_buf by pointer-swapping */
518 : : {
6179 tgl@sss.pgh.pa.us 519 : 1464 : PQExpBuffer swap_buf = previous_buf;
520 : :
521 : 1464 : previous_buf = query_buf;
522 : 1464 : query_buf = swap_buf;
523 : : }
9329 bruce@momjian.us 524 : 1464 : resetPQExpBuffer(query_buf);
525 : :
526 : : /* flush any paren nesting info after forced send */
7921 tgl@sss.pgh.pa.us 527 : 1464 : psql_scan_reset(scan_state);
528 : : }
7078 529 [ - + ]: 8191 : else if (slashCmdStatus == PSQL_CMD_NEWEDIT)
530 : : {
531 : : /* should not see this in inactive branch */
3133 tgl@sss.pgh.pa.us 532 [ # # ]:UBC 0 : Assert(conditional_active(cond_stack));
533 : : /* ensure what came back from editing ends in a newline */
2166 534 [ # # ]: 0 : if (query_buf->len > 0 &&
535 [ # # ]: 0 : query_buf->data[query_buf->len - 1] != '\n')
536 : 0 : appendPQExpBufferChar(query_buf, '\n');
537 : : /* rescan query_buf as new input */
7078 538 : 0 : psql_scan_finish(scan_state);
539 : 0 : free(line);
540 : 0 : line = pg_strdup(query_buf->data);
541 : 0 : resetPQExpBuffer(query_buf);
542 : : /* reset parsing state since we are rescanning whole line */
543 : 0 : psql_scan_reset(scan_state);
3510 544 : 0 : psql_scan_setup(scan_state, line, strlen(line),
545 : 0 : pset.encoding, standard_strings());
7078 546 : 0 : line_saved_in_history = false;
547 : 0 : prompt_status = PROMPT_READY;
548 : : /* we'll want to redisplay after parsing what we have */
2166 549 : 0 : need_redisplay = true;
550 : : }
7078 tgl@sss.pgh.pa.us 551 [ + + ]:CBC 8191 : else if (slashCmdStatus == PSQL_CMD_TERMINATE)
552 : 333238 : break;
553 : : }
554 : :
555 : : /* fall out of loop if lexer reached EOL */
556 [ + + + + ]: 509810 : if (scan_result == PSCAN_INCOMPLETE ||
557 : : scan_result == PSCAN_EOL)
558 : : break;
559 : : }
560 : :
561 : : /*
562 : : * Add line to pending history if we didn't do so already. Then, if
563 : : * the query buffer is still empty, flush out any unsent history
564 : : * entry. This means that empty lines (containing only whitespace and
565 : : * perhaps a dash-dash comment) that precede a query will be recorded
566 : : * as separate history entries, not as part of that query.
567 : : */
1426 568 [ + + ]: 333284 : if (pset.cur_cmd_interactive)
569 : : {
570 [ + + ]: 61 : if (!line_saved_in_history)
571 : 2 : pg_append_history(line, history_buf);
572 [ + + ]: 61 : if (query_buf->len == 0)
573 : 59 : pg_send_history(history_buf);
574 : : }
575 : :
7921 576 : 333284 : psql_scan_finish(scan_state);
9489 bruce@momjian.us 577 : 333284 : free(line);
578 : :
7253 peter_e@gmx.net 579 [ + + ]: 333284 : if (slashCmdStatus == PSQL_CMD_TERMINATE)
580 : : {
7921 tgl@sss.pgh.pa.us 581 : 88 : successResult = EXIT_SUCCESS;
582 : 88 : break;
583 : : }
584 : :
8257 bruce@momjian.us 585 [ + + ]: 333196 : if (!pset.cur_cmd_interactive)
586 : : {
587 [ + + + + ]: 333138 : if (!success && die_on_error)
8120 588 : 46 : successResult = EXIT_USER;
589 : : /* Have we lost the db connection? */
8257 590 [ - + ]: 333092 : else if (!pset.db)
8120 bruce@momjian.us 591 :UBC 0 : successResult = EXIT_BADCONN;
592 : : }
593 : : } /* while !endoffile/session */
594 : :
595 : : /*
596 : : * If we have a non-semicolon-terminated query at the end of file, we
597 : : * process it unless the input source is interactive --- in that case it
598 : : * seems better to go ahead and quit. Also skip if this is an error exit.
599 : : */
8984 tgl@sss.pgh.pa.us 600 [ + + + - ]:CBC 8033 : if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
601 [ + - ]: 4839 : successResult == EXIT_SUCCESS)
602 : : {
603 : : /* save query in history */
604 : : /* currently unneeded since we don't use this block if interactive */
605 : : #ifdef NOT_USED
606 : : if (pset.cur_cmd_interactive)
607 : : pg_send_history(history_buf);
608 : : #endif
609 : :
610 : : /* execute query unless we're in an inactive \if branch */
3133 611 [ + - ]: 4839 : if (conditional_active(cond_stack))
612 : : {
613 : 4839 : success = SendQuery(query_buf->data);
614 : : }
615 : : else
616 : : {
3133 tgl@sss.pgh.pa.us 617 [ # # ]:UBC 0 : if (pset.cur_cmd_interactive)
2401 peter@eisentraut.org 618 : 0 : pg_log_error("query ignored; use \\endif or Ctrl-C to exit current \\if block");
3133 tgl@sss.pgh.pa.us 619 : 0 : success = true;
620 : : }
621 : :
9329 bruce@momjian.us 622 [ + + + - ]:CBC 4838 : if (!success && die_on_error)
623 : 45 : successResult = EXIT_USER;
624 [ - + ]: 4793 : else if (pset.db == NULL)
9329 bruce@momjian.us 625 :UBC 0 : successResult = EXIT_BADCONN;
626 : : }
627 : :
628 : : /*
629 : : * Check for unbalanced \if-\endifs unless user explicitly quit, or the
630 : : * script is erroring out
631 : : */
3133 tgl@sss.pgh.pa.us 632 [ + + ]:CBC 8032 : if (slashCmdStatus != PSQL_CMD_TERMINATE &&
633 [ + + ]: 7944 : successResult != EXIT_USER &&
634 [ - + ]: 7853 : !conditional_stack_empty(cond_stack))
635 : : {
2401 peter@eisentraut.org 636 :UBC 0 : pg_log_error("reached EOF without finding closing \\endif(s)");
3133 tgl@sss.pgh.pa.us 637 [ # # # # ]: 0 : if (die_on_error && !pset.cur_cmd_interactive)
638 : 0 : successResult = EXIT_USER;
639 : : }
640 : :
641 : : /*
642 : : * Let's just make real sure the SIGINT handler won't try to use
643 : : * sigint_interrupt_jmp after we exit this routine. If there is an outer
644 : : * MainLoop instance, it will reset sigint_interrupt_jmp to point to
645 : : * itself at the top of its loop, before any further interactive input
646 : : * happens.
647 : : */
7075 tgl@sss.pgh.pa.us 648 :CBC 8032 : sigint_interrupt_enabled = false;
649 : :
9489 bruce@momjian.us 650 : 8032 : destroyPQExpBuffer(query_buf);
9453 651 : 8032 : destroyPQExpBuffer(previous_buf);
7198 652 : 8032 : destroyPQExpBuffer(history_buf);
653 : :
7921 tgl@sss.pgh.pa.us 654 : 8032 : psql_scan_destroy(scan_state);
3133 655 : 8032 : conditional_stack_destroy(cond_stack);
656 : :
9418 peter_e@gmx.net 657 : 8032 : pset.cur_cmd_source = prev_cmd_source;
658 : 8032 : pset.cur_cmd_interactive = prev_cmd_interactive;
9329 bruce@momjian.us 659 : 8032 : pset.lineno = prev_lineno;
660 : :
9489 661 : 8032 : return successResult;
662 : : } /* MainLoop() */
|