diff --git a/lib/client/db/dbcmd/dbcmd.go b/lib/client/db/dbcmd/dbcmd.go index 812eb62b2c9..26cb5d6f7e7 100644 --- a/lib/client/db/dbcmd/dbcmd.go +++ b/lib/client/db/dbcmd/dbcmd.go @@ -477,12 +477,6 @@ func (c *CLICommandBuilder) isMySQLBinMariaDBFlavor() (bool, error) { return strings.Contains(strings.ToLower(string(mysqlVer)), "mariadb"), nil } -// isSqlcmdAvailable returns true if "sqlcmd" binary is fouind in the system -// PATH. -func (c *CLICommandBuilder) isSqlcmdAvailable() bool { - return c.isBinAvailable(sqlcmdBin) -} - func (c *CLICommandBuilder) shouldUseMongoshBin(db types.Database) bool { // DocumentDB prefers the legacy "mongo" client. if db.GetType() == types.DatabaseTypeDocumentDB { @@ -645,6 +639,13 @@ func (c *CLICommandBuilder) getRedisCommand() *exec.Cmd { // getSQLServerCommand returns a command to connect to SQL Server. // mssql-cli and sqlcmd commands have the same argument names. +// +// sqlcmd is preferred and used by default. mssql-cli is only returned when +// sqlcmd is not in PATH but mssql-cli is, so users who only have mssql-cli +// installed are still given a working command. When neither binary is in PATH +// (e.g., Teleport Connect launched from a desktop environment where $PATH is +// not set), the printed command falls back to sqlcmd, which is the actively +// maintained client. func (c *CLICommandBuilder) getSQLServerCommand() *exec.Cmd { args := []string{ // Host and port must be comma-separated. @@ -659,11 +660,11 @@ func (c *CLICommandBuilder) getSQLServerCommand() *exec.Cmd { args = append(args, "-d", c.db.Database) } - if c.isSqlcmdAvailable() { - return exec.Command(sqlcmdBin, args...) + if !c.isBinAvailable(sqlcmdBin) && c.isBinAvailable(mssqlBin) { + return exec.Command(mssqlBin, args...) } - return exec.Command(mssqlBin, args...) + return exec.Command(sqlcmdBin, args...) } func (c *CLICommandBuilder) getSnowflakeCommand() *exec.Cmd { diff --git a/lib/client/db/dbcmd/dbcmd_test.go b/lib/client/db/dbcmd/dbcmd_test.go index 2ad618608ed..6d2a4588a0c 100644 --- a/lib/client/db/dbcmd/dbcmd_test.go +++ b/lib/client/db/dbcmd/dbcmd_test.go @@ -465,7 +465,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) { dbProtocol: defaults.ProtocolSQLServer, databaseName: "mydb", execer: &fakeExec{}, - cmd: []string{mssqlBin, + cmd: []string{sqlcmdBin, "-S", "localhost,12345", "-U", "myUser", "-P", fixtures.UUID, @@ -490,6 +490,41 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) { }, wantErr: false, }, + { + name: "sqlserver mssql-cli", + dbProtocol: defaults.ProtocolSQLServer, + databaseName: "mydb", + execer: &fakeExec{ + execOutput: map[string][]byte{ + "mssql-cli": {}, + }, + }, + cmd: []string{mssqlBin, + "-S", "localhost,12345", + "-U", "myUser", + "-P", fixtures.UUID, + "-d", "mydb", + }, + wantErr: false, + }, + { + name: "sqlserver prefers sqlcmd over mssql-cli", + dbProtocol: defaults.ProtocolSQLServer, + databaseName: "mydb", + execer: &fakeExec{ + execOutput: map[string][]byte{ + "sqlcmd": {}, + "mssql-cli": {}, + }, + }, + cmd: []string{sqlcmdBin, + "-S", "localhost,12345", + "-U", "myUser", + "-P", fixtures.UUID, + "-d", "mydb", + }, + wantErr: false, + }, { name: "redis-cli", dbProtocol: defaults.ProtocolRedis,