Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
16 changes: 15 additions & 1 deletion pkg/sqlcmd/sqlcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,20 @@ func (s *Sqlcmd) getRunnableQuery(q string) string {
return b.String()
}

// safeColumnTypes safely calls rows.ColumnTypes() and recovers from any panics
// that might occur in the underlying driver, particularly for unsupported types
// like GEOGRAPHY and GEOMETRY (SQL Server type 240).
func safeColumnTypes(rows *sql.Rows) (cols []*sql.ColumnType, err error) {
defer func() {
if r := recover(); r != nil {
// Convert panic to error
err = fmt.Errorf("failed to get column types: %v", r)
cols = nil
Comment thread
dlevy-msft-sql marked this conversation as resolved.
}
}()
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
Expand Down Expand Up @@ -476,7 +490,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)
Expand Down
23 changes: 23 additions & 0 deletions pkg/sqlcmd/sqlcmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,3 +705,26 @@ func TestSqlcmdPrefersSharedMemoryProtocol(t *testing.T) {
assert.EqualValuesf(t, "np", msdsn.ProtocolParsers[3].Protocol(), "np should be fourth protocol")

}

// TestSafeColumnTypesHandlesPanic verifies that safeColumnTypes properly catches
// panics from the underlying driver and converts them to errors.
//
// This test validates the panic recovery mechanism by triggering a panic with a
// nil Rows pointer. While this doesn't test the exact GEOGRAPHY/GEOMETRY type 240
// panic from the driver, it proves that the defer/recover mechanism works correctly
// and any panic (including the type 240 panic) will be caught and converted to an error.
//
// The actual GEOGRAPHY/GEOMETRY panic occurs deep inside the go-mssqldb driver's
// makeGoLangScanType function when it encounters type 240. Our safeColumnTypes
// wrapper ensures this panic is caught regardless of where in the ColumnTypes()
// call stack it originates.
func TestSafeColumnTypesHandlesPanic(t *testing.T) {
// This will trigger a panic due to nil pointer, but safeColumnTypes should catch it
var rows *sql.Rows
cols, err := safeColumnTypes(rows)

// The function should return an error, not panic
assert.Nil(t, cols, "Expected nil cols on panic")
assert.Error(t, err, "Expected error on panic")
assert.Contains(t, err.Error(), "failed to get column types", "Error message should indicate column type failure")
Comment thread
dlevy-msft-sql marked this conversation as resolved.
Outdated
}
Loading