2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
6 * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.66 2005/01/01 05:43:08 momjian Exp $
8 #include "postgres_fe.h"
11 #include "pqexpbuffer.h"
22 sigjmp_buf main_loop_jmp;
27 * Main processing loop for reading lines of input
28 * and sending them to the backend.
30 * This loop is re-entrant. May be called by \i command
31 * which reads input from a file.
34 MainLoop(FILE *source)
36 PsqlScanState scan_state; /* lexer working state */
37 PQExpBuffer query_buf; /* buffer for query being accumulated */
38 PQExpBuffer previous_buf; /* if there isn't anything in the new
39 * buffer yet, use this one for \e, etc. */
40 char *line; /* current line of input */
43 volatile int successResult = EXIT_SUCCESS;
44 volatile backslashResult slashCmdStatus = CMD_UNKNOWN;
45 volatile promptStatus_t prompt_status = PROMPT_READY;
46 volatile int count_eof = 0;
47 volatile bool die_on_error = false;
49 /* Save the prior command source */
50 FILE *prev_cmd_source;
51 bool prev_cmd_interactive;
52 unsigned int prev_lineno;
54 /* Save old settings */
55 prev_cmd_source = pset.cur_cmd_source;
56 prev_cmd_interactive = pset.cur_cmd_interactive;
57 prev_lineno = pset.lineno;
59 /* Establish new source */
60 pset.cur_cmd_source = source;
61 pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
64 /* Create working state */
65 scan_state = psql_scan_create();
67 query_buf = createPQExpBuffer();
68 previous_buf = createPQExpBuffer();
69 if (!query_buf || !previous_buf)
71 psql_error("out of memory\n");
75 /* main loop to get queries and execute them */
76 while (successResult == EXIT_SUCCESS)
79 * Welcome code for Control-C
83 if (!pset.cur_cmd_interactive)
86 * You get here if you stopped a script with Ctrl-C and a
87 * query cancel was issued. In that case we don't do the
88 * longjmp, so the query routine can finish nicely.
90 successResult = EXIT_USER;
94 cancel_pressed = false;
98 if (sigsetjmp(main_loop_jmp, 1) != 0)
100 /* got here with longjmp */
102 /* reset parsing state */
103 resetPQExpBuffer(query_buf);
104 psql_scan_finish(scan_state);
105 psql_scan_reset(scan_state);
107 slashCmdStatus = CMD_UNKNOWN;
108 prompt_status = PROMPT_READY;
110 if (pset.cur_cmd_interactive)
114 successResult = EXIT_USER;
120 * establish the control-C handler only after main_loop_jmp is
123 pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
126 setup_cancel_handler();
131 if (slashCmdStatus == CMD_NEWEDIT)
134 * just returned from editing the line? then just copy to the
137 line = pg_strdup(query_buf->data);
138 /* reset parsing state since we are rescanning whole line */
139 resetPQExpBuffer(query_buf);
140 psql_scan_reset(scan_state);
141 slashCmdStatus = CMD_UNKNOWN;
142 prompt_status = PROMPT_READY;
146 * otherwise, get another line
148 else if (pset.cur_cmd_interactive)
150 /* May need to reset prompt, eg after \r command */
151 if (query_buf->len == 0)
152 prompt_status = PROMPT_READY;
153 line = gets_interactive(get_prompt(prompt_status));
156 line = gets_fromFile(source);
159 * query_buf holds query already accumulated. line is the
160 * malloc'd new line of input (note it must be freed before
164 /* No more input. Time to quit, or \i done */
167 if (pset.cur_cmd_interactive)
169 /* This tries to mimic bash's IGNOREEOF feature. */
172 if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false))
175 printf(gettext("Use \"\\q\" to leave %s.\n"), pset.progname);
179 puts(QUIET() ? "" : "\\q");
188 /* nothing left on line? then ignore */
189 if (line[0] == '\0' && !psql_scan_in_quote(scan_state))
195 /* echo back if flag is set */
196 if (!pset.cur_cmd_interactive &&
197 VariableEquals(pset.vars, "ECHO", "all"))
201 /* insert newlines into query buffer between source lines */
202 if (query_buf->len > 0)
204 appendPQExpBufferChar(query_buf, '\n');
205 added_nl_pos = query_buf->len;
208 added_nl_pos = -1; /* flag we didn't add one */
210 /* Setting this will not have effect until next line. */
211 die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP");
214 * Parse line, looking for command separators.
216 psql_scan_setup(scan_state, line, strlen(line));
219 while (success || !die_on_error)
221 PsqlScanResult scan_result;
222 promptStatus_t prompt_tmp = prompt_status;
224 scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
225 prompt_status = prompt_tmp;
228 * Send command if semicolon found, or if end of line and
229 * we're in single-line mode.
231 if (scan_result == PSCAN_SEMICOLON ||
232 (scan_result == PSCAN_EOL &&
233 GetVariableBool(pset.vars, "SINGLELINE")))
236 success = SendQuery(query_buf->data);
237 slashCmdStatus = success ? CMD_SEND : CMD_ERROR;
239 resetPQExpBuffer(previous_buf);
240 appendPQExpBufferStr(previous_buf, query_buf->data);
241 resetPQExpBuffer(query_buf);
243 /* we need not do psql_scan_reset() here */
245 else if (scan_result == PSCAN_BACKSLASH)
247 /* handle backslash command */
250 * If we added a newline to query_buf, and nothing else
251 * has been inserted in query_buf by the lexer, then strip
252 * off the newline again. This avoids any change to
253 * query_buf when a line contains only a backslash
256 if (query_buf->len == added_nl_pos)
257 query_buf->data[--query_buf->len] = '\0';
260 slashCmdStatus = HandleSlashCmds(scan_state,
262 query_buf : previous_buf);
264 success = slashCmdStatus != CMD_ERROR;
266 if ((slashCmdStatus == CMD_SEND || slashCmdStatus == CMD_NEWEDIT) &&
269 /* copy previous buffer to current for handling */
270 appendPQExpBufferStr(query_buf, previous_buf->data);
273 if (slashCmdStatus == CMD_SEND)
275 success = SendQuery(query_buf->data);
277 resetPQExpBuffer(previous_buf);
278 appendPQExpBufferStr(previous_buf, query_buf->data);
279 resetPQExpBuffer(query_buf);
281 /* flush any paren nesting info after forced send */
282 psql_scan_reset(scan_state);
285 if (slashCmdStatus == CMD_TERMINATE)
289 /* fall out of loop if lexer reached EOL */
290 if (scan_result == PSCAN_INCOMPLETE ||
291 scan_result == PSCAN_EOL)
295 psql_scan_finish(scan_state);
298 if (slashCmdStatus == CMD_TERMINATE)
300 successResult = EXIT_SUCCESS;
304 if (!pset.cur_cmd_interactive)
306 if (!success && die_on_error)
307 successResult = EXIT_USER;
308 /* Have we lost the db connection? */
310 successResult = EXIT_BADCONN;
312 } /* while !endoffile/session */
315 * Process query at the end of file without a semicolon
317 if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
318 successResult == EXIT_SUCCESS)
320 success = SendQuery(query_buf->data);
322 if (!success && die_on_error)
323 successResult = EXIT_USER;
324 else if (pset.db == NULL)
325 successResult = EXIT_BADCONN;
329 * Reset SIGINT handler because main_loop_jmp will be invalid as soon
330 * as we exit this routine. If there is an outer MainLoop instance,
331 * it will re-enable ^C catching as soon as it gets back to the top of
332 * its loop and resets main_loop_jmp to point to itself.
335 pqsignal(SIGINT, SIG_DFL);
338 destroyPQExpBuffer(query_buf);
339 destroyPQExpBuffer(previous_buf);
341 psql_scan_destroy(scan_state);
343 pset.cur_cmd_source = prev_cmd_source;
344 pset.cur_cmd_interactive = prev_cmd_interactive;
345 pset.lineno = prev_lineno;
347 return successResult;