1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-24 15:49:44 +02:00
documize/vendor/github.com/denisenkom/go-mssqldb/tvp_go19_db_test.go
2019-06-25 15:37:19 +01:00

751 lines
18 KiB
Go

// +build go1.9
package mssql
import (
"context"
"database/sql"
"log"
"reflect"
"testing"
"time"
)
const (
crateSchema = `create schema TestTVPSchema;`
dropSchema = `drop schema TestTVPSchema;`
createTVP = `
CREATE TYPE TestTVPSchema.exempleTVP AS TABLE
(
message NVARCHAR(100)
)`
dropTVP = `DROP TYPE TestTVPSchema.exempleTVP;`
procedureWithTVP = `
CREATE PROCEDURE ExecTVP
@param1 TestTVPSchema.exempleTVP READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM @param1;
END;
`
dropProcedure = `drop PROCEDURE ExecTVP`
execTvp = `exec ExecTVP @param1;`
)
type TvptableRow struct {
PBinary []byte `db:"p_binary"`
PVarchar string `db:"p_varchar"`
PVarcharNull *string `db:"p_varcharNull"`
PNvarchar string `db:"p_nvarchar"`
PNvarcharNull *string `db:"p_nvarcharNull"`
PID UniqueIdentifier `db:"p_id"`
PIDNull *UniqueIdentifier `db:"p_idNull"`
PVarbinary []byte `db:"p_varbinary"`
PTinyint int8 `db:"p_tinyint"`
PTinyintNull *int8 `db:"p_tinyintNull"`
PSmallint int16 `db:"p_smallint"`
PSmallintNull *int16 `db:"p_smallintNull"`
PInt int32 `db:"p_int"`
PIntNull *int32 `db:"p_intNull"`
PBigint int64 `db:"p_bigint"`
PBigintNull *int64 `db:"p_bigintNull"`
PBit bool `db:"p_bit"`
PBitNull *bool `db:"p_bitNull"`
PFloat32 float32 `db:"p_float32"`
PFloatNull32 *float32 `db:"p_floatNull32"`
PFloat64 float64 `db:"p_float64"`
PFloatNull64 *float64 `db:"p_floatNull64"`
DTime time.Time `db:"p_timeNull"`
DTimeNull *time.Time `db:"p_time"`
Pint int `db:"pInt"`
PintNull *int `db:"pIntNull"`
}
type TvptableRowWithSkipTag struct {
PBinary []byte `db:"p_binary"`
SkipPBinary []byte `json:"-"`
PVarchar string `db:"p_varchar"`
SkipPVarchar string `tvp:"-"`
PVarcharNull *string `db:"p_varcharNull"`
SkipPVarcharNull *string `json:"-" tvp:"-"`
PNvarchar string `db:"p_nvarchar"`
SkipPNvarchar string `json:"-"`
PNvarcharNull *string `db:"p_nvarcharNull"`
SkipPNvarcharNull *string `json:"-"`
PID UniqueIdentifier `db:"p_id"`
SkipPID UniqueIdentifier `json:"-"`
PIDNull *UniqueIdentifier `db:"p_idNull"`
SkipPIDNull *UniqueIdentifier `tvp:"-"`
PVarbinary []byte `db:"p_varbinary"`
SkipPVarbinary []byte `json:"-" tvp:"-"`
PTinyint int8 `db:"p_tinyint"`
SkipPTinyint int8 `tvp:"-"`
PTinyintNull *int8 `db:"p_tinyintNull"`
SkipPTinyintNull *int8 `tvp:"-" json:"any"`
PSmallint int16 `db:"p_smallint"`
SkipPSmallint int16 `json:"-"`
PSmallintNull *int16 `db:"p_smallintNull"`
SkipPSmallintNull *int16 `tvp:"-"`
PInt int32 `db:"p_int"`
SkipPInt int32 `json:"-"`
PIntNull *int32 `db:"p_intNull"`
SkipPIntNull *int32 `tvp:"-"`
PBigint int64 `db:"p_bigint"`
SkipPBigint int64 `tvp:"-"`
PBigintNull *int64 `db:"p_bigintNull"`
SkipPBigintNull *int64 `json:"any" tvp:"-"`
PBit bool `db:"p_bit"`
SkipPBit bool `json:"-"`
PBitNull *bool `db:"p_bitNull"`
SkipPBitNull *bool `json:"-"`
PFloat32 float32 `db:"p_float32"`
SkipPFloat32 float32 `tvp:"-"`
PFloatNull32 *float32 `db:"p_floatNull32"`
SkipPFloatNull32 *float32 `tvp:"-"`
PFloat64 float64 `db:"p_float64"`
SkipPFloat64 float64 `tvp:"-"`
PFloatNull64 *float64 `db:"p_floatNull64"`
SkipPFloatNull64 *float64 `tvp:"-"`
DTime time.Time `db:"p_timeNull"`
SkipDTime time.Time `tvp:"-"`
DTimeNull *time.Time `db:"p_time"`
SkipDTimeNull *time.Time `tvp:"-"`
Pint int `db:"p_int_null"`
SkipPint int `tvp:"-"`
PintNull *int `db:"p_int_"`
SkipPintNull *int `tvp:"-"`
}
func TestTVP(t *testing.T) {
checkConnStr(t)
SetLogger(testLogger{t})
db, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
t.Fatalf("failed to open driver sqlserver")
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sqltextcreatetable := `
CREATE TYPE tvptable AS TABLE
(
p_binary BINARY(3),
p_varchar VARCHAR(500),
p_varcharNull VARCHAR(500),
p_nvarchar NVARCHAR(100),
p_nvarcharNull NVARCHAR(100),
p_id UNIQUEIDENTIFIER,
p_idNull UNIQUEIDENTIFIER,
p_varbinary VARBINARY(MAX),
p_tinyint TINYINT,
p_tinyintNull TINYINT,
p_smallint SMALLINT,
p_smallintNull SMALLINT,
p_int INT,
p_intNull INT,
p_bigint BIGINT,
p_bigintNull BIGINT,
p_bit BIT,
p_bitNull BIT,
p_float32 FLOAT,
p_floatNull32 FLOAT,
p_float64 FLOAT,
p_floatNull64 FLOAT,
p_time datetime2,
p_timeNull datetime2,
pInt INT,
pIntNull INT
); `
sqltextdroptable := `DROP TYPE tvptable;`
sqltextcreatesp := `
CREATE PROCEDURE spwithtvp
@param1 tvptable READONLY,
@param2 tvptable READONLY,
@param3 NVARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM @param1;
SELECT * FROM @param2;
SELECT @param3;
END;`
sqltextdropsp := `DROP PROCEDURE spwithtvp;`
db.ExecContext(ctx, sqltextdropsp)
db.ExecContext(ctx, sqltextdroptable)
_, err = db.ExecContext(ctx, sqltextcreatetable)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdroptable)
_, err = db.ExecContext(ctx, sqltextcreatesp)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdropsp)
varcharNull := "aaa"
nvarchar := "bbb"
bytesMock := []byte("ddd")
i8 := int8(1)
i16 := int16(2)
i32 := int32(3)
i64 := int64(4)
i := int(5)
bFalse := false
floatValue64 := 0.123
floatValue32 := float32(-10.123)
timeNow := time.Now().UTC()
param1 := []TvptableRow{
{
PBinary: []byte("ccc"),
PVarchar: varcharNull,
PNvarchar: nvarchar,
PID: UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PVarbinary: bytesMock,
PTinyint: i8,
PSmallint: i16,
PInt: i32,
PBigint: i64,
PBit: bFalse,
PFloat32: floatValue32,
PFloat64: floatValue64,
DTime: timeNow,
Pint: 355,
},
{
PBinary: []byte("www"),
PVarchar: "eee",
PNvarchar: "lll",
PID: UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PVarbinary: []byte("zzz"),
PTinyint: 5,
PSmallint: 16000,
PInt: 20000000,
PBigint: 2000000020000000,
PBit: true,
PFloat32: -123.45,
PFloat64: -123.45,
DTime: time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
Pint: 455,
},
{
PBinary: nil,
PVarcharNull: &varcharNull,
PNvarcharNull: &nvarchar,
PIDNull: &UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PTinyintNull: &i8,
PSmallintNull: &i16,
PIntNull: &i32,
PBigintNull: &i64,
PBitNull: &bFalse,
PFloatNull32: &floatValue32,
PFloatNull64: &floatValue64,
DTime: timeNow,
DTimeNull: &timeNow,
PintNull: &i,
},
{
PBinary: []byte("www"),
PVarchar: "eee",
PNvarchar: "lll",
PIDNull: &UniqueIdentifier{},
PVarbinary: []byte("zzz"),
PTinyint: 5,
PSmallint: 16000,
PInt: 20000000,
PBigint: 2000000020000000,
PBit: true,
PFloat64: 123.45,
DTime: time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
PVarcharNull: &varcharNull,
PNvarcharNull: &nvarchar,
PTinyintNull: &i8,
PSmallintNull: &i16,
PIntNull: &i32,
PBigintNull: &i64,
PBitNull: &bFalse,
PFloatNull32: &floatValue32,
PFloatNull64: &floatValue64,
DTimeNull: &timeNow,
PintNull: &i,
},
}
tvpType := TVP{
TypeName: "tvptable",
Value: param1,
}
tvpTypeEmpty := TVP{
TypeName: "tvptable",
Value: []TvptableRow{},
}
rows, err := db.QueryContext(ctx,
"exec spwithtvp @param1, @param2, @param3",
sql.Named("param1", tvpType),
sql.Named("param2", tvpTypeEmpty),
sql.Named("param3", "test"),
)
if err != nil {
t.Fatal(err)
}
var result1 []TvptableRow
for rows.Next() {
var val TvptableRow
err := rows.Scan(
&val.PBinary,
&val.PVarchar,
&val.PVarcharNull,
&val.PNvarchar,
&val.PNvarcharNull,
&val.PID,
&val.PIDNull,
&val.PVarbinary,
&val.PTinyint,
&val.PTinyintNull,
&val.PSmallint,
&val.PSmallintNull,
&val.PInt,
&val.PIntNull,
&val.PBigint,
&val.PBigintNull,
&val.PBit,
&val.PBitNull,
&val.PFloat32,
&val.PFloatNull32,
&val.PFloat64,
&val.PFloatNull64,
&val.DTime,
&val.DTimeNull,
&val.Pint,
&val.PintNull,
)
if err != nil {
t.Fatalf("scan failed with error: %s", err)
}
result1 = append(result1, val)
}
if !reflect.DeepEqual(param1, result1) {
t.Logf("expected: %+v", param1)
t.Logf("actual: %+v", result1)
t.Errorf("first resultset did not match param1")
}
if !rows.NextResultSet() {
t.Errorf("second resultset did not exist")
}
if rows.Next() {
t.Errorf("second resultset was not empty")
}
if !rows.NextResultSet() {
t.Errorf("third resultset did not exist")
}
if !rows.Next() {
t.Errorf("third resultset was empty")
}
var result3 string
if err := rows.Scan(&result3); err != nil {
t.Errorf("error scanning third result set: %s", err)
}
if result3 != "test" {
t.Errorf("third result set had wrong value expected: %s actual: %s", "test", result3)
}
}
func TestTVP_WithTag(t *testing.T) {
checkConnStr(t)
SetLogger(testLogger{t})
db, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
t.Fatalf("failed to open driver sqlserver")
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sqltextcreatetable := `
CREATE TYPE tvptable AS TABLE
(
p_binary BINARY(3),
p_varchar VARCHAR(500),
p_varcharNull VARCHAR(500),
p_nvarchar NVARCHAR(100),
p_nvarcharNull NVARCHAR(100),
p_id UNIQUEIDENTIFIER,
p_idNull UNIQUEIDENTIFIER,
p_varbinary VARBINARY(MAX),
p_tinyint TINYINT,
p_tinyintNull TINYINT,
p_smallint SMALLINT,
p_smallintNull SMALLINT,
p_int INT,
p_intNull INT,
p_bigint BIGINT,
p_bigintNull BIGINT,
p_bit BIT,
p_bitNull BIT,
p_float32 FLOAT,
p_floatNull32 FLOAT,
p_float64 FLOAT,
p_floatNull64 FLOAT,
p_time datetime2,
p_timeNull datetime2,
pInt INT,
pIntNull INT
); `
sqltextdroptable := `DROP TYPE tvptable;`
sqltextcreatesp := `
CREATE PROCEDURE spwithtvp
@param1 tvptable READONLY,
@param2 tvptable READONLY,
@param3 NVARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM @param1;
SELECT * FROM @param2;
SELECT @param3;
END;`
sqltextdropsp := `DROP PROCEDURE spwithtvp;`
db.ExecContext(ctx, sqltextdropsp)
db.ExecContext(ctx, sqltextdroptable)
_, err = db.ExecContext(ctx, sqltextcreatetable)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdroptable)
_, err = db.ExecContext(ctx, sqltextcreatesp)
if err != nil {
t.Fatal(err)
}
defer db.ExecContext(ctx, sqltextdropsp)
varcharNull := "aaa"
nvarchar := "bbb"
bytesMock := []byte("ddd")
i8 := int8(1)
i16 := int16(2)
i32 := int32(3)
i64 := int64(4)
i := int(355)
bFalse := false
floatValue64 := 0.123
floatValue32 := float32(-10.123)
timeNow := time.Now().UTC()
param1 := []TvptableRowWithSkipTag{
{
PBinary: []byte("ccc"),
PVarchar: varcharNull,
PNvarchar: nvarchar,
PID: UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PVarbinary: bytesMock,
PTinyint: i8,
PSmallint: i16,
PInt: i32,
PBigint: i64,
PBit: bFalse,
PFloat32: floatValue32,
PFloat64: floatValue64,
DTime: timeNow,
Pint: i,
PintNull: &i,
},
{
PBinary: []byte("www"),
PVarchar: "eee",
PNvarchar: "lll",
PID: UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PVarbinary: []byte("zzz"),
PTinyint: 5,
PSmallint: 16000,
PInt: 20000000,
PBigint: 2000000020000000,
PBit: true,
PFloat32: -123.45,
PFloat64: -123.45,
DTime: time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
Pint: 3669,
PintNull: &i,
},
{
PBinary: nil,
PVarcharNull: &varcharNull,
PNvarcharNull: &nvarchar,
PIDNull: &UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
PTinyintNull: &i8,
PSmallintNull: &i16,
PIntNull: &i32,
PBigintNull: &i64,
PBitNull: &bFalse,
PFloatNull32: &floatValue32,
PFloatNull64: &floatValue64,
DTime: timeNow,
DTimeNull: &timeNow,
Pint: 969,
},
{
PBinary: []byte("www"),
PVarchar: "eee",
PNvarchar: "lll",
PIDNull: &UniqueIdentifier{},
PVarbinary: []byte("zzz"),
PTinyint: 5,
PSmallint: 16000,
PInt: 20000000,
PBigint: 2000000020000000,
PBit: true,
PFloat64: 123.45,
DTime: time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
PVarcharNull: &varcharNull,
PNvarcharNull: &nvarchar,
PTinyintNull: &i8,
PSmallintNull: &i16,
PIntNull: &i32,
PBigintNull: &i64,
PBitNull: &bFalse,
PFloatNull32: &floatValue32,
PFloatNull64: &floatValue64,
DTimeNull: &timeNow,
PintNull: &i,
},
}
tvpType := TVP{
TypeName: "tvptable",
Value: param1,
}
tvpTypeEmpty := TVP{
TypeName: "tvptable",
Value: []TvptableRowWithSkipTag{},
}
rows, err := db.QueryContext(ctx,
"exec spwithtvp @param1, @param2, @param3",
sql.Named("param1", tvpType),
sql.Named("param2", tvpTypeEmpty),
sql.Named("param3", "test"),
)
if err != nil {
t.Fatal(err)
}
var result1 []TvptableRowWithSkipTag
for rows.Next() {
var val TvptableRowWithSkipTag
err := rows.Scan(
&val.PBinary,
&val.PVarchar,
&val.PVarcharNull,
&val.PNvarchar,
&val.PNvarcharNull,
&val.PID,
&val.PIDNull,
&val.PVarbinary,
&val.PTinyint,
&val.PTinyintNull,
&val.PSmallint,
&val.PSmallintNull,
&val.PInt,
&val.PIntNull,
&val.PBigint,
&val.PBigintNull,
&val.PBit,
&val.PBitNull,
&val.PFloat32,
&val.PFloatNull32,
&val.PFloat64,
&val.PFloatNull64,
&val.DTime,
&val.DTimeNull,
&val.Pint,
&val.PintNull,
)
if err != nil {
t.Fatalf("scan failed with error: %s", err)
}
result1 = append(result1, val)
}
if !reflect.DeepEqual(param1, result1) {
t.Logf("expected: %+v", param1)
t.Logf("actual: %+v", result1)
t.Errorf("first resultset did not match param1")
}
if !rows.NextResultSet() {
t.Errorf("second resultset did not exist")
}
if rows.Next() {
t.Errorf("second resultset was not empty")
}
if !rows.NextResultSet() {
t.Errorf("third resultset did not exist")
}
if !rows.Next() {
t.Errorf("third resultset was empty")
}
var result3 string
if err := rows.Scan(&result3); err != nil {
t.Errorf("error scanning third result set: %s", err)
}
if result3 != "test" {
t.Errorf("third result set had wrong value expected: %s actual: %s", "test", result3)
}
}
type TvpExample struct {
Message string
}
func TestTVPSchema(t *testing.T) {
checkConnStr(t)
SetLogger(testLogger{t})
conn, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
log.Fatal("Open connection failed:", err.Error())
}
defer conn.Close()
_, err = conn.Exec(crateSchema)
if err != nil {
log.Println(err)
return
}
defer conn.Exec(dropSchema)
_, err = conn.Exec(createTVP)
if err != nil {
log.Println(err)
return
}
defer conn.Exec(dropTVP)
_, err = conn.Exec(procedureWithTVP)
if err != nil {
log.Println(err)
return
}
defer conn.Exec(dropProcedure)
exempleData := []TvpExample{
{
Message: "Hello",
},
{
Message: "World",
},
{
Message: "TVP",
},
}
tvpType := TVP{
TypeName: "exempleTVP",
Value: exempleData,
}
rows, err := conn.Query(execTvp,
sql.Named("param1", tvpType),
)
if err != nil {
log.Println(err)
return
}
tvpResult := make([]TvpExample, 0)
for rows.Next() {
tvpExemple := TvpExample{}
err = rows.Scan(&tvpExemple.Message)
if err != nil {
log.Println(err)
return
}
tvpResult = append(tvpResult, tvpExemple)
}
log.Println(tvpResult)
}
func TestTVPObject(t *testing.T) {
checkConnStr(t)
SetLogger(testLogger{t})
conn, err := sql.Open("sqlserver", makeConnStr(t).String())
if err != nil {
log.Fatal("Open connection failed:", err.Error())
}
defer conn.Close()
tests := []struct {
name string
tvp TVP
wantErr bool
}{
{
name: "empty name",
wantErr: true,
tvp: TVP{TypeName: ""},
},
{
name: "value is wrong type",
wantErr: true,
tvp: TVP{TypeName: "type", Value: "wrong type"},
},
{
name: "tvp type is wrong",
wantErr: true,
tvp: TVP{TypeName: "[type", Value: []TvpExample{{}}},
},
{
name: "tvp type is wrong",
wantErr: true,
tvp: TVP{TypeName: "[type", Value: []TestFieldsUnsupportedTypes{{}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := conn.Exec("somequery", tt.tvp)
if (err != nil) != tt.wantErr {
t.Errorf("TVP.encode() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}