From 608fc3bd11c6ba519ebc75169d522a2574f9aa87 Mon Sep 17 00:00:00 2001 From: David Levy Date: Sat, 24 Jan 2026 22:37:08 -0600 Subject: [PATCH 1/2] Allow -r without argument, defaulting to 0 ODBC sqlcmd allows -r without an explicit value, defaulting to 0 (enable stderr redirection for severity >= 11). Previously go-sqlcmd required -r0 or -r1 explicitly. Changes: - normalizeFlags: Accept empty value for -r flag - getOptionalIntArgument: Return 0 for empty value instead of -1 - Add TestErrorsToStderrDefaultValue test --- cmd/sqlcmd/sqlcmd.go | 7 ++++++- cmd/sqlcmd/sqlcmd_test.go | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index ea655b47..3a8ba271 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -376,6 +376,11 @@ func getOptionalIntArgument(cmd *cobra.Command, name string) (i *int) { if val != nil && val.Changed { i = new(int) value := val.Value.String() + // Handle empty value for flags that allow no argument (e.g., -r without value defaults to 0) + if value == "" { + *i = 0 + return + } v, e := strconv.Atoi(value) if e != nil { *i = -1 @@ -513,7 +518,7 @@ func normalizeFlags(cmd *cobra.Command) error { } case errorsToStderr: switch v { - case "0", "1": + case "0", "1", "": return pflag.NormalizedName(name) default: err = invalidParameterError("-r", v, "0", "1") diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index 511816b2..0ee3732c 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -246,6 +246,49 @@ func TestValidateFlags(t *testing.T) { } } +func TestErrorsToStderrDefaultValue(t *testing.T) { + // Test that -r without a value defaults to 0 (ODBC sqlcmd compatibility) + arguments := &SQLCmdArguments{} + cmd := &cobra.Command{ + Use: "testCommand", + PreRunE: func(cmd *cobra.Command, argss []string) error { + SetScreenWidthFlags(arguments, cmd) + return nil + }, + Run: func(cmd *cobra.Command, argss []string) { + }, + SilenceErrors: true, + SilenceUsage: true, + } + setFlags(cmd, arguments) + // Test -r0 explicit + cmd.SetArgs([]string{"-r0"}) + err := cmd.Execute() + assert.NoError(t, err, "-r0 should not error") + assert.NotNil(t, arguments.ErrorsToStderr, "ErrorsToStderr should be set") + assert.Equal(t, 0, *arguments.ErrorsToStderr, "-r0 should set value to 0") + + // Test -r1 explicit + arguments = &SQLCmdArguments{} + cmd2 := &cobra.Command{ + Use: "testCommand", + PreRunE: func(cmd *cobra.Command, argss []string) error { + SetScreenWidthFlags(arguments, cmd) + return nil + }, + Run: func(cmd *cobra.Command, argss []string) { + }, + SilenceErrors: true, + SilenceUsage: true, + } + setFlags(cmd2, arguments) + cmd2.SetArgs([]string{"-r1"}) + err = cmd2.Execute() + assert.NoError(t, err, "-r1 should not error") + assert.NotNil(t, arguments.ErrorsToStderr, "ErrorsToStderr should be set") + assert.Equal(t, 1, *arguments.ErrorsToStderr, "-r1 should set value to 1") +} + // Simulate main() using files func TestRunInputFiles(t *testing.T) { o, err := os.CreateTemp("", "sqlcmdmain") From efd5f3108e58e6f273baf2e0b7241dd264548bef Mon Sep 17 00:00:00 2001 From: David Levy Date: Sun, 25 Jan 2026 11:41:26 -0600 Subject: [PATCH 2/2] Fix review comments for PR #635 - Remove dead code: empty string case in errorsToStderr switch (unreachable due to early return) - Add test for bare -r flag without argument to verify it defaults to 0 --- cmd/sqlcmd/sqlcmd.go | 2 +- cmd/sqlcmd/sqlcmd_test.go | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 3a8ba271..9871df7f 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -518,7 +518,7 @@ func normalizeFlags(cmd *cobra.Command) error { } case errorsToStderr: switch v { - case "0", "1", "": + case "0", "1": return pflag.NormalizedName(name) default: err = invalidParameterError("-r", v, "0", "1") diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index 0ee3732c..728d0618 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -261,16 +261,36 @@ func TestErrorsToStderrDefaultValue(t *testing.T) { SilenceUsage: true, } setFlags(cmd, arguments) - // Test -r0 explicit - cmd.SetArgs([]string{"-r0"}) + // Test -r alone (bare flag) + cmd.SetArgs(convertOsArgs([]string{"-r"})) err := cmd.Execute() + assert.NoError(t, err, "-r should not error") + assert.NotNil(t, arguments.ErrorsToStderr, "ErrorsToStderr should be set when using -r alone") + assert.Equal(t, 0, *arguments.ErrorsToStderr, "-r alone should default to 0") + + // Test -r0 explicit + arguments = &SQLCmdArguments{} + cmd2 := &cobra.Command{ + Use: "testCommand", + PreRunE: func(cmd *cobra.Command, argss []string) error { + SetScreenWidthFlags(arguments, cmd) + return nil + }, + Run: func(cmd *cobra.Command, argss []string) { + }, + SilenceErrors: true, + SilenceUsage: true, + } + setFlags(cmd2, arguments) + cmd2.SetArgs([]string{"-r0"}) + err = cmd2.Execute() assert.NoError(t, err, "-r0 should not error") assert.NotNil(t, arguments.ErrorsToStderr, "ErrorsToStderr should be set") assert.Equal(t, 0, *arguments.ErrorsToStderr, "-r0 should set value to 0") // Test -r1 explicit arguments = &SQLCmdArguments{} - cmd2 := &cobra.Command{ + cmd3 := &cobra.Command{ Use: "testCommand", PreRunE: func(cmd *cobra.Command, argss []string) error { SetScreenWidthFlags(arguments, cmd) @@ -281,9 +301,9 @@ func TestErrorsToStderrDefaultValue(t *testing.T) { SilenceErrors: true, SilenceUsage: true, } - setFlags(cmd2, arguments) - cmd2.SetArgs([]string{"-r1"}) - err = cmd2.Execute() + setFlags(cmd3, arguments) + cmd3.SetArgs([]string{"-r1"}) + err = cmd3.Execute() assert.NoError(t, err, "-r1 should not error") assert.NotNil(t, arguments.ErrorsToStderr, "ErrorsToStderr should be set") assert.Equal(t, 1, *arguments.ErrorsToStderr, "-r1 should set value to 1")