~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/bin/psql/mainloop.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psql - the PostgreSQL interactive terminal
 
3
 *
 
4
 * Copyright (c) 2000-2005, PostgreSQL Global Development Group
 
5
 *
 
6
 * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.66 2005-01-01 05:43:08 momjian Exp $
 
7
 */
 
8
#include "postgres_fe.h"
 
9
#include "mainloop.h"
 
10
 
 
11
#include "pqexpbuffer.h"
 
12
 
 
13
#include "command.h"
 
14
#include "common.h"
 
15
#include "input.h"
 
16
#include "prompt.h"
 
17
#include "psqlscan.h"
 
18
#include "settings.h"
 
19
 
 
20
#ifndef WIN32
 
21
#include <setjmp.h>
 
22
sigjmp_buf      main_loop_jmp;
 
23
#endif
 
24
 
 
25
 
 
26
/*
 
27
 * Main processing loop for reading lines of input
 
28
 *      and sending them to the backend.
 
29
 *
 
30
 * This loop is re-entrant. May be called by \i command
 
31
 *      which reads input from a file.
 
32
 */
 
33
int
 
34
MainLoop(FILE *source)
 
35
{
 
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 */
 
41
        int                     added_nl_pos;
 
42
        bool            success;
 
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;
 
48
 
 
49
        /* Save the prior command source */
 
50
        FILE       *prev_cmd_source;
 
51
        bool            prev_cmd_interactive;
 
52
        unsigned int prev_lineno;
 
53
 
 
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;
 
58
 
 
59
        /* Establish new source */
 
60
        pset.cur_cmd_source = source;
 
61
        pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
 
62
        pset.lineno = 0;
 
63
 
 
64
        /* Create working state */
 
65
        scan_state = psql_scan_create();
 
66
 
 
67
        query_buf = createPQExpBuffer();
 
68
        previous_buf = createPQExpBuffer();
 
69
        if (!query_buf || !previous_buf)
 
70
        {
 
71
                psql_error("out of memory\n");
 
72
                exit(EXIT_FAILURE);
 
73
        }
 
74
 
 
75
        /* main loop to get queries and execute them */
 
76
        while (successResult == EXIT_SUCCESS)
 
77
        {
 
78
                /*
 
79
                 * Welcome code for Control-C
 
80
                 */
 
81
                if (cancel_pressed)
 
82
                {
 
83
                        if (!pset.cur_cmd_interactive)
 
84
                        {
 
85
                                /*
 
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.
 
89
                                 */
 
90
                                successResult = EXIT_USER;
 
91
                                break;
 
92
                        }
 
93
 
 
94
                        cancel_pressed = false;
 
95
                }
 
96
 
 
97
#ifndef WIN32
 
98
                if (sigsetjmp(main_loop_jmp, 1) != 0)
 
99
                {
 
100
                        /* got here with longjmp */
 
101
 
 
102
                        /* reset parsing state */
 
103
                        resetPQExpBuffer(query_buf);
 
104
                        psql_scan_finish(scan_state);
 
105
                        psql_scan_reset(scan_state);
 
106
                        count_eof = 0;
 
107
                        slashCmdStatus = CMD_UNKNOWN;
 
108
                        prompt_status = PROMPT_READY;
 
109
 
 
110
                        if (pset.cur_cmd_interactive)
 
111
                                putc('\n', stdout);
 
112
                        else
 
113
                        {
 
114
                                successResult = EXIT_USER;
 
115
                                break;
 
116
                        }
 
117
                }
 
118
 
 
119
                /*
 
120
                 * establish the control-C handler only after main_loop_jmp is
 
121
                 * ready
 
122
                 */
 
123
                pqsignal(SIGINT, handle_sigint);                /* control-C => cancel */
 
124
 
 
125
#else /* WIN32 */
 
126
                setup_cancel_handler();
 
127
#endif
 
128
 
 
129
                fflush(stdout);
 
130
 
 
131
                if (slashCmdStatus == CMD_NEWEDIT)
 
132
                {
 
133
                        /*
 
134
                         * just returned from editing the line? then just copy to the
 
135
                         * input buffer
 
136
                         */
 
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;
 
143
                }
 
144
 
 
145
                /*
 
146
                 * otherwise, get another line
 
147
                 */
 
148
                else if (pset.cur_cmd_interactive)
 
149
                {
 
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));
 
154
                }
 
