summaryrefslogtreecommitdiff
path: root/src/bin/psql/mainloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/psql/mainloop.c')
-rw-r--r--src/bin/psql/mainloop.c350
1 files changed, 86 insertions, 264 deletions
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index b02ffe2a8cc..71531814255 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -3,18 +3,19 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.61 2004/01/25 03:07:22 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.62 2004/02/19 19:40:09 tgl Exp $
*/
#include "postgres_fe.h"
#include "mainloop.h"
#include "pqexpbuffer.h"
-#include "settings.h"
-#include "prompt.h"
-#include "input.h"
-#include "common.h"
#include "command.h"
+#include "common.h"
+#include "input.h"
+#include "prompt.h"
+#include "psqlscan.h"
+#include "settings.h"
#ifndef WIN32
#include <setjmp.h>
@@ -28,48 +29,39 @@ sigjmp_buf main_loop_jmp;
*
* This loop is re-entrant. May be called by \i command
* which reads input from a file.
- *
- * FIXME: rewrite this whole thing with flex
*/
int
MainLoop(FILE *source)
{
+ PsqlScanState scan_state; /* lexer working state */
PQExpBuffer query_buf; /* buffer for query being accumulated */
PQExpBuffer previous_buf; /* if there isn't anything in the new
* buffer yet, use this one for \e, etc. */
char *line; /* current line of input */
- int len; /* length of the line */
+ int added_nl_pos;
+ bool success;
volatile int successResult = EXIT_SUCCESS;
volatile backslashResult slashCmdStatus = CMD_UNKNOWN;
-
- bool success;
- volatile char in_quote = 0; /* == 0 for no in_quote */
- volatile int in_xcomment = 0; /* in extended comment */
- volatile int paren_level = 0;
- unsigned int query_start;
+ volatile promptStatus_t prompt_status = PROMPT_READY;
volatile int count_eof = 0;
- volatile unsigned int bslash_count = 0;
-
- int i,
- prevlen,
- thislen;
-
+ volatile bool die_on_error = false;
/* Save the prior command source */
FILE *prev_cmd_source;
bool prev_cmd_interactive;
-
unsigned int prev_lineno;
- volatile bool die_on_error = false;
-
/* Save old settings */
prev_cmd_source = pset.cur_cmd_source;
prev_cmd_interactive = pset.cur_cmd_interactive;
+ prev_lineno = pset.lineno;
/* Establish new source */
pset.cur_cmd_source = source;
pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
+ pset.lineno = 0;
+ /* Create working state */
+ scan_state = psql_scan_create();
query_buf = createPQExpBuffer();
previous_buf = createPQExpBuffer();
@@ -79,10 +71,6 @@ MainLoop(FILE *source)
exit(EXIT_FAILURE);
}
- prev_lineno = pset.lineno;
- pset.lineno = 0;
-
-
/* main loop to get queries and execute them */
while (successResult == EXIT_SUCCESS)
{
@@ -110,17 +98,17 @@ MainLoop(FILE *source)
{
/* got here with longjmp */
+ /* reset parsing state */
+ resetPQExpBuffer(query_buf);
+ psql_scan_finish(scan_state);
+ psql_scan_reset(scan_state);
+ count_eof = 0;
+ slashCmdStatus = CMD_UNKNOWN;
+ prompt_status = PROMPT_READY;
+
if (pset.cur_cmd_interactive)
{
putc('\n', stdout);
- resetPQExpBuffer(query_buf);
-
- /* reset parsing state */
- in_xcomment = 0;
- in_quote = 0;
- paren_level = 0;
- count_eof = 0;
- slashCmdStatus = CMD_UNKNOWN;
}
else
{
@@ -145,48 +133,30 @@ MainLoop(FILE *source)
* input buffer
*/
line = pg_strdup(query_buf->data);
- resetPQExpBuffer(query_buf);
/* reset parsing state since we are rescanning whole line */
- in_xcomment = 0;
- in_quote = 0;
- paren_level = 0;
+ resetPQExpBuffer(query_buf);
+ psql_scan_reset(scan_state);
slashCmdStatus = CMD_UNKNOWN;
+ prompt_status = PROMPT_READY;
}
/*
- * otherwise, set interactive prompt if necessary and get another
- * line
+ * otherwise, get another line
*/
else if (pset.cur_cmd_interactive)
{
- int prompt_status;
-
- if (in_quote && in_quote == '\'')
- prompt_status = PROMPT_SINGLEQUOTE;
- else if (in_quote && in_quote == '"')
- prompt_status = PROMPT_DOUBLEQUOTE;
- else if (in_xcomment)
- prompt_status = PROMPT_COMMENT;
- else if (paren_level)
- prompt_status = PROMPT_PAREN;
- else if (query_buf->len > 0)
- prompt_status = PROMPT_CONTINUE;
- else
+ /* May need to reset prompt, eg after \r command */
+ if (query_buf->len == 0)
prompt_status = PROMPT_READY;
-
line = gets_interactive(get_prompt(prompt_status));
}
else
line = gets_fromFile(source);
- /* Setting this will not have effect until next line. */
- die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP");
-
/*
* query_buf holds query already accumulated. line is the
* malloc'd new line of input (note it must be freed before
- * looping around!) query_start is the next command start location
- * within the line.
+ * looping around!)
*/
/* No more input. Time to quit, or \i done */
@@ -214,165 +184,52 @@ MainLoop(FILE *source)
pset.lineno++;
/* nothing left on line? then ignore */
- if (line[0] == '\0' && !in_quote)
+ if (line[0] == '\0' && !psql_scan_in_quote(scan_state))
{
free(line);
continue;
}
/* echo back if flag is set */
- if (!pset.cur_cmd_interactive && VariableEquals(pset.vars, "ECHO", "all"))
+ if (!pset.cur_cmd_interactive &&
+ VariableEquals(pset.vars, "ECHO", "all"))
puts(line);
fflush(stdout);
- len = strlen(line);
- query_start = 0;
+ /* insert newlines into query buffer between source lines */
+ if (query_buf->len > 0)
+ {
+ appendPQExpBufferChar(query_buf, '\n');
+ added_nl_pos = query_buf->len;
+ }
+ else
+ added_nl_pos = -1; /* flag we didn't add one */
+
+ /* Setting this will not have effect until next line. */
+ die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP");
/*
* Parse line, looking for command separators.
- *
- * The current character is at line[i], the prior character at line[i
- * - prevlen], the next character at line[i + thislen].
*/
-#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i, pset.encoding))
-
+ psql_scan_setup(scan_state, line, strlen(line));
success = true;
- prevlen = 0;
- thislen = ((len > 0) ? PQmblen(line, pset.encoding) : 0);
- for (i = 0; (i < len) && (success || !die_on_error); ADVANCE_1)
+ while (success || !die_on_error)
{
- /* was the previous character a backslash? */
- if (i > 0 && line[i - prevlen] == '\\')
- bslash_count++;
- else
- bslash_count = 0;
+ PsqlScanResult scan_result;
+ promptStatus_t prompt_tmp = prompt_status;
- rescan:
+ scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
+ prompt_status = prompt_tmp;
/*
- * It is important to place the in_* test routines before the
- * in_* detection routines. i.e. we have to test if we are in
- * a quote before testing for comments. bjm 2000-06-30
+ * Send command if semicolon found, or if end of line and
+ * we're in single-line mode.
*/
-
- /* in quote? */
- if (in_quote)
- {
- /*
- * end of quote if matching non-backslashed character.
- * backslashes don't count for double quotes, though.
- */
- if (line[i] == in_quote &&
- (bslash_count % 2 == 0 || in_quote == '"'))
- in_quote = 0;
- }
-
- /* start of extended comment? */
- else if (line[i] == '/' && line[i + thislen] == '*')
- {
- in_xcomment++;
- if (in_xcomment == 1)
- ADVANCE_1;
- }
-
- /* in or end of extended comment? */
- else if (in_xcomment)
- {
- if (line[i] == '*' && line[i + thislen] == '/' &&
- !--in_xcomment)
- ADVANCE_1;
- }
-
- /* start of quote? */
- else if (line[i] == '\'' || line[i] == '"')
- in_quote = line[i];
-
- /* single-line comment? truncate line */
- else if (line[i] == '-' && line[i + thislen] == '-')
- {
- line[i] = '\0'; /* remove comment */
- break;
- }
-
- /* count nested parentheses */
- else if (line[i] == '(')
- paren_level++;
-
- else if (line[i] == ')' && paren_level > 0)
- paren_level--;
-
- /* colon -> substitute variable */
- /* we need to be on the watch for the '::' operator */
- else if (line[i] == ':' && !bslash_count
- && strspn(line + i + thislen, VALID_VARIABLE_CHARS) > 0
- && !(prevlen > 0 && line[i - prevlen] == ':')
- )
+ if (scan_result == PSCAN_SEMICOLON ||
+ (scan_result == PSCAN_EOL &&
+ GetVariableBool(pset.vars, "SINGLELINE")))
{
- size_t in_length,
- out_length;
- const char *value;
- char *new;
- char after; /* the character after the
- * variable name will be
- * temporarily overwritten */
-
- in_length = strspn(&line[i + thislen], VALID_VARIABLE_CHARS);
- /* mark off the possible variable name */
- after = line[i + thislen + in_length];
- line[i + thislen + in_length] = '\0';
-
- value = GetVariable(pset.vars, &line[i + thislen]);
-
- /* restore overwritten character */
- line[i + thislen + in_length] = after;
-
- if (value)
- {
- /* It is a variable, perform substitution */
- out_length = strlen(value);
-
- new = pg_malloc(len + out_length - in_length + 1);
- sprintf(new, "%.*s%s%s", i, line, value,
- &line[i + thislen + in_length]);
-
- free(line);
- line = new;
- len = strlen(new);
-
- if (i < len)
- {
- thislen = PQmblen(line + i, pset.encoding);
- goto rescan; /* reparse the just substituted */
- }
- }
- else
- {
- /*
- * if the variable doesn't exist we'll leave the
- * string as is ... move on ...
- */
- }
- }
-
- /* semicolon? then send query */
- else if (line[i] == ';' && !bslash_count && !paren_level)
- {
- line[i] = '\0';
- /* is there anything else on the line? */
- if (line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
- {
- /*
- * insert a cosmetic newline, if this is not the first
- * line in the buffer
- */
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- /* append the line to the query buffer */
- appendPQExpBufferStr(query_buf, line + query_start);
- appendPQExpBufferChar(query_buf, ';');
- }
-
/* execute query */
success = SendQuery(query_buf->data);
slashCmdStatus = success ? CMD_SEND : CMD_ERROR;
@@ -380,46 +237,26 @@ MainLoop(FILE *source)
resetPQExpBuffer(previous_buf);
appendPQExpBufferStr(previous_buf, query_buf->data);
resetPQExpBuffer(query_buf);
- query_start = i + thislen;
- }
-
- /*
- * if you have a burning need to send a semicolon or colon to
- * the backend ...
- */
- else if (bslash_count && (line[i] == ';' || line[i] == ':'))
- {
- /* remove the backslash */
- memmove(line + i - prevlen, line + i, len - i + 1);
- len--;
- i--;
+ added_nl_pos = -1;
+ /* we need not do psql_scan_reset() here */
}
-
- /* backslash command */
- else if (bslash_count)
+ else if (scan_result == PSCAN_BACKSLASH)
{
- const char *end_of_cmd = NULL;
-
- line[i - prevlen] = '\0'; /* overwrites backslash */
+ /* handle backslash command */
- /* is there anything else on the line for the command? */
- if (line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
- {
- /*
- * insert a cosmetic newline, if this is not the first
- * line in the buffer
- */
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- /* append the line to the query buffer */
- appendPQExpBufferStr(query_buf, line + query_start);
- }
+ /*
+ * If we added a newline to query_buf, and nothing else has
+ * been inserted in query_buf by the lexer, then strip off
+ * the newline again. This avoids any change to query_buf
+ * when a line contains only a backslash command.
+ */
+ if (query_buf->len == added_nl_pos)
+ query_buf->data[--query_buf->len] = '\0';
+ added_nl_pos = -1;
- /* handle backslash command */
- slashCmdStatus = HandleSlashCmds(&line[i],
- query_buf->len > 0 ? query_buf : previous_buf,
- &end_of_cmd,
- &paren_level);
+ slashCmdStatus = HandleSlashCmds(scan_state,
+ query_buf->len > 0 ?
+ query_buf : previous_buf);
success = slashCmdStatus != CMD_ERROR;
@@ -433,49 +270,32 @@ MainLoop(FILE *source)
if (slashCmdStatus == CMD_SEND)
{
success = SendQuery(query_buf->data);
- query_start = i + thislen;
resetPQExpBuffer(previous_buf);
appendPQExpBufferStr(previous_buf, query_buf->data);
resetPQExpBuffer(query_buf);
- }
- if (query_buf->len == 0 && previous_buf->len == 0)
- paren_level = 0;
+ /* flush any paren nesting info after forced send */
+ psql_scan_reset(scan_state);
+ }
- /* process anything left after the backslash command */
- i = end_of_cmd - line;
- query_start = i;
+ if (slashCmdStatus == CMD_TERMINATE)
+ break;
}
- } /* for (line) */
-
- if (slashCmdStatus == CMD_TERMINATE)
- {
- successResult = EXIT_SUCCESS;
- break;
- }
-
-
- /* Put the rest of the line in the query buffer. */
- if (in_quote || line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
- {
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- appendPQExpBufferStr(query_buf, line + query_start);
+ /* fall out of loop if lexer reached EOL */
+ if (scan_result == PSCAN_INCOMPLETE ||
+ scan_result == PSCAN_EOL)
+ break;
}
+ psql_scan_finish(scan_state);
free(line);
-
- /* In single line mode, send off the query if any */
- if (query_buf->data[0] != '\0' && GetVariableBool(pset.vars, "SINGLELINE"))
+ if (slashCmdStatus == CMD_TERMINATE)
{
- success = SendQuery(query_buf->data);
- slashCmdStatus = (success ? CMD_SEND : CMD_ERROR);
- resetPQExpBuffer(previous_buf);
- appendPQExpBufferStr(previous_buf, query_buf->data);
- resetPQExpBuffer(query_buf);
+ successResult = EXIT_SUCCESS;
+ break;
}
if (!pset.cur_cmd_interactive)
@@ -515,6 +335,8 @@ MainLoop(FILE *source)
destroyPQExpBuffer(query_buf);
destroyPQExpBuffer(previous_buf);
+ psql_scan_destroy(scan_state);
+
pset.cur_cmd_source = prev_cmd_source;
pset.cur_cmd_interactive = prev_cmd_interactive;
pset.lineno = prev_lineno;