diff --git a/conn_test.go b/conn_test.go index af0c730..3bc96ac 100644 --- a/conn_test.go +++ b/conn_test.go @@ -21,6 +21,7 @@ create table freetds_types ( long bigint null, smallint smallint null, tinyint tinyint null, + numeric numeric(5,3) null, varchar varchar(255) COLLATE latin1_general_ci_as null , nvarchar nvarchar(255) COLLATE latin1_general_ci_as null, char char(255) COLLATE latin1_general_ci_as null, @@ -42,11 +43,11 @@ create table freetds_types ( ) ; -insert into freetds_types (int, long, smallint, tinyint, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) -values (2147483647, 9223372036854775807, 32767, 255, 'išo medo u dućan ',N'išo medo u dućan 2','išo medo u dućan 3',N'išo medo u dućan 4','išo medo u dućan 5',N'išo medo u dućan 6', '1972-08-08T10:11:12','1972-08-08T10:11:12', 1234.5678, 1234.5678, 1234.5678, 1234.5678, 0, 0x123567890) +insert into freetds_types (int, long, smallint, tinyint, numeric, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) +values (2147483647, 9223372036854775807, 32767, 255, 99.999, 'išo medo u dućan ',N'išo medo u dućan 2','išo medo u dućan 3',N'išo medo u dućan 4','išo medo u dućan 5',N'išo medo u dućan 6', '1972-08-08T10:11:12','1972-08-08T10:11:12', 1234.5678, 1234.5678, 1234.5678, 1234.5678, 0, 0x123567890) -insert into freetds_types (int, long, smallint, tinyint, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) -values (-2147483648, -9223372036854775808, -32768, 0, 'nije reko dobar dan',N'nije reko dobar dan 2','nije reko dobar dan 3',N'nije reko dobar dan 4','nije reko dobar dan 5',N'nije reko dobar dan 6', '1998-10-10T16:17:18','1998-10-10T16:17:18', -1234.5678, -1234.5678, -1234.5678, -1234.5678, 1, 0x0987654321abcd) +insert into freetds_types (int, long, smallint, tinyint, numeric, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) +values (-2147483648, -9223372036854775808, -32768, 0, -99.999, 'nije reko dobar dan',N'nije reko dobar dan 2','nije reko dobar dan 3',N'nije reko dobar dan 4','nije reko dobar dan 5',N'nije reko dobar dan 6', '1998-10-10T16:17:18','1998-10-10T16:17:18', -1234.5678, -1234.5678, -1234.5678, -1234.5678, 1, 0x0987654321abcd) insert into freetds_types (int) values (3) `, ` @@ -74,6 +75,7 @@ create table freetds_types ( long decimal(19,0) null, smallint smallint null, tinyint tinyint null, + numeric numeric(5,3) null, varchar varchar(255) null , nvarchar nvarchar(255) null, char char(255) null, @@ -95,11 +97,11 @@ create table freetds_types ( ) -insert into freetds_types (int, long, smallint, tinyint, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) -values (2147483647, 9223372036854775807, 32767, 255, 'išo medo u dućan ',N'išo medo u dućan 2','išo medo u dućan 3',N'išo medo u dućan 4','išo medo u dućan 5',N'išo medo u dućan 6', '1972-08-08 10:11:12','1972-08-08 10:11:12', 1234.5678, 1234.5678, 1234.5678, 1234.5678, 0, 0x123567890) +insert into freetds_types (int, long, smallint, tinyint, numeric, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) +values (2147483647, 9223372036854775807, 32767, 255, 99.999, 'išo medo u dućan ',N'išo medo u dućan 2','išo medo u dućan 3',N'išo medo u dućan 4','išo medo u dućan 5',N'išo medo u dućan 6', '1972-08-08 10:11:12','1972-08-08 10:11:12', 1234.5678, 1234.5678, 1234.5678, 1234.5678, 0, 0x123567890) -insert into freetds_types (int, long, smallint, tinyint, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) -values (-2147483648, -9223372036854775808, -32768, 0, 'nije reko dobar dan',N'nije reko dobar dan 2','nije reko dobar dan 3',N'nije reko dobar dan 4','nije reko dobar dan 5',N'nije reko dobar dan 6', '1998-10-10 16:17:18','1998-10-10 16:17:18', -1234.5678, -1234.5678, -1234.5678, -1234.5678, 1, 0x0987654321abcd) +insert into freetds_types (int, long, smallint, tinyint, numeric, varchar, nvarchar, char, nchar, text, ntext, datetime, smalldatetime, money, smallmoney, real, float, bit, binary) +values (-2147483648, -9223372036854775808, -32768, 0, -99.999, 'nije reko dobar dan',N'nije reko dobar dan 2','nije reko dobar dan 3',N'nije reko dobar dan 4','nije reko dobar dan 5',N'nije reko dobar dan 6', '1998-10-10 16:17:18','1998-10-10 16:17:18', -1234.5678, -1234.5678, -1234.5678, -1234.5678, 1, 0x0987654321abcd) insert into freetds_types (int, bit) values (3, 0) `, ` diff --git a/executesql.go b/executesql.go index 7c313be..163107e 100644 --- a/executesql.go +++ b/executesql.go @@ -114,6 +114,8 @@ func go2SqlDataType(value interface{}) (string, string, error) { strValue := fmt.Sprintf("%v", value) switch t := value.(type) { + case nil: + return "nvarchar (1)", "NULL", nil case bool: bitStrValue := "0" if strValue == "true" { diff --git a/executesql_test.go b/executesql_test.go index 428749f..d93e1d3 100644 --- a/executesql_test.go +++ b/executesql_test.go @@ -82,6 +82,7 @@ func TestGoTo2SqlDataType(t *testing.T) { checker([]byte{1, 2, 3, 4, 5, 6, 7, 8}, "varbinary (8)", "0x0102030405060708") checker("", "nvarchar (1)", "''") + checker(nil, "nvarchar (1)", "NULL") checker(true, "bit", "1") checker(false, "bit", "0") } diff --git a/mssql_test.go b/mssql_test.go index e18f55e..67d78f3 100644 --- a/mssql_test.go +++ b/mssql_test.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "os" @@ -44,7 +45,6 @@ func TestMssqlConnOpenSybase125(t *testing.T) { c.Close() } - func TestGoSqlDbQueryRow(t *testing.T) { db, err, _ := open(t) defer db.Close() @@ -240,3 +240,261 @@ func TestBlobs(t *testing.T) { assert.Equal(t, strWant, strGot) assert.Equal(t, want, got) } + +var insertQuery = ` +insert into freetds_types ( + int, + long, + smallint, + tinyint, + numeric, + varchar, + nvarchar, + char, + nchar, + text, + ntext, + datetime, + smalldatetime, + money, + smallmoney, + real, + float, + bit, + -- We cannot insert NULL for timestamp, binary, and varbinary_max + -- timestamp, + -- binary, + nvarchar_max, + varchar_max + -- varbinary_max +) values ( + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ?, + ? +) +` + +func TestNullInserts(t *testing.T) { + db, _, _ := open(t) + defer db.Close() + + // First clear out the nil one(s) + _, err := db.Exec(`delete from freetds_types where int is null`) + assert.Nil(t, err) + + _, err = db.Exec(insertQuery, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + // Because sybase 12.5 does not allow null bit, making it non-null to resort to lowest common denominator. + true, + nil, nil) + assert.Nil(t, err) + + rows, err := db.Query("select * from freetds_types where int is null") + defer rows.Close() + assert.Nil(t, err) + assertRowNil(t, rows) +} + +func TestSQLNullInserts(t *testing.T) { + db, _, _ := open(t) + defer db.Close() + + // First clear out the nil one(s) + _, err := db.Exec(`delete from freetds_types where int is null`) + assert.Nil(t, err) + + _, err = db.Exec(insertQuery, + // sql.NullInt32{Valid: true, Int32: 100}, + sql.NullInt32{}, + sql.NullInt64{}, + sql.NullInt32{}, + sql.NullInt32{}, + sql.NullFloat64{}, + sql.NullString{}, + sql.NullString{}, + sql.NullString{}, + sql.NullString{}, + sql.NullString{}, + sql.NullString{}, + sql.NullTime{}, + sql.NullTime{}, + sql.NullFloat64{}, + sql.NullFloat64{}, + sql.NullFloat64{}, + sql.NullFloat64{}, + // Because sybase 12.5 does not allow null bit, making it non-null to resort to lowest common denominator. + sql.NullBool{Valid: true, Bool: true}, + sql.NullString{}, + sql.NullString{}, + ) + assert.Nil(t, err) + + rows, err := db.Query("select * from freetds_types where int is null") + defer rows.Close() + assert.Nil(t, err) + assertRowNil(t, rows) +} + +func TestTypedNilNullInserts(t *testing.T) { + db, _, _ := open(t) + defer db.Close() + + // First clear out the nil one(s) + _, err := db.Exec(`delete from freetds_types where int is null`) + assert.Nil(t, err) + + var ( + myInt *int32 + myLong *int64 + mySmallInt *int16 + myTinyInt *int8 + myNumeric *float64 + myVarchar *string + myNVarchar *string + myChar *byte + myNChar *[]byte + myText *string + myNText *string + myDateTime *time.Time + mySmallDateTime *time.Time + myMoney *float64 + mySmallMoney *float64 + myReal *float64 + myFloat *float64 + myBit *bool + myNVarcharMax *string + myVarcharMax *string + ) + // Because sybase 12.5 does not allow null bit, making it non-null to resort to lowest common denominator. + myTrue := true + myBit = &myTrue + _, err = db.Exec(insertQuery, + myInt, + myLong, + mySmallInt, + myTinyInt, + myNumeric, + myVarchar, + myNVarchar, + myChar, + myNChar, + myText, + myNText, + myDateTime, + mySmallDateTime, + myMoney, + mySmallMoney, + myReal, + myFloat, + myBit, + myNVarcharMax, + myVarcharMax, + ) + assert.Nil(t, err) + + rows, err := db.Query("select * from freetds_types where int is null") + defer rows.Close() + assert.Nil(t, err) + assertRowNil(t, rows) +} + +func assertRowNil(t *testing.T, rows *sql.Rows) { + rowCount := 0 + for rows.Next() { + rowCount++ + var ( + myInt *int32 + myLong *int64 + mySmallInt *int16 + myTinyInt *int8 + myNumeric *float64 + myVarchar *string + myNVarchar *string + myChar *byte + myNChar *[]byte + myText *string + myNText *string + myDateTime *time.Time + mySmallDateTime *time.Time + myMoney *float64 + mySmallMoney *float64 + myReal *float64 + myFloat *float64 + myBit *bool + myTimestamp *[]byte + myBinary *[]byte + myNVarcharMax *string + myVarcharMax *string + myVarBinaryMax *string + ) + + err := rows.Scan( + &myInt, + &myLong, + &mySmallInt, + &myTinyInt, + &myNumeric, + &myVarchar, + &myNVarchar, + &myChar, + &myNChar, + &myText, + &myNText, + &myDateTime, + &mySmallDateTime, + &myMoney, + &mySmallMoney, + &myReal, + &myFloat, + &myBit, + &myTimestamp, + &myBinary, + &myNVarcharMax, + &myVarcharMax, + &myVarBinaryMax, + ) + assert.Nil(t, err) + + assert.Nil(t, myInt) + assert.Nil(t, myLong) + assert.Nil(t, mySmallInt) + assert.Nil(t, myTinyInt) + assert.Nil(t, myNumeric) + assert.Nil(t, myVarchar) + assert.Nil(t, myChar) + assert.Nil(t, myNChar) + assert.Nil(t, myText) + assert.Nil(t, myNText) + assert.Nil(t, myDateTime) + assert.Nil(t, mySmallDateTime) + assert.Nil(t, myMoney) + assert.Nil(t, myReal) + assert.Nil(t, myFloat) + // Because sybase 12.5 does not allow null bit, we made it non-null to resort to lowest common denominator. + assert.True(t, *myBit) + + assert.Nil(t, myBinary) + assert.Nil(t, myNVarcharMax) + assert.Nil(t, myVarcharMax) + assert.Nil(t, myVarBinaryMax) + assert.Nil(t, err) + } + + assert.Equal(t, rowCount, 1) +}