Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: adds \d, \dt, \du, \l metacommands to sql shell #39141

Merged
merged 1 commit into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
73 changes: 48 additions & 25 deletions pkg/cli/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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"),
)
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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")
Expand Down
47 changes: 47 additions & 0 deletions pkg/cli/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}