// 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("") } }
// 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 }
// 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 }
// 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 }