From 2dcbb88b98733d7b90125fa5857851b567afba52 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 16:25:26 -0500 Subject: [PATCH 01/10] feat: implement -j raw-errors flag for ODBC sqlcmd compatibility Adds the -j / --raw-errors flag to the legacy sqlcmd CLI, mirroring the ODBC sqlcmd's m_PrintRawErrorMessages behavior (Sql/utils/sqlcmd/console/src/Formatter.cpp). When set, the driver-supplied 'mssql: ' prefix is preserved in error output; the Msg/Level/State/Server header and screen-width wrapping are unaffected, matching ODBC sqlcmd which also only gates the prefix strip. Closes the -j gap tracked in discussion #292. --- cmd/sqlcmd/sqlcmd.go | 4 +++- cmd/sqlcmd/sqlcmd_test.go | 3 +++ pkg/sqlcmd/format.go | 25 +++++++++++++++++++++--- pkg/sqlcmd/format_test.go | 41 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 5abc0860..91d3b0d4 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -83,6 +83,7 @@ type SQLCmdArguments struct { ChangePasswordAndExit string TraceFile string ServerNameOverride string + RawErrors bool // Keep Help at the end of the list Help bool } @@ -481,6 +482,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) { rootCmd.Flags().IntVar(&args.DriverLoggingLevel, "driver-logging-level", 0, localizer.Sprintf("Level of mssql driver messages to print")) rootCmd.Flags().BoolVarP(&args.ExitOnError, "exit-on-error", "b", false, localizer.Sprintf("Specifies that sqlcmd exits and returns a %s value when an error occurs", localizer.DosErrorLevel)) rootCmd.Flags().IntVarP(&args.ErrorLevel, "error-level", "m", 0, localizer.Sprintf("Controls which error messages are sent to %s. Messages that have severity level greater than or equal to this level are sent", localizer.StdoutName)) + rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Prints raw error messages from the SQL Server driver without removing the driver-supplied prefix from the message text")) //Need to decide on short of Header , as "h" is already used in help command in Cobra rootCmd.Flags().IntVarP(&args.Headers, "headers", "h", 0, localizer.Sprintf("Specifies the number of rows to print between the column headings. Use -h-1 to specify that headers not be printed")) @@ -862,7 +864,7 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) { } s.Connect = &connectConfig - s.Format = sqlcmd.NewSQLCmdDefaultFormatter(args.TrimSpaces, args.getControlCharacterBehavior()) + s.Format = sqlcmd.NewSQLCmdDefaultFormatter(args.TrimSpaces, args.getControlCharacterBehavior(), sqlcmd.WithRawErrors(args.RawErrors)) if args.OutputFile != "" { err = s.RunCommand(s.Cmd["OUT"], []string{args.OutputFile}) if err != nil { diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index 4554998a..b81b805b 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -123,6 +123,9 @@ func TestValidCommandLineToArgsConversion(t *testing.T) { {[]string{"-N", "true", "-J", "/path/to/cert2.pem"}, func(args SQLCmdArguments) bool { return args.EncryptConnection == "true" && args.ServerCertificate == "/path/to/cert2.pem" }}, + {[]string{"-j"}, func(args SQLCmdArguments) bool { + return args.RawErrors + }}, } for _, test := range commands { diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 55bd2e25..0495feff 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -85,16 +85,33 @@ type sqlCmdFormatterType struct { maxColNameLen int colorizer color.Colorizer xml bool + rawErrors bool +} + +// FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. +type FormatterOption func(*sqlCmdFormatterType) + +// WithRawErrors controls the -j "raw error messages" behavior. When enabled, +// the driver-supplied error text is emitted verbatim (the "mssql: " prefix +// that go-mssqldb prepends is not stripped). The Msg/Level/State/Server +// header and screen-width wrapping are unaffected, matching the ODBC sqlcmd -j +// implementation, which also only suppresses driver-prefix stripping. +func WithRawErrors(raw bool) FormatterOption { + return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } // NewSQLCmdDefaultFormatter returns a Formatter that mimics the original ODBC-based sqlcmd formatter -func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior) Formatter { - return &sqlCmdFormatterType{ +func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...FormatterOption) Formatter { + f := &sqlCmdFormatterType{ removeTrailingSpaces: removeTrailingSpaces, format: "horizontal", colorizer: color.New(false), ccb: ccb, } + for _, opt := range opts { + opt(f) + } + return f } // Adds the given string to the current line, wrapping it based on the screen width setting @@ -228,7 +245,9 @@ func (f *sqlCmdFormatterType) AddError(err error) { } else { b.WriteString(localizer.Sprintf("Msg %#v, Level %d, State %d, Server %s, Line %#v%s", e.Number, e.Class, e.State, e.ServerName, e.LineNo, SqlcmdEol)) } - msg = strings.TrimPrefix(msg, "mssql: ") + if !f.rawErrors { + msg = strings.TrimPrefix(msg, "mssql: ") + } } } if print { diff --git a/pkg/sqlcmd/format_test.go b/pkg/sqlcmd/format_test.go index f4bee464..907e6ebb 100644 --- a/pkg/sqlcmd/format_test.go +++ b/pkg/sqlcmd/format_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + mssql "github.com/microsoft/go-mssqldb" "github.com/microsoft/go-sqlcmd/internal/color" "github.com/stretchr/testify/assert" ) @@ -162,3 +163,43 @@ func TestFormatterXmlMode(t *testing.T) { assert.NoError(t, err, "runSqlCmd returned error") assert.Equal(t, ``+SqlcmdEol, buf.buf.String()) } + +func TestAddErrorRawErrors(t *testing.T) { + mssqlErr := mssql.Error{ + Number: 50000, + State: 1, + Class: 16, + Message: "Something failed", + ServerName: "server", + LineNo: 7, + } + + tests := []struct { + name string + rawErrors bool + }{ + {name: "default strips mssql prefix", rawErrors: false}, + {name: "raw preserves mssql prefix", rawErrors: true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + out := new(strings.Builder) + errOut := new(strings.Builder) + vars := InitializeVariables(false) + f := NewSQLCmdDefaultFormatter(false, ControlIgnore, WithRawErrors(tc.rawErrors)) + f.BeginBatch("", vars, out, errOut) + + f.AddError(mssqlErr) + + got := errOut.String() + assert.Contains(t, got, "Msg 50000, Level 16, State 1, Server server, Line 7", "header should always be printed") + assert.Contains(t, got, "Something failed", "message body should always be printed") + if tc.rawErrors { + assert.Contains(t, got, "mssql: Something failed", "raw mode preserves the driver-supplied prefix") + } else { + assert.NotContains(t, got, "mssql: ", "default mode strips the driver-supplied prefix") + } + }) + } +} From 881b158135686631fbddd22e843e11ac9b9b59d3 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 16:41:49 -0500 Subject: [PATCH 02/10] refactor: skip nil FormatterOption to harden variadic API --- pkg/sqlcmd/format.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 0495feff..b0c64e92 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -109,7 +109,9 @@ func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBe ccb: ccb, } for _, opt := range opts { - opt(f) + if opt != nil { + opt(f) + } } return f } From 878e6d4d8cb75f90f8391fa24126241eeb5b1673 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 16:43:40 -0500 Subject: [PATCH 03/10] docs: clarify -j wording to name the go-mssqldb prefix explicitly --- cmd/sqlcmd/sqlcmd.go | 2 +- pkg/sqlcmd/format.go | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 91d3b0d4..ebb3430c 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -482,7 +482,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) { rootCmd.Flags().IntVar(&args.DriverLoggingLevel, "driver-logging-level", 0, localizer.Sprintf("Level of mssql driver messages to print")) rootCmd.Flags().BoolVarP(&args.ExitOnError, "exit-on-error", "b", false, localizer.Sprintf("Specifies that sqlcmd exits and returns a %s value when an error occurs", localizer.DosErrorLevel)) rootCmd.Flags().IntVarP(&args.ErrorLevel, "error-level", "m", 0, localizer.Sprintf("Controls which error messages are sent to %s. Messages that have severity level greater than or equal to this level are sent", localizer.StdoutName)) - rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Prints raw error messages from the SQL Server driver without removing the driver-supplied prefix from the message text")) + rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Prints error messages exactly as returned by the go-mssqldb client driver, without stripping its \"mssql: \" prefix")) //Need to decide on short of Header , as "h" is already used in help command in Cobra rootCmd.Flags().IntVarP(&args.Headers, "headers", "h", 0, localizer.Sprintf("Specifies the number of rows to print between the column headings. Use -h-1 to specify that headers not be printed")) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index b0c64e92..e1231072 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -92,10 +92,11 @@ type sqlCmdFormatterType struct { type FormatterOption func(*sqlCmdFormatterType) // WithRawErrors controls the -j "raw error messages" behavior. When enabled, -// the driver-supplied error text is emitted verbatim (the "mssql: " prefix -// that go-mssqldb prepends is not stripped). The Msg/Level/State/Server -// header and screen-width wrapping are unaffected, matching the ODBC sqlcmd -j -// implementation, which also only suppresses driver-prefix stripping. +// the error string returned by the go-mssqldb client driver is emitted verbatim +// (the "mssql: " prefix that go-mssqldb's mssql.Error.Error() prepends is not +// stripped). The Msg/Level/State/Server header and screen-width wrapping are +// unaffected, matching the ODBC sqlcmd -j implementation, which likewise only +// suppresses the prefix strip. func WithRawErrors(raw bool) FormatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } From 456773ad0bf3410d54109a6e7abd293c8c8dc6af Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 16:58:48 -0500 Subject: [PATCH 04/10] refactor: trim -j docs and split test for DAMP readability --- cmd/sqlcmd/sqlcmd.go | 2 +- pkg/sqlcmd/format.go | 8 ++---- pkg/sqlcmd/format_test.go | 57 +++++++++++++++------------------------ 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index ebb3430c..7230c8d4 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -482,7 +482,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) { rootCmd.Flags().IntVar(&args.DriverLoggingLevel, "driver-logging-level", 0, localizer.Sprintf("Level of mssql driver messages to print")) rootCmd.Flags().BoolVarP(&args.ExitOnError, "exit-on-error", "b", false, localizer.Sprintf("Specifies that sqlcmd exits and returns a %s value when an error occurs", localizer.DosErrorLevel)) rootCmd.Flags().IntVarP(&args.ErrorLevel, "error-level", "m", 0, localizer.Sprintf("Controls which error messages are sent to %s. Messages that have severity level greater than or equal to this level are sent", localizer.StdoutName)) - rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Prints error messages exactly as returned by the go-mssqldb client driver, without stripping its \"mssql: \" prefix")) + rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Keep the \"mssql: \" prefix that go-mssqldb adds to error messages")) //Need to decide on short of Header , as "h" is already used in help command in Cobra rootCmd.Flags().IntVarP(&args.Headers, "headers", "h", 0, localizer.Sprintf("Specifies the number of rows to print between the column headings. Use -h-1 to specify that headers not be printed")) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index e1231072..0017faa0 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -91,12 +91,8 @@ type sqlCmdFormatterType struct { // FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. type FormatterOption func(*sqlCmdFormatterType) -// WithRawErrors controls the -j "raw error messages" behavior. When enabled, -// the error string returned by the go-mssqldb client driver is emitted verbatim -// (the "mssql: " prefix that go-mssqldb's mssql.Error.Error() prepends is not -// stripped). The Msg/Level/State/Server header and screen-width wrapping are -// unaffected, matching the ODBC sqlcmd -j implementation, which likewise only -// suppresses the prefix strip. +// WithRawErrors implements -j: when raw is true, AddError keeps the "mssql: " +// prefix that go-mssqldb adds to error text instead of stripping it. func WithRawErrors(raw bool) FormatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } diff --git a/pkg/sqlcmd/format_test.go b/pkg/sqlcmd/format_test.go index 907e6ebb..621773f4 100644 --- a/pkg/sqlcmd/format_test.go +++ b/pkg/sqlcmd/format_test.go @@ -164,42 +164,27 @@ func TestFormatterXmlMode(t *testing.T) { assert.Equal(t, ``+SqlcmdEol, buf.buf.String()) } -func TestAddErrorRawErrors(t *testing.T) { - mssqlErr := mssql.Error{ - Number: 50000, - State: 1, - Class: 16, - Message: "Something failed", - ServerName: "server", - LineNo: 7, - } +func TestAddErrorStripsMssqlPrefixByDefault(t *testing.T) { + out, errOut := new(strings.Builder), new(strings.Builder) + f := NewSQLCmdDefaultFormatter(false, ControlIgnore) + f.BeginBatch("", InitializeVariables(false), out, errOut) - tests := []struct { - name string - rawErrors bool - }{ - {name: "default strips mssql prefix", rawErrors: false}, - {name: "raw preserves mssql prefix", rawErrors: true}, - } + f.AddError(mssql.Error{Number: 50000, State: 1, Class: 16, Message: "Something failed", ServerName: "server", LineNo: 7}) - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - out := new(strings.Builder) - errOut := new(strings.Builder) - vars := InitializeVariables(false) - f := NewSQLCmdDefaultFormatter(false, ControlIgnore, WithRawErrors(tc.rawErrors)) - f.BeginBatch("", vars, out, errOut) - - f.AddError(mssqlErr) - - got := errOut.String() - assert.Contains(t, got, "Msg 50000, Level 16, State 1, Server server, Line 7", "header should always be printed") - assert.Contains(t, got, "Something failed", "message body should always be printed") - if tc.rawErrors { - assert.Contains(t, got, "mssql: Something failed", "raw mode preserves the driver-supplied prefix") - } else { - assert.NotContains(t, got, "mssql: ", "default mode strips the driver-supplied prefix") - } - }) - } + got := errOut.String() + assert.Contains(t, got, "Msg 50000, Level 16, State 1, Server server, Line 7") + assert.Contains(t, got, "Something failed") + assert.NotContains(t, got, "mssql: Something failed") +} + +func TestAddErrorWithRawErrorsKeepsMssqlPrefix(t *testing.T) { + out, errOut := new(strings.Builder), new(strings.Builder) + f := NewSQLCmdDefaultFormatter(false, ControlIgnore, WithRawErrors(true)) + f.BeginBatch("", InitializeVariables(false), out, errOut) + + f.AddError(mssql.Error{Number: 50000, State: 1, Class: 16, Message: "Something failed", ServerName: "server", LineNo: 7}) + + got := errOut.String() + assert.Contains(t, got, "Msg 50000, Level 16, State 1, Server server, Line 7") + assert.Contains(t, got, "mssql: Something failed") } From 7b2359388b27f6c17798d8838e7c9c7c9c7a828d Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 17:15:52 -0500 Subject: [PATCH 05/10] docs: drop driver name from -j help text --- cmd/sqlcmd/sqlcmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 7230c8d4..766fd018 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -482,7 +482,7 @@ func setFlags(rootCmd *cobra.Command, args *SQLCmdArguments) { rootCmd.Flags().IntVar(&args.DriverLoggingLevel, "driver-logging-level", 0, localizer.Sprintf("Level of mssql driver messages to print")) rootCmd.Flags().BoolVarP(&args.ExitOnError, "exit-on-error", "b", false, localizer.Sprintf("Specifies that sqlcmd exits and returns a %s value when an error occurs", localizer.DosErrorLevel)) rootCmd.Flags().IntVarP(&args.ErrorLevel, "error-level", "m", 0, localizer.Sprintf("Controls which error messages are sent to %s. Messages that have severity level greater than or equal to this level are sent", localizer.StdoutName)) - rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Keep the \"mssql: \" prefix that go-mssqldb adds to error messages")) + rootCmd.Flags().BoolVarP(&args.RawErrors, "raw-errors", "j", false, localizer.Sprintf("Do not strip the \"mssql: \" prefix from error messages")) //Need to decide on short of Header , as "h" is already used in help command in Cobra rootCmd.Flags().IntVarP(&args.Headers, "headers", "h", 0, localizer.Sprintf("Specifies the number of rows to print between the column headings. Use -h-1 to specify that headers not be printed")) From d0b37a4f24dff9ca28fe849bf65d11051975e6b8 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 17:32:49 -0500 Subject: [PATCH 06/10] refactor: unexport formatterOption for closed-set options --- pkg/sqlcmd/format.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 0017faa0..1a304c3c 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -88,17 +88,18 @@ type sqlCmdFormatterType struct { rawErrors bool } -// FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. -type FormatterOption func(*sqlCmdFormatterType) +// formatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. +// The type is unexported so options must come from helpers in this package. +type formatterOption func(*sqlCmdFormatterType) // WithRawErrors implements -j: when raw is true, AddError keeps the "mssql: " // prefix that go-mssqldb adds to error text instead of stripping it. -func WithRawErrors(raw bool) FormatterOption { +func WithRawErrors(raw bool) formatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } // NewSQLCmdDefaultFormatter returns a Formatter that mimics the original ODBC-based sqlcmd formatter -func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...FormatterOption) Formatter { +func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...formatterOption) Formatter { f := &sqlCmdFormatterType{ removeTrailingSpaces: removeTrailingSpaces, format: "horizontal", From 932fb6d7f87ba193e4284045b7cd2440fa98d9de Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 17:48:11 -0500 Subject: [PATCH 07/10] refactor: re-export FormatterOption for public SDK consumers --- pkg/sqlcmd/format.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 1a304c3c..0017faa0 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -88,18 +88,17 @@ type sqlCmdFormatterType struct { rawErrors bool } -// formatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. -// The type is unexported so options must come from helpers in this package. -type formatterOption func(*sqlCmdFormatterType) +// FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. +type FormatterOption func(*sqlCmdFormatterType) // WithRawErrors implements -j: when raw is true, AddError keeps the "mssql: " // prefix that go-mssqldb adds to error text instead of stripping it. -func WithRawErrors(raw bool) formatterOption { +func WithRawErrors(raw bool) FormatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } // NewSQLCmdDefaultFormatter returns a Formatter that mimics the original ODBC-based sqlcmd formatter -func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...formatterOption) Formatter { +func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...FormatterOption) Formatter { f := &sqlCmdFormatterType{ removeTrailingSpaces: removeTrailingSpaces, format: "horizontal", From fc8c7d45f442dd914e4a67c61730d3fa9370ec86 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 18:00:35 -0500 Subject: [PATCH 08/10] test: tighten default-mode assertion to forbid any mssql: occurrence --- pkg/sqlcmd/format_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sqlcmd/format_test.go b/pkg/sqlcmd/format_test.go index 621773f4..f51f2cbc 100644 --- a/pkg/sqlcmd/format_test.go +++ b/pkg/sqlcmd/format_test.go @@ -174,7 +174,7 @@ func TestAddErrorStripsMssqlPrefixByDefault(t *testing.T) { got := errOut.String() assert.Contains(t, got, "Msg 50000, Level 16, State 1, Server server, Line 7") assert.Contains(t, got, "Something failed") - assert.NotContains(t, got, "mssql: Something failed") + assert.NotContains(t, got, "mssql:") } func TestAddErrorWithRawErrorsKeepsMssqlPrefix(t *testing.T) { From a65bd095c068372ef0de05fe036eab757eccb802 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 18:16:29 -0500 Subject: [PATCH 09/10] docs: decouple WithRawErrors doc from CLI flag name --- pkg/sqlcmd/format.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 0017faa0..85f7ed4b 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -91,8 +91,9 @@ type sqlCmdFormatterType struct { // FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. type FormatterOption func(*sqlCmdFormatterType) -// WithRawErrors implements -j: when raw is true, AddError keeps the "mssql: " -// prefix that go-mssqldb adds to error text instead of stripping it. +// WithRawErrors controls AddError prefix handling: when raw is true, AddError +// keeps the "mssql: " prefix that go-mssqldb adds to error text instead of +// stripping it. func WithRawErrors(raw bool) FormatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } From 7270f015c69d0dabab0861e7d6646c47fdbfed17 Mon Sep 17 00:00:00 2001 From: David Levy Date: Tue, 19 May 2026 18:26:51 -0500 Subject: [PATCH 10/10] docs: document FormatterOption and opts param; test: add --raw-errors long-form case --- cmd/sqlcmd/sqlcmd_test.go | 3 +++ pkg/sqlcmd/format.go | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index b81b805b..5f6f2740 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -126,6 +126,9 @@ func TestValidCommandLineToArgsConversion(t *testing.T) { {[]string{"-j"}, func(args SQLCmdArguments) bool { return args.RawErrors }}, + {[]string{"--raw-errors"}, func(args SQLCmdArguments) bool { + return args.RawErrors + }}, } for _, test := range commands { diff --git a/pkg/sqlcmd/format.go b/pkg/sqlcmd/format.go index 85f7ed4b..3a016b62 100644 --- a/pkg/sqlcmd/format.go +++ b/pkg/sqlcmd/format.go @@ -88,7 +88,8 @@ type sqlCmdFormatterType struct { rawErrors bool } -// FormatterOption configures a Formatter returned by NewSQLCmdDefaultFormatter. +// FormatterOption customizes the default formatter built by NewSQLCmdDefaultFormatter. +// Use the provided With* constructors (e.g. WithRawErrors) to supply options. type FormatterOption func(*sqlCmdFormatterType) // WithRawErrors controls AddError prefix handling: when raw is true, AddError @@ -98,7 +99,8 @@ func WithRawErrors(raw bool) FormatterOption { return func(f *sqlCmdFormatterType) { f.rawErrors = raw } } -// NewSQLCmdDefaultFormatter returns a Formatter that mimics the original ODBC-based sqlcmd formatter +// NewSQLCmdDefaultFormatter returns a Formatter that mimics the original ODBC-based sqlcmd formatter. +// Any FormatterOption values passed via opts (e.g. WithRawErrors) are applied to the new formatter. func NewSQLCmdDefaultFormatter(removeTrailingSpaces bool, ccb ControlCharacterBehavior, opts ...FormatterOption) Formatter { f := &sqlCmdFormatterType{ removeTrailingSpaces: removeTrailingSpaces,