diff --git a/pkg/cli/interactive_tests/test_version_reporting.tcl b/pkg/cli/interactive_tests/test_sql_version_reporting.tcl similarity index 97% rename from pkg/cli/interactive_tests/test_version_reporting.tcl rename to pkg/cli/interactive_tests/test_sql_version_reporting.tcl index 40e157fc944c..482a89d8dda3 100644 --- a/pkg/cli/interactive_tests/test_version_reporting.tcl +++ b/pkg/cli/interactive_tests/test_sql_version_reporting.tcl @@ -7,7 +7,7 @@ start_server $argv spawn $argv sql start_test "Check that the client starts with the welcome message." -eexpect "# Welcome to the cockroach SQL interface." +eexpect "# Welcome to the CockroachDB SQL shell." end_test start_test "Check that the client reports the server version, and correctly detects the version is the same as the client." diff --git a/pkg/cli/sql.go b/pkg/cli/sql.go index 41aaa9ed4461..f4e456158228 100644 --- a/pkg/cli/sql.go +++ b/pkg/cli/sql.go @@ -42,17 +42,36 @@ import ( ) const ( - infoMessage = `# Welcome to the cockroach SQL interface. + welcomeMessage = `# +# Welcome to the CockroachDB SQL shell. # All statements must be terminated by a semicolon. -# To exit: CTRL + D. +# To exit, type: \q. # ` -) + helpMessageFmt = `You are using 'cockroach sql', CockroachDB's lightweight SQL client. +Type: + \? or "help" print this help. + \q, quit, exit exit the shell (Ctrl+C/Ctrl+D also supported) + \! CMD run an external command and print its results on standard output. + \| CMD run an external command and run its output as SQL statements. + \set [NAME] set a client-side flag or (without argument) print the current settings. + \unset NAME unset a flag. + \show during a multi-line statement or transaction, show the SQL entered so far. + \h [NAME] help on syntax of SQL commands. + \hf [NAME] help on SQL built-in functions. + \l list all databases in the CockroachDB cluster + \dt show the tables of the current schema in the current database + \du list the users for all databases + \d TABLE show details about columns in the specified table +More documentation about our SQL dialect and the CLI shell is available online: +%s +%s` -const defaultPromptPattern = "%n@%M/%/%x>" + defaultPromptPattern = "%n@%M/%/%x>" -// debugPromptPattern avoids substitution patterns that require a db roundtrip. -const debugPromptPattern = "%n@%M>" + // debugPromptPattern avoids substitution patterns that require a db roundtrip. + debugPromptPattern = "%n@%M>" +) // sqlShellCmd opens a sql shell. var sqlShellCmd = &cobra.Command{ @@ -187,21 +206,7 @@ const ( // printCliHelp prints a short inline help about the CLI. func printCliHelp() { - fmt.Printf(`You are using 'cockroach sql', CockroachDB's lightweight SQL client. -Type: - \q, quit, exit exit the shell (Ctrl+C/Ctrl+D also supported) - \! CMD run an external command and print its results on standard output. - \| CMD run an external command and run its output as SQL statements. - \set [NAME] set a client-side flag or (without argument) print the current settings. - \unset NAME unset a flag. - \show during a multi-line statement or transaction, show the SQL entered so far. - \? or "help" print this help. - \h [NAME] help on syntax of SQL commands. - \hf [NAME] help on SQL built-in functions. - -More documentation about our SQL dialect and the CLI shell is available online: -%s -%s`, + fmt.Printf(helpMessageFmt, base.DocsURL("sql-statements.html"), base.DocsURL("use-the-built-in-sql-client.html"), ) @@ -214,8 +219,7 @@ func (c *cliState) hasEditor() bool { return c.ins != noLineEditor } -// addHistory persists a line of input to the readline history -// file. +// addHistory persists a line of input to the readline history file. func (c *cliState) addHistory(line string) { if !c.hasEditor() || len(line) == 0 { return @@ -1020,6 +1024,25 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu case `\hf`: return c.handleFunctionHelp(cmd[1:], loopState, errState) + case `\l`: + c.concatLines = `SHOW DATABASES` + return cliRunStatement + + case `\dt`: + c.concatLines = `SHOW TABLES` + return cliRunStatement + + case `\du`: + c.concatLines = `SHOW USERS` + return cliRunStatement + + case `\d`: + if len(cmd) == 2 { + c.concatLines = `SHOW COLUMNS FROM ` + cmd[1] + return cliRunStatement + } + return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + default: if strings.HasPrefix(cmd[0], `\d`) { // Unrecognized command for now, but we want to be helpful. @@ -1408,8 +1431,8 @@ func runTerm(cmd *cobra.Command, args []string) error { checkInteractive() if cliCtx.isInteractive { - // The user only gets to see the info screen on interactive sessions. - fmt.Print(infoMessage) + // The user only gets to see the welcome message on interactive sessions. + fmt.Print(welcomeMessage) } conn, err := getPasswordAndMakeSQLClient("cockroach sql") diff --git a/pkg/cli/sql_test.go b/pkg/cli/sql_test.go index 76dbe2d4877d..7ff7f79ec232 100644 --- a/pkg/cli/sql_test.go +++ b/pkg/cli/sql_test.go @@ -19,6 +19,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/security" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/stretchr/testify/assert" ) // Example_sql_lex tests the usage of the lexer in the sql subcommand. @@ -188,3 +189,49 @@ func TestIsEndOfStatement(t *testing.T) { } } } + +// Test handleCliCmd cases for metacommands that are aliases for sql statements +func TestHandleCliCmdSqlAliasMetacommands(t *testing.T) { + defer leaktest.AfterTest(t)() + metaCommandTestsTable := []struct { + commandString string + wantSQLStmt string + }{ + {`\l`, `SHOW DATABASES`}, + {`\dt`, `SHOW TABLES`}, + {`\du`, `SHOW USERS`}, + {`\d mytable`, `SHOW COLUMNS FROM mytable`}, + } + + var c cliState + for _, tt := range metaCommandTestsTable { + c = setupTestCliState() + c.lastInputLine = tt.commandString + gotState := c.doHandleCliCmd(cliStateEnum(0), cliStateEnum(1)) + + assert.Equal(t, cliRunStatement, gotState) + assert.Equal(t, tt.wantSQLStmt, c.concatLines) + } +} + +func TestHandleCliCmdSlashDInvalidSyntax(t *testing.T) { + defer leaktest.AfterTest(t)() + + metaCommandTestsTable := []string{`\d`, `\d goodarg badarg`, `\dz`} + + var c cliState + for _, tt := range metaCommandTestsTable { + c = setupTestCliState() + c.lastInputLine = tt + gotState := c.doHandleCliCmd(cliStateEnum(0), cliStateEnum(1)) + + assert.Equal(t, cliStateEnum(0), gotState) + assert.Equal(t, errInvalidSyntax, c.exitErr) + } +} + +func setupTestCliState() cliState { + c := cliState{} + c.ins = noLineEditor + return c +}