155
                else
 
156
                        line = gets_fromFile(source);
 
157
 
 
158
                /*
 
159
                 * query_buf holds query already accumulated.  line is the
 
160
                 * malloc'd new line of input (note it must be freed before
 
161
                 * looping around!)
 
162
                 */
 
163
 
 
164
                /* No more input.  Time to quit, or \i done */
 
165
                if (line == NULL)
 
166
                {
 
167
                        if (pset.cur_cmd_interactive)
 
168
                        {
 
169
                                /* This tries to mimic bash's IGNOREEOF feature. */
 
170
                                count_eof++;
 
171
 
 
172
                                if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false))
 
173
                                {
 
174
                                        if (!QUIET())
 
175
                                                printf(gettext("Use \"\\q\" to leave %s.\n"), pset.progname);
 
176
                                        continue;
 
177
                                }
 
178
 
 
179
                                puts(QUIET() ? "" : "\\q");
 
180
                        }
 
181
                        break;
 
182
                }
 
183
 
 
184
                count_eof = 0;
 
185
 
 
186
                pset.lineno++;
 
187
 
 
188
                /* nothing left on line? then ignore */
 
189
                if (line[0] == '\0' && !psql_scan_in_quote(scan_state))
 
190
                {
 
191
                        free(line);
 
192
                        continue;
 
193
                }
 
194
 
 
195
                /* echo back if flag is set */
 
196
                if (!pset.cur_cmd_interactive &&
 
197
                        VariableEquals(pset.vars, "ECHO", "all"))
 
198
                        puts(line);
 
199
                fflush(stdout);
 
200
 
 
201
                /* insert newlines into query buffer between source lines */
 
202
                if (query_buf->len > 0)
 
203
                {
 
204
                        appendPQExpBufferChar(query_buf, '\n');
 
205
                        added_nl_pos = query_buf->len;
 
206
                }
 
207
                else
 
208
                        added_nl_pos = -1;      /* flag we didn't add one */
 
209
 
 
210
                /* Setting this will not have effect until next line. */
 
211
                die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP");
 
212
 
 
213
                /*
 
214
                 * Parse line, looking for command separators.
 
215
                 */
 
216
                psql_scan_setup(scan_state, line, strlen(line));
 
217
                success = true;
 
218
 
 
219
                while (success || !die_on_error)
 
220
                {
 
221
                        PsqlScanResult scan_result;
 
222
                        promptStatus_t prompt_tmp = prompt_status;
 
223
 
 
224
                        scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
 
225
                        prompt_status = prompt_tmp;
 
226
 
 
227
                        /*
 
228
                         * Send command if semicolon found, or if end of line and
 
229
                         * we're in single-line mode.
 
230
                         */
 
231
                        if (scan_result == PSCAN_SEMICOLON ||
 
232
                                (scan_result == PSCAN_EOL &&
 
233
                                 GetVariableBool(pset.vars, "SINGLELINE")))
 
234
                        {
 
235
                                /* execute query */
 
236
                                success = SendQuery(query_buf->data);
 
237
                                slashCmdStatus = success ? CMD_SEND : CMD_ERROR;
 
238
 
 
239
                                resetPQExpBuffer(previous_buf);
 
240
                                appendPQExpBufferStr(previous_buf, query_buf->data);
 
241
                                resetPQExpBuffer(query_buf);
 
242
                                added_nl_pos = -1;
 
243
                                /* we need not do psql_scan_reset() here */
 
244
                        }
 
245
                        else if (scan_result == PSCAN_BACKSLASH)
 
246
                        {
 
247
                                /* handle backslash command */
 
248
 
 
249
                                /*
 
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
 
254
                                 * command.
 
255
                                 */
 
256
                                if (query_buf->len == added_nl_pos)
 
257
                                        query_buf->data[--query_buf->len] = '\0';
 
258
                                added_nl_pos = -1;
 
259
 
 
260
                                slashCmdStatus = HandleSlashCmds(scan_state,
 
261
                                                                                                 query_buf->len > 0 ?
 
262
                                                                                           query_buf : previous_buf);
 
263
 
 
264
                                success = slashCmdStatus != CMD_ERROR;
 
265
 
 
266
                                if ((slashCmdStatus == CMD_SEND || slashCmdStatus == CMD_NEWEDIT) &&
 
267
                                        query_buf->len == 0)
 
268
                                {
 
269
                                        /* copy previous buffer to current for handling */
 
270
                                        appendPQExpBufferStr(query_buf, previous_buf->data);
 
271
                                }
 
272
 
 
273
                                if (slashCmdStatus == CMD_SEND)
 
274
                                {
 
275
                                        success = SendQuery(query_buf->data);
 
276
 
 
277
                                        resetPQExpBuffer(previous_buf);
 
278
                                        appendPQExpBufferStr(previous_buf, query_buf->data);
 
279
                                        resetPQExpBuffer(query_buf);
 
280
 
 
281
                                        /* flush any paren nesting info after forced send */
 
282
                                        psql_scan_reset(scan_state);
 
283
                                }
 
284
 
 
285
                                if (slashCmdStatus == CMD_TERMINATE)
 
286
                                        break;
 
287
                        }
 
288
 
 
289
                        /* fall out of loop if lexer reached EOL */
 
290
                        if (scan_result == PSCAN_INCOMPLETE ||
 
291
                                scan_result == PSCAN_EOL)
 
292
                                break;
 
293
                }
 
294
 
 
295
                psql_scan_finish(scan_state);
 
296
                free(line);
 
297
 
 
298
                if (slashCmdStatus == CMD_TERMINATE)
 
299
                {
 
300
                        successResult = EXIT_SUCCESS;
 
301
                        break;
 
302
                }
 
303
 
 
304
                if (!pset.cur_cmd_interactive)
 
305
                {
 
306
                        if (!success && die_on_error)
 
307
                                successResult = EXIT_USER;
 
308
                        /* Have we lost the db connection? */
 
309
                        else if (!pset.db)
 
310
                                successResult = EXIT_BADCONN;
 
311
                }
 
312
        }                                                       /* while !endoffile/session */
 
313
 
 
314
        /*
 
315
         * Process query at the end of file without a semicolon
 
316
         */
 
317
        if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
 
318
                successResult == EXIT_SUCCESS)
 
319
        {
 
320
                success = SendQuery(query_buf->data);
 
321
 
 
322
                if (!success && die_on_error)
 
323
                        successResult = EXIT_USER;
 
324
                else if (pset.db == NULL)
 
325
                        successResult = EXIT_BADCONN;
 
326
        }
 
327
 
 
328
        /*
 
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.
 
333
         */
 
334
#ifndef WIN32
 
335
        pqsignal(SIGINT, SIG_DFL);
 
336
#endif
 
337
 
 
338
        destroyPQExpBuffer(query_buf);
 
339
        destroyPQExpBuffer(previous_buf);
 
340
 
 
341
        psql_scan_destroy(scan_state);
 
342
 
 
343
        pset.cur_cmd_source = prev_cmd_source;
 
344
        pset.cur_cmd_interactive = prev_cmd_interactive;
 
345
        pset.lineno = prev_lineno;
 
346
 
 
347
        return successResult;
 
348
}       /* MainLoop() */