Skip to content

Commit 8ef7a8f

Browse files
Add float64/float32 formatting to match ODBC sqlcmd behavior
Co-authored-by: dlevy-msft-sql <[email protected]>
1 parent de9d0c2 commit 8ef7a8f

3 files changed

Lines changed: 45 additions & 0 deletions

File tree

modern

25.4 MB
Binary file not shown.

pkg/sqlcmd/format.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"errors"
1010
"fmt"
1111
"io"
12+
"strconv"
1213
"strings"
1314
"time"
1415

@@ -530,6 +531,14 @@ func (f *sqlCmdFormatterType) scanRow(rows *sql.Rows) ([]string, error) {
530531
} else {
531532
row[n] = "0"
532533
}
534+
case float64:
535+
// Format float64 to match ODBC sqlcmd behavior
536+
// Use 'f' format with -1 precision to avoid scientific notation
537+
// and to show all significant digits
538+
row[n] = strconv.FormatFloat(x, 'f', -1, 64)
539+
case float32:
540+
// Format float32 to match ODBC sqlcmd behavior
541+
row[n] = strconv.FormatFloat(float64(x), 'f', -1, 32)
533542
default:
534543
var err error
535544
if row[n], err = fmt.Sprintf("%v", x), nil; err != nil {

pkg/sqlcmd/format_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,39 @@ func TestFormatterXmlMode(t *testing.T) {
158158
assert.NoError(t, err, "runSqlCmd returned error")
159159
assert.Equal(t, `<sys.databases name="master"/>`+SqlcmdEol, buf.buf.String())
160160
}
161+
162+
func TestFormatterFloatFormatting(t *testing.T) {
163+
// Test that float formatting matches ODBC sqlcmd behavior
164+
// This addresses the issue where go-sqlcmd was using scientific notation
165+
// while ODBC sqlcmd uses decimal notation
166+
s, buf := setupSqlCmdWithMemoryOutput(t)
167+
defer buf.Close()
168+
169+
// Test query with float values from the issue
170+
query := `SELECT
171+
CAST(788991.19988463481 AS FLOAT) as Longitude1,
172+
CAST(4713347.3103808956 AS FLOAT) as Latitude1,
173+
CAST(789288.40771771886 AS FLOAT) as Longitude2,
174+
CAST(4712632.075629076 AS FLOAT) as Latitude2,
175+
CAST(788569.36558582436 AS FLOAT) as Longitude3,
176+
CAST(4714608.0418091472 AS FLOAT) as Latitude3`
177+
178+
err := runSqlCmd(t, s, []string{query, "GO"})
179+
assert.NoError(t, err, "runSqlCmd returned error")
180+
181+
output := buf.buf.String()
182+
183+
// Verify that the output contains decimal notation, not scientific notation
184+
// Scientific notation would look like "4.713347310380896e+06"
185+
// Decimal notation should look like "4713347.3103808956"
186+
assert.NotContains(t, output, "e+", "Output should not contain scientific notation (e+)")
187+
assert.NotContains(t, output, "E+", "Output should not contain scientific notation (E+)")
188+
189+
// Verify that specific expected values are present (allowing for precision differences)
190+
assert.Contains(t, output, "788991.1998846", "Output should contain decimal representation of Longitude1")
191+
assert.Contains(t, output, "4713347.310380", "Output should contain decimal representation of Latitude1")
192+
assert.Contains(t, output, "789288.4077177", "Output should contain decimal representation of Longitude2")
193+
assert.Contains(t, output, "4712632.075629", "Output should contain decimal representation of Latitude2")
194+
assert.Contains(t, output, "788569.3655858", "Output should contain decimal representation of Longitude3")
195+
assert.Contains(t, output, "4714608.041809", "Output should contain decimal representation of Latitude3")
196+
}

0 commit comments

Comments
 (0)