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/sql: prompting fixes #105137

Merged
merged 1 commit into from Jun 21, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 72 additions & 11 deletions pkg/cli/clisqlshell/sql.go
Expand Up @@ -131,10 +131,10 @@ Commands specific to the demo shell (EXPERIMENTAL):
\demo add <locality> add a node (locality specified as "region=<region>,zone=<zone>").
`

defaultPromptPattern = "%n@%M/%C%/%x>"
defaultPromptPattern = "%n@%M:%>/%C%/%x>"

// debugPromptPattern avoids substitution patterns that require a db roundtrip.
debugPromptPattern = "%n@%M %C>"
debugPromptPattern = "%n@%M:%> %C>"
)

// cliState defines the current state of the CLI during
Expand Down Expand Up @@ -530,7 +530,7 @@ var options = map[string]struct {
deprecated: true,
},
`prompt1`: {
description: "prompt string to use before each command (the following are expanded: %M full host, %m host, %> port number, %n user, %/ database, %x txn status)",
description: "prompt string to use before each command (expansions: %M full host, %m host, %> port number, %n user, %/ database, %x txn status, %C logical cluster)",
isBoolean: false,
validDuringMultilineEntry: true,
set: func(c *cliState, val string) error {
Expand Down Expand Up @@ -971,31 +971,92 @@ func (c *cliState) doRefreshPrompts(nextState cliStateEnum) cliStateEnum {
}

c.fullPrompt = rePromptFmt.ReplaceAllStringFunc(c.iCtx.customPromptPattern, func(m string) string {
// See:
// https://www.postgresql.org/docs/15/app-psql.html#APP-PSQL-PROMPTING
switch m {
case "%M":
_, host, port := parsedURL.GetNetworking()
return host + ":" + port // server:port
// "The full host name (with domain name) of the database
// server, or [local] if the connection is over a Unix domain
// socket, or [local:/dir/name], if the Unix domain socket is
// not at the compiled in default location."
net, host, _ := parsedURL.GetNetworking()
switch net {
case pgurl.ProtoTCP:
return host
case pgurl.ProtoUnix:
// We do not have "compiled-in default location" in
// CockroachDB so the location is always explicit.
return fmt.Sprintf("[local:%s]", host)
default:
// unreachable
return ""
}

case "%m":
_, host, _ := parsedURL.GetNetworking()
return host
// "The host name of the database server, truncated at the
// first dot, or [local] if the connection is over a Unix
// domain socket."
net, host, _ := parsedURL.GetNetworking()
switch net {
case pgurl.ProtoTCP:
return strings.SplitN(host, ".", 2)[0]
case pgurl.ProtoUnix:
return "[local]"
default:
// unreachable
return ""
}

case "%>":
// "The port number at which the database server is listening."
_, _, port := parsedURL.GetNetworking()
return port
case "%n": // user name.

case "%n":
// "The database session user name."
//
// TODO(sql): in psql this is updated based on the current user
// in the session set via SET SESSION AUTHORIZATION.
// See: https://github.com/cockroachdb/cockroach/issues/105136
return userName
case "%/": // database name.

case "%/":
// "The name of the current database."
return dbName
case "%x": // txn status.

case "%x":
// "Transaction status:..."
// Note: the specific string here is incompatible with psql.
// For example we use "OPEN" instead of '*". This was an extremely
// early decision in the SQL shell's history.
return c.lastKnownTxnStatus

case "%%":
return "%"

case "%C":
// CockroachDB extension: the logical cluster name.
return logicalCluster

default:
// Not implemented:
// %~: "Like %/, but the output is ~ (tilde) if the database is your default database."
// CockroachDB does not have per-user default databases (yet).
// %#: "If the session user is a database superuser, then a #, otherwise a >."
// The shell does not know how to determine this yet. See: #105136
// %p: "The process ID of the backend currently connected to."
// CockroachDB does not have "backend process IDs" like PostgreSQL.
// %R: continuation character
// The mechanism for continuation is handled internally by the input editor.
// %l: "The line number inside the current statement, starting from 1."
// Lines are handled internally by the input editor.
// %w: "Whitespace of the same width as the most recent output of PROMPT1."
// The prompt alignment for multi-line edits is handled internally by the input editor.
// %digits: "Character with given octal code."
// This can also be done via `\set prompt1 '\NNN'`
err = fmt.Errorf("unrecognized format code in prompt: %q", m)
return ""
}

})
if err != nil {
c.fullPrompt = err.Error()
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/interactive_tests/test_client_side_checking.tcl
Expand Up @@ -98,7 +98,7 @@ eexpect "root@"
send "\\set display_format csv\r\\set\r"
eexpect "check_syntax,false"
eexpect "echo,true"
eexpect "prompt1,%n@%M %C>"
eexpect "prompt1,%n@%M:%> %C>"
eexpect "root@"
send "\\q\r"
eexpect ":/# "
Expand Down
8 changes: 3 additions & 5 deletions pkg/cli/interactive_tests/test_txn_prompt.tcl
Expand Up @@ -8,11 +8,9 @@ spawn /bin/bash
send "PS1='\\h:''/# '\r"
eexpect ":/# "

send "$argv sql --no-line-editor --host=localhost\r"
send "$argv sql --no-line-editor --host=127.0.0.1\r"
eexpect root@

###START tests prompt customization

start_test "Check that invalid prompt patterns cause an error."
send "\\set prompt1 %?\r"
eexpect "unrecognized format code in prompt"
Expand All @@ -27,10 +25,10 @@ send "\\set prompt1 abc%%def\r"
eexpect "abc%def"

send "\\set prompt1 abc%Mdef\r"
eexpect abclocalhost:26257def
eexpect abc127.0.0.1def

send "\\set prompt1 abc%mdef\r"
eexpect abclocalhostdef
eexpect abc127def

send "\\set prompt1 abc%>def\r"
eexpect abc26257def
Expand Down
21 changes: 17 additions & 4 deletions pkg/cli/interactive_tests/test_url_login.tcl
Expand Up @@ -17,19 +17,32 @@ end_test

stop_server $argv

start_test "Check that the unix socket can be used simply."

# Start a server with a socket listener.
set mywd [pwd]

system "$argv start-single-node --insecure --pid-file=server_pid --socket-dir=. --background -s=path=logs/db >>logs/expect-cmd.log 2>&1;
$argv sql --insecure -e 'select 1'"

start_test "Check that the unix socket can be used simply."

spawn $argv sql --no-line-editor --url "postgresql://?host=$mywd&port=26257"
eexpect root@

end_test


start_test "Check that the prompt can be customized to display a socket-based conn."
send "\\set prompt1 abc%Mdef\r"
eexpect "abc\\\[local:$mywd\\\]def"

send "\\set prompt1 abc%mdef\r"
expect "abc\\\[local\\\]def"

send "\\set prompt1 abc%>def\r"
eexpect "abc26257def"
end_test

send_eof
eexpect eof

stop_server $argv

end_test