From 71242a75ab3d6a2a04f925112d7298f8425ee8ba Mon Sep 17 00:00:00 2001 From: David Levy Date: Thu, 5 Feb 2026 20:05:01 -0600 Subject: [PATCH] fix: recover from driver panic on GEOGRAPHY/GEOMETRY columns The go-mssqldb driver panics when calling ColumnTypes() for GEOGRAPHY/GEOMETRY types (type 240). Wrap the call with panic recovery to return an error instead of crashing. --- pkg/sqlcmd/sqlcmd.go | 14 +++++++++++++- pkg/sqlcmd/sqlcmd_test.go | 9 +++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkg/sqlcmd/sqlcmd.go b/pkg/sqlcmd/sqlcmd.go index 5e572a94..e353a89f 100644 --- a/pkg/sqlcmd/sqlcmd.go +++ b/pkg/sqlcmd/sqlcmd.go @@ -413,6 +413,18 @@ func (s *Sqlcmd) getRunnableQuery(q string) string { return b.String() } +// safeColumnTypes wraps rows.ColumnTypes() with panic recovery. +// The go-mssqldb driver panics for unsupported types like GEOGRAPHY/GEOMETRY (type 240). +func safeColumnTypes(rows *sql.Rows) (cols []*sql.ColumnType, err error) { + defer func() { + if r := recover(); r != nil { + err = localizer.Errorf("unsupported column type: %v", r) + cols = nil + } + }() + return rows.ColumnTypes() +} + // runQuery runs the query and prints the results // The return value is based on the first cell of the last column of the last result set. // If it's numeric, it will be converted to int @@ -476,7 +488,7 @@ func (s *Sqlcmd) runQuery(query string) (int, error) { case sqlexp.MsgNext: if first { first = false - cols, err = rows.ColumnTypes() + cols, err = safeColumnTypes(rows) if err != nil { retcode = -100 qe = s.handleError(&retcode, err) diff --git a/pkg/sqlcmd/sqlcmd_test.go b/pkg/sqlcmd/sqlcmd_test.go index dfe97d1a..5200b307 100644 --- a/pkg/sqlcmd/sqlcmd_test.go +++ b/pkg/sqlcmd/sqlcmd_test.go @@ -705,3 +705,12 @@ func TestSqlcmdPrefersSharedMemoryProtocol(t *testing.T) { assert.EqualValuesf(t, "np", msdsn.ProtocolParsers[3].Protocol(), "np should be fourth protocol") } + +func TestSafeColumnTypesRecoversPanic(t *testing.T) { + // A nil *sql.Rows will panic when ColumnTypes() is called. + // safeColumnTypes should recover and return an error instead. + var rows *sql.Rows + cols, err := safeColumnTypes(rows) + assert.Nil(t, cols) + assert.Error(t, err) +}