Exemplo n.º 1
0
// runInteractive runs the SQL client interactively, presenting
// a prompt to the user for each statement.
func runInteractive(conn *sqlConn) (exitErr error) {
	fullPrompt, continuePrompt := preparePrompts(conn.url)

	if isatty.IsTerminal(os.Stdout.Fd()) {
		// We only enable history management when the terminal is actually
		// interactive. This saves on memory when e.g. piping a large SQL
		// script through the command-line client.
		userAcct, err := user.Current()
		if err != nil {
			if log.V(2) {
				log.Warningf("cannot retrieve user information: %s", err)
				log.Info("cannot load or save the command-line history")
			}
		} else {
			histFile := filepath.Join(userAcct.HomeDir, ".cockroachdb_history")
			readline.SetHistoryPath(histFile)
		}
	}

	fmt.Print(infoMessage)

	var stmt []string

	for {
		thisPrompt := fullPrompt
		if len(stmt) > 0 {
			thisPrompt = continuePrompt
		}
		l, err := readline.Line(thisPrompt)

		if err == readline.ErrInterrupt || err == io.EOF {
			break
		} else if err != nil {
			fmt.Fprintf(osStderr, "input error: %s\n", err)
			return err
		}

		tl := strings.TrimSpace(l)

		// Check if this is a request for help or a client-side command.
		// If so, process it directly and skip query processing below.
		status := handleInputLine(&stmt, tl)
		if status == cliNextLine {
			continue
		} else if status == cliExit {
			break
		}

		// We join the statements back together with newlines in case
		// there is a significant newline inside a string literal. However
		// we join with spaces for keeping history, because otherwise a
		// history recall will only pull one line from a multi-line
		// statement.
		fullStmt := strings.Join(stmt, "\n")

		// We save the history between each statement, This enables
		// reusing history in another SQL shell without closing the
		// current shell.
		//
		// AddHistory will push command into memory and try to persist
		// to disk (if readline.SetHistoryPath was called).
		// err can be not nil only if it got a IO error while
		// trying to persist.
		if err := readline.AddHistory(strings.Join(stmt, " ")); err != nil {
			log.Warningf("cannot save command-line history: %s", err)
			log.Info("command-line history will not be saved in this session")
			readline.SetHistoryPath("")
		}

		if exitErr = runPrettyQuery(conn, os.Stdout, makeQuery(fullStmt)); exitErr != nil {
			fmt.Fprintln(osStderr, exitErr)
		}

		// Clear the saved statement.
		stmt = stmt[:0]
	}

	return exitErr
}
Exemplo n.º 2
0
// runInteractive runs the SQL client interactively, presenting
// a prompt to the user for each statement.
func runInteractive(conn *sqlConn) (exitErr error) {
	fullPrompt, continuePrompt := preparePrompts(conn.url)

	isInteractive := isatty.IsTerminal(os.Stdout.Fd()) &&
		isatty.IsTerminal(os.Stdin.Fd())

	if isInteractive {
		// We only enable history management when the terminal is actually
		// interactive. This saves on memory when e.g. piping a large SQL
		// script through the command-line client.
		userAcct, err := user.Current()
		if err != nil {
			if log.V(2) {
				log.Warningf("cannot retrieve user information: %s", err)
				log.Info("cannot load or save the command-line history")
			}
		} else {
			histFile := filepath.Join(userAcct.HomeDir, ".cockroachdb_history")
			readline.SetHistoryPath(histFile)
		}
	}

	if isInteractive {
		fmt.Print(infoMessage)
	}

	var stmt []string

	for isFinished := false; !isFinished; {
		thisPrompt := fullPrompt
		if !isInteractive {
			thisPrompt = ""
		} else if len(stmt) > 0 {
			thisPrompt = continuePrompt
		}
		l, err := readline.Line(thisPrompt)

		if !isInteractive && err == io.EOF {
			// In non-interactive mode, we want EOF to finish the last statement.
			err = nil
			isFinished = true
		}
		if err == readline.ErrInterrupt || err == io.EOF {
			break
		} else if err != nil {
			fmt.Fprintf(osStderr, "input error: %s\n", err)
			return err
		}

		tl := strings.TrimSpace(l)

		// Check if this is a request for help or a client-side command.
		// If so, process it directly and skip query processing below.
		status := handleInputLine(&stmt, tl)
		if status == cliExit {
			break
		} else if status == cliNextLine && !isFinished {
			// Ask for more input unless we reached EOF.
			continue
		} else if isFinished && len(stmt) == 0 {
			// Exit if we reached EOF and the currently built-up statement is empty.
			break
		}

		// We join the statements back together with newlines in case
		// there is a significant newline inside a string literal. However
		// we join with spaces for keeping history, because otherwise a
		// history recall will only pull one line from a multi-line
		// statement.
		fullStmt := strings.Join(stmt, "\n")

		if isInteractive {
			// We save the history between each statement, This enables
			// reusing history in another SQL shell without closing the
			// current shell.
			addHistory(strings.Join(stmt, " "))
		}

		if exitErr = runPrettyQuery(conn, os.Stdout, makeQuery(fullStmt)); exitErr != nil {
			fmt.Fprintln(osStderr, exitErr)
		}

		// Clear the saved statement.
		stmt = stmt[:0]
	}

	return exitErr
}