Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"request": "launch",
"mode" : "auto",
"program": "${workspaceFolder}/cmd/modern",
"args" : ["-Q", "EXIT(select net_transport from sys.dm_exec_connections)"],
"args" : ["-Q", "EXIT(select net_transport from sys.dm_exec_connections)"],
},
{
"name" : "Run file query",
Expand Down
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,10 @@ The `sqlcmd` project aims to be a complete port of the original ODBC sqlcmd to t
### Changes in behavior from the ODBC based sqlcmd

The following switches have different behavior in this version of `sqlcmd` compared to the original ODBC based `sqlcmd`.

- `-P` switch will be removed. Passwords for SQL authentication can only be provided through these mechanisms:

- The `SQLCMDPASSWORD` environment variable
- The `:CONNECT` command
- When prompted, the user can type the password to complete a connection
- `-r` requires a 0 or 1 argument
- `-R` switch will be removed. The go runtime does not provide access to user locale information, and it's not readily available through syscall on all supported platforms.
- `-I` switch will be removed. To disable quoted identifier behavior, add `SET QUOTED IDENTIFIER OFF` in your scripts.
- `-N` now takes a string value that can be one of `true`, `false`, or `disable` to specify the encryption choice. (`default` is the same as omitting the parameter)
- `-N` now takes a string value that can be one of `true`, `false`, or `disable` to specify the encryption choice.
- If `-N` and `-C` are not provided, sqlcmd will negotiate authentication with the server without validating the server certificate.
- If `-N` is provided but `-C` is not, sqlcmd will require validation of the server certificate. Note that a `false` value for encryption could still lead to encryption of the login packet.
- If both `-N` and `-C` are provided, sqlcmd will use their values for encryption negotiation.
Expand All @@ -133,6 +127,7 @@ The following switches have different behavior in this version of `sqlcmd` compa
- Some behaviors that were kept to maintain compatibility with `OSQL` may be changed, such as alignment of column headers for some data types.
- All commands must fit on one line, even `EXIT`. Interactive mode will not check for open parentheses or quotes for commands and prompt for successive lines. The ODBC sqlcmd allows the query run by `EXIT(query)` to span multiple lines.
- `-i` now requires multiple arguments for the switch to be separated by `,`.
- `-v` requires multiple variable setters to be comma-separated. eg: `-v var1=v1,var2=v2 -v "var3=v 3"`

### Switches not available in the new sqlcmd (go-sqlcmd) yet

Expand Down
27 changes: 24 additions & 3 deletions build/build.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1)
set "DEL=%%a"
set "ESC=%%b"
)

REM Get Version Tag
for /f %%i in ('"git describe --tags --abbrev=0"') do set sqlcmdVersion=%%i

setlocal
SET RED=%ESC%[1;31m
echo %RED%
Expand All @@ -13,10 +17,26 @@ REM using for/do instead of running it directly so the status code isn't checked
REM Once we are prepared to block the build with the linter we will move this step into a pipeline
for /F "usebackq" %%l in (`go run cmd\sqlcmd-linter\main.go -test %~dp0../...`) DO echo %%l
echo %ESC%[0m

if not exist %gopath%\bin\go-winres.exe (
go install github.com/tc-hib/go-winres@latest
)
if not exist %gopath%\bin\gotext.exe (
go install golang.org/x/text/cmd/gotext@latest
)

REM go-winres likes to append instead of overwrite so delete existing resource file
del %~dp0..\cmd\modern\*.syso

REM generates translations file and resources
go generate %~dp0../... 2> %~dp0generate.txt
echo Fix any conflicting localizable strings:
echo %RED%
findstr conflicting "%~dp0generate.txt"
echo %ESC%[0m
endlocal
REM Get Version Tag
for /f %%i in ('"git describe --tags --abbrev=0"') do set sqlcmdVersion=%%i

if not %errorlevel% == 0 goto :end
REM Generates sqlcmd.exe in the root dir of the repo
go build -o %~dp0..\sqlcmd.exe -ldflags="-X main.version=%sqlcmdVersion%" %~dp0..\cmd\modern

Expand All @@ -34,4 +54,5 @@ setlocal
for /F "tokens=1-3 delims=," %%i in (%~dp0arch.txt) do set GOOS=%%i&set GOARCH=%%j&go build -o %~dp0..\%%i-%%j\%%k -ldflags="-X main.version=%sqlcmdVersion%" %~dp0..\cmd\modern
endlocal


:end
del %~dp0generate.txt
228 changes: 164 additions & 64 deletions cmd/sqlcmd/sqlcmd.go

Large diffs are not rendered by default.

57 changes: 42 additions & 15 deletions cmd/sqlcmd/sqlcmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
// The long flag names are up for debate.
commands := []cmdLineTest{
{[]string{}, func(args SQLCmdArguments) bool {
return args.Server == "" && !args.UseTrustedConnection && args.UserName == ""
return args.Server == "" && !args.UseTrustedConnection && args.UserName == "" && args.ScreenWidth == nil && args.ErrorsToStderr == -1 && args.EncryptConnection == "default"
}},
{[]string{"-c", "MYGO", "-C", "-E", "-i", "file1", "-o", "outfile", "-i", "file2"}, func(args SQLCmdArguments) bool {
return args.BatchTerminator == "MYGO" && args.TrustServerCertificate && len(args.InputFile) == 2 && strings.HasSuffix(args.OutputFile, "outfile")
Expand Down Expand Up @@ -75,15 +75,15 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
{[]string{"--version"}, func(args SQLCmdArguments) bool {
return args.Version
}},
{[]string{}, func(args SQLCmdArguments) bool {
return args.ScreenWidth == nil
}},
{[]string{"-w", "10"}, func(args SQLCmdArguments) bool {
return args.ScreenWidth != nil && *args.ScreenWidth == 10
return args.ScreenWidth != nil && *args.ScreenWidth == 10 && args.FixedTypeWidth == nil && args.VariableTypeWidth == nil
}},
{[]string{"-s", "|", "-w", "10", "-W"}, func(args SQLCmdArguments) bool {
return args.TrimSpaces && args.ColumnSeparator == "|" && *args.ScreenWidth == 10
}},
{[]string{"-y", "100", "-Y", "200", "-P", "placeholder"}, func(args SQLCmdArguments) bool {
return *args.FixedTypeWidth == 200 && *args.VariableTypeWidth == 100 && args.Password == "placeholder"
}},
}

for _, test := range commands {
Expand All @@ -93,8 +93,8 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
Short: "A brief description of my command",
Long: "A long description of my command",
PreRunE: func(cmd *cobra.Command, argss []string) error {
SetScreenWidthFlag(arguments, cmd)
return arguments.Validate()
SetScreenWidthFlags(arguments, cmd)
return arguments.Validate(cmd)
},
Run: func(cmd *cobra.Command, argss []string) {
// Command logic goes here
Expand All @@ -103,6 +103,7 @@ func TestValidCommandLineToArgsConversion(t *testing.T) {
SilenceUsage: true,
}
cmd.SetOut(new(bytes.Buffer))
cmd.SetErr(new(bytes.Buffer))
setFlags(cmd, arguments)
cmd.SetArgs(test.commandLine)
err := cmd.Execute()
Expand All @@ -125,8 +126,13 @@ func TestInvalidCommandLine(t *testing.T) {
commands := []cmdLineTest{
// Issue:341 https://github.com/microsoft/go-sqlcmd/issues/341
//{[]string{"-E", "-U", "someuser"}, "--use-trusted-connection and --user-name can't be used together"},
{[]string{"-F", "what"}, "--format must be one of \"horiz\",\"horizontal\",\"vert\",\"vertical\" but got \"what\""},
{[]string{"-r", "5"}, `--errors-to-stderr must be one of "-1","0","1" but got "5"`},
{[]string{"-F", "what"}, "'-F what': Unexpected argument. Argument value has to be one of [horiz horizontal vert vertical]."},
{[]string{"-r", "5"}, `'-r 5': Unexpected argument. Argument value has to be one of [0 1].`},
{[]string{"-w", "x"}, "'-w x': value must be greater than 8 and less than 65536."},
{[]string{"-y", "111111"}, "'-y 111111': value must be greater than or equal to 0 and less than or equal to 8000."},
{[]string{"-Y", "-2"}, "'-Y -2': value must be greater than or equal to 0 and less than or equal to 8000."},
{[]string{"-P"}, "'-P': Missing argument. Enter '-?' for help."},
{[]string{"-;"}, "';': Unknown Option. Enter '-?' for help."},
}

for _, test := range commands {
Expand All @@ -136,17 +142,25 @@ func TestInvalidCommandLine(t *testing.T) {
Short: "A brief description of my command",
Long: "A long description of my command",
PreRunE: func(cmd *cobra.Command, argss []string) error {
SetScreenWidthFlags(arguments, cmd)
if err := arguments.Validate(cmd); err != nil {
cmd.SilenceUsage = true
return err
}
return normalizeFlags(cmd)
},
Run: func(cmd *cobra.Command, argss []string) {
},
SilenceErrors: true,
SilenceUsage: true,
SilenceUsage: true,
}
buf := &memoryBuffer{buf: new(bytes.Buffer)}
cmd.SetErr(buf)
setFlags(cmd, arguments)
cmd.SetArgs(test.commandLine)
err := cmd.Execute()
assert.EqualError(t, err, test.errorMessage, "Command line:%v", test.commandLine)
assert.EqualError(t, err, test.errorMessage, "Command line:", test.commandLine)
errBytes := buf.buf.String()
assert.Equalf(t, sqlcmdErrorPrefix, string(errBytes)[:len(sqlcmdErrorPrefix)], "Output error should start with '%s' - %s", sqlcmdErrorPrefix, test.commandLine)
}
}

Expand All @@ -170,15 +184,15 @@ func TestValidateFlags(t *testing.T) {
Short: "A brief description of my command",
Long: "A long description of my command",
PreRunE: func(cmd *cobra.Command, argss []string) error {
SetScreenWidthFlag(arguments, cmd)
return arguments.Validate()
SetScreenWidthFlags(arguments, cmd)
return arguments.Validate(cmd)
},
Run: func(cmd *cobra.Command, argss []string) {
},
SilenceErrors: true,
SilenceUsage: true,
}

cmd.SetErr(new(bytes.Buffer))
setFlags(cmd, arguments)
cmd.SetArgs(test.commandLine)
err := cmd.Execute()
Expand Down Expand Up @@ -469,3 +483,16 @@ func canTestAzureAuth() bool {
userName := os.Getenv(sqlcmd.SQLCMDUSER)
return strings.Contains(server, ".database.windows.net") && userName == ""
}

// memoryBuffer has both Write and Close methods for use as io.WriteCloser
type memoryBuffer struct {
buf *bytes.Buffer
}

func (b *memoryBuffer) Write(p []byte) (n int, err error) {
return b.buf.Write(p)
}

func (b *memoryBuffer) Close() error {
return nil
}
3 changes: 1 addition & 2 deletions internal/localizer/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ const (
StdoutName = "stdout"
ColSeparatorVar = "SQLCMDCOLSEP"
ErrorLevel = "ERRORLEVEL"
AppIntentValues = "\"default\",\"readonly\""
EncryptConnValues = "\"default\",\"false\",\"true\",\"disable\""
AppIntentValues = `"readonly"`
FormatValues = "\"horiz\",\"horizontal\",\"vert\",\"vertical\""
ErrToStderrValues = "\"-1\",\"0\",\"1\""
)
Loading