Beispiel #1
0
// addHistory persists a line of input to the readline history
// file.
func addHistory(line string) {
	// readline.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(line); 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("")
	}
}
Beispiel #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)

	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
}
Beispiel #3
0
// runInteractive runs the SQL client interactively, presenting
// a prompt to the user for each statement.
func runInteractive(conn *sqlConn, config *readline.Config) (exitErr error) {
	fullPrompt, continuePrompt := preparePrompts(conn.url)

	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
	syntax := parser.Traditional

	ins, err := readline.NewEx(config)
	if err != nil {
		return err
	}

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

		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
		}

		// Check if this is a request for help or a client-side command.
		// If so, process it directly and skip query processing below.
		status, hasSet := handleInputLine(&stmt, l, syntax)
		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")

		// Ensure the statement is terminated with a semicolon. This
		// catches cases where the last line before EOF was not terminated
		// properly.
		if len(fullStmt) > 0 && !strings.HasSuffix(fullStmt, ";") {
			fmt.Fprintln(osStderr, "no semicolon at end of statement; statement ignored")
			continue
		}

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

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

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

		if hasSet {
			newSyntax, err := getSyntax(conn)
			if err != nil {
				fmt.Fprintf(osStderr, "could not get session syntax: %s", err)
			} else {
				syntax = newSyntax
			}
		}
	}

	return exitErr
}
Beispiel #4
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
}