Skip to content

Commit

Permalink
Fix: support money/smallmoney types for bulkcopy
Browse files Browse the repository at this point in the history
  • Loading branch information
dlapko committed Mar 4, 2025
1 parent a91360d commit 6babea6
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 5 deletions.
69 changes: 68 additions & 1 deletion bulkcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,74 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error)
err = fmt.Errorf("mssql: invalid type for time column: %T %s", val, val)
return
}
// case typeMoney, typeMoney4, typeMoneyN:
case typeMoney, typeMoney4, typeMoneyN:
var intvalue int64

string2Int64 := func(str string) (int64, error) {
// Split on decimal point
parts := strings.Split(str, ".")
if len(parts) > 2 {
return 0, fmt.Errorf("invalid money format")
}

// Handle the decimal places
if len(parts) == 2 {
// Pad or truncate decimal places to exactly 4 digits
decimal := parts[1]
if len(decimal) > 4 {
decimal = decimal[:4] // truncate to 4 decimal places
} else {
decimal = decimal + strings.Repeat("0", 4-len(decimal)) // pad with zeros
}
str = parts[0] + decimal
} else {
// No decimal point, append 4 zeros
str = str + "0000"
}

return strconv.ParseInt(str, 10, 64)
}

switch val := val.(type) {
case int:
intvalue = int64(val)
case int64:
intvalue = val
case []byte:
intvalue, err = string2Int64(string(val))
if err != nil {
return res, fmt.Errorf("mssql: invalid money string format: %s", string(val))
}
case string:
intvalue, err = string2Int64(val)
if err != nil {
return res, fmt.Errorf("mssql: invalid money string format: %s", val)
}
default:
err = fmt.Errorf("mssql: invalid type for money column: %T %s", val, val)
return
}

res.buffer = make([]byte, res.ti.Size)

// smallmoney is a 4-byte integer stored as value * 10^4.
// money is an 8-byte integer stored as value * 10^4.
//
// https://learn.microsoft.com/openspecs/windows_protocols/ms-tds/1266679d-cd6e-492a-b2b2-3a9ba004196d
switch col.ti.Size {
case 4:
binary.LittleEndian.PutUint32(res.buffer, uint32(intvalue))
case 8:
// The 8-byte signed integer is represented in the following sequence:
// - One 4-byte integer that represents the more significant half.
// - One 4-byte integer that represents the less significant half.
//
// https://learn.microsoft.com/openspecs/windows_protocols/ms-tds/1266679d-cd6e-492a-b2b2-3a9ba004196d
binary.LittleEndian.PutUint32(res.buffer[0:4], uint32(intvalue>>32))
binary.LittleEndian.PutUint32(res.buffer[4:8], uint32(intvalue&0xFFFFFFFF))
default:
err = fmt.Errorf("mssql: invalid size of column %d", col.ti.Size)
}
case typeDecimal, typeDecimalN, typeNumeric, typeNumericN:
prec := col.ti.Prec
scale := col.ti.Scale
Expand Down
12 changes: 8 additions & 4 deletions bulkcopy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ func testBulkcopy(t *testing.T, guidConversion bool) {
{"test_nullint32", sql.NullInt32{2147483647, true}, 2147483647},
{"test_nullint16", sql.NullInt16{32767, true}, 32767},
{"test_nulltime", sql.NullTime{time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC), true}, time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
// {"test_smallmoney", 1234.56, nil},
// {"test_money", 1234.56, nil},
{"test_smallmoney", []byte("1234.5600"), nil},
{"test_smallmoneyn", nil, nil},
{"test_money", []byte("1234.5600"), nil},
{"test_moneyn", nil, nil},
{"test_decimal_18_0", 1234.0001, "1234"},
{"test_decimal_9_2", -1234.560001, "-1234.56"},
{"test_decimal_20_0", 1234, "1234"},
Expand Down Expand Up @@ -407,8 +409,10 @@ func setupTable(ctx context.Context, t *testing.T, conn *sql.Conn, tableName str
[test_date_2] [date] NULL,
[test_time] [time](7) NULL,
[test_time_2] [time](7) NULL,
[test_smallmoney] [smallmoney] NULL,
[test_money] [money] NULL,
[test_smallmoney] [smallmoney] NOT NULL,
[test_smallmoneyn] [smallmoney] NULL,
[test_money] [money] NOT NULL,
[test_moneyn] [money] NULL,
[test_tinyint] [tinyint] NULL,
[test_smallint] [smallint] NOT NULL,
[test_smallintn] [smallint] NULL,
Expand Down

0 comments on commit 6babea6

Please sign in to comment.