1// +build go1.9
2
3package mssql
4
5import (
6	"context"
7	"database/sql"
8	"log"
9	"reflect"
10	"testing"
11	"time"
12)
13
14const (
15	crateSchema = `create schema TestTVPSchema;`
16
17	dropSchema = `drop schema TestTVPSchema;`
18
19	createTVP = `
20		CREATE TYPE TestTVPSchema.exempleTVP AS TABLE
21		(
22			message	NVARCHAR(100)
23		)`
24
25	dropTVP = `DROP TYPE TestTVPSchema.exempleTVP;`
26
27	procedureWithTVP = `
28	CREATE PROCEDURE ExecTVP
29		@param1 TestTVPSchema.exempleTVP READONLY
30	AS
31	BEGIN
32		SET NOCOUNT ON;
33		SELECT * FROM @param1;
34	END;
35	`
36
37	dropProcedure = `drop PROCEDURE ExecTVP`
38
39	execTvp = `exec ExecTVP @param1;`
40)
41
42type TvptableRow struct {
43	PBinary       []byte            `db:"p_binary"`
44	PVarchar      string            `db:"p_varchar"`
45	PVarcharNull  *string           `db:"p_varcharNull"`
46	PNvarchar     string            `db:"p_nvarchar"`
47	PNvarcharNull *string           `db:"p_nvarcharNull"`
48	PID           UniqueIdentifier  `db:"p_id"`
49	PIDNull       *UniqueIdentifier `db:"p_idNull"`
50	PVarbinary    []byte            `db:"p_varbinary"`
51	PTinyint      int8              `db:"p_tinyint"`
52	PTinyintNull  *int8             `db:"p_tinyintNull"`
53	PSmallint     int16             `db:"p_smallint"`
54	PSmallintNull *int16            `db:"p_smallintNull"`
55	PInt          int32             `db:"p_int"`
56	PIntNull      *int32            `db:"p_intNull"`
57	PBigint       int64             `db:"p_bigint"`
58	PBigintNull   *int64            `db:"p_bigintNull"`
59	PBit          bool              `db:"p_bit"`
60	PBitNull      *bool             `db:"p_bitNull"`
61	PFloat32      float32           `db:"p_float32"`
62	PFloatNull32  *float32          `db:"p_floatNull32"`
63	PFloat64      float64           `db:"p_float64"`
64	PFloatNull64  *float64          `db:"p_floatNull64"`
65	DTime         time.Time         `db:"p_timeNull"`
66	DTimeNull     *time.Time        `db:"p_time"`
67	Pint          int               `db:"pInt"`
68	PintNull      *int              `db:"pIntNull"`
69}
70
71type TvptableRowWithSkipTag struct {
72	PBinary           []byte            `db:"p_binary"`
73	SkipPBinary       []byte            `json:"-"`
74	PVarchar          string            `db:"p_varchar"`
75	SkipPVarchar      string            `tvp:"-"`
76	PVarcharNull      *string           `db:"p_varcharNull"`
77	SkipPVarcharNull  *string           `json:"-" tvp:"-"`
78	PNvarchar         string            `db:"p_nvarchar"`
79	SkipPNvarchar     string            `json:"-"`
80	PNvarcharNull     *string           `db:"p_nvarcharNull"`
81	SkipPNvarcharNull *string           `json:"-"`
82	PID               UniqueIdentifier  `db:"p_id"`
83	SkipPID           UniqueIdentifier  `json:"-"`
84	PIDNull           *UniqueIdentifier `db:"p_idNull"`
85	SkipPIDNull       *UniqueIdentifier `tvp:"-"`
86	PVarbinary        []byte            `db:"p_varbinary"`
87	SkipPVarbinary    []byte            `json:"-" tvp:"-"`
88	PTinyint          int8              `db:"p_tinyint"`
89	SkipPTinyint      int8              `tvp:"-"`
90	PTinyintNull      *int8             `db:"p_tinyintNull"`
91	SkipPTinyintNull  *int8             `tvp:"-" json:"any"`
92	PSmallint         int16             `db:"p_smallint"`
93	SkipPSmallint     int16             `json:"-"`
94	PSmallintNull     *int16            `db:"p_smallintNull"`
95	SkipPSmallintNull *int16            `tvp:"-"`
96	PInt              int32             `db:"p_int"`
97	SkipPInt          int32             `json:"-"`
98	PIntNull          *int32            `db:"p_intNull"`
99	SkipPIntNull      *int32            `tvp:"-"`
100	PBigint           int64             `db:"p_bigint"`
101	SkipPBigint       int64             `tvp:"-"`
102	PBigintNull       *int64            `db:"p_bigintNull"`
103	SkipPBigintNull   *int64            `json:"any" tvp:"-"`
104	PBit              bool              `db:"p_bit"`
105	SkipPBit          bool              `json:"-"`
106	PBitNull          *bool             `db:"p_bitNull"`
107	SkipPBitNull      *bool             `json:"-"`
108	PFloat32          float32           `db:"p_float32"`
109	SkipPFloat32      float32           `tvp:"-"`
110	PFloatNull32      *float32          `db:"p_floatNull32"`
111	SkipPFloatNull32  *float32          `tvp:"-"`
112	PFloat64          float64           `db:"p_float64"`
113	SkipPFloat64      float64           `tvp:"-"`
114	PFloatNull64      *float64          `db:"p_floatNull64"`
115	SkipPFloatNull64  *float64          `tvp:"-"`
116	DTime             time.Time         `db:"p_timeNull"`
117	SkipDTime         time.Time         `tvp:"-"`
118	DTimeNull         *time.Time        `db:"p_time"`
119	SkipDTimeNull     *time.Time        `tvp:"-"`
120	Pint              int               `db:"p_int_null"`
121	SkipPint          int               `tvp:"-"`
122	PintNull          *int              `db:"p_int_"`
123	SkipPintNull      *int              `tvp:"-"`
124}
125
126func TestTVP(t *testing.T) {
127	checkConnStr(t)
128	SetLogger(testLogger{t})
129
130	db, err := sql.Open("sqlserver", makeConnStr(t).String())
131	if err != nil {
132		t.Fatalf("failed to open driver sqlserver")
133	}
134	defer db.Close()
135
136	ctx, cancel := context.WithCancel(context.Background())
137	defer cancel()
138
139	sqltextcreatetable := `
140		CREATE TYPE tvptable AS TABLE
141		(
142			p_binary 			BINARY(3),
143			p_varchar 			VARCHAR(500),
144			p_varcharNull 		VARCHAR(500),
145			p_nvarchar 			NVARCHAR(100),
146			p_nvarcharNull 		NVARCHAR(100),
147			p_id 				UNIQUEIDENTIFIER,
148			p_idNull 			UNIQUEIDENTIFIER,
149			p_varbinary 		VARBINARY(MAX),
150			p_tinyint 			TINYINT,
151			p_tinyintNull 		TINYINT,
152			p_smallint          SMALLINT,
153			p_smallintNull      SMALLINT,
154			p_int               INT,
155			p_intNull           INT,
156			p_bigint            BIGINT,
157			p_bigintNull        BIGINT,
158			p_bit               BIT,
159			p_bitNull           BIT,
160			p_float32           FLOAT,
161			p_floatNull32       FLOAT,
162			p_float64           FLOAT,
163			p_floatNull64       FLOAT,
164			p_time 				datetime2,
165			p_timeNull			datetime2,
166			pInt              	INT,
167			pIntNull          	INT
168		); `
169
170	sqltextdroptable := `DROP TYPE tvptable;`
171
172	sqltextcreatesp := `
173	CREATE PROCEDURE spwithtvp
174		@param1 tvptable READONLY,
175		@param2 tvptable READONLY,
176		@param3 NVARCHAR(10)
177	AS
178	BEGIN
179		SET NOCOUNT ON;
180		SELECT * FROM @param1;
181		SELECT * FROM @param2;
182		SELECT @param3;
183	END;`
184
185	sqltextdropsp := `DROP PROCEDURE spwithtvp;`
186
187	db.ExecContext(ctx, sqltextdropsp)
188	db.ExecContext(ctx, sqltextdroptable)
189
190	_, err = db.ExecContext(ctx, sqltextcreatetable)
191	if err != nil {
192		t.Fatal(err)
193	}
194	defer db.ExecContext(ctx, sqltextdroptable)
195
196	_, err = db.ExecContext(ctx, sqltextcreatesp)
197	if err != nil {
198		t.Fatal(err)
199	}
200	defer db.ExecContext(ctx, sqltextdropsp)
201	varcharNull := "aaa"
202	nvarchar := "bbb"
203	bytesMock := []byte("ddd")
204	i8 := int8(1)
205	i16 := int16(2)
206	i32 := int32(3)
207	i64 := int64(4)
208	i := int(5)
209	bFalse := false
210	floatValue64 := 0.123
211	floatValue32 := float32(-10.123)
212	timeNow := time.Now().UTC()
213	param1 := []TvptableRow{
214		{
215			PBinary:    []byte("ccc"),
216			PVarchar:   varcharNull,
217			PNvarchar:  nvarchar,
218			PID:        UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
219			PVarbinary: bytesMock,
220			PTinyint:   i8,
221			PSmallint:  i16,
222			PInt:       i32,
223			PBigint:    i64,
224			PBit:       bFalse,
225			PFloat32:   floatValue32,
226			PFloat64:   floatValue64,
227			DTime:      timeNow,
228			Pint:       355,
229		},
230		{
231			PBinary:    []byte("www"),
232			PVarchar:   "eee",
233			PNvarchar:  "lll",
234			PID:        UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
235			PVarbinary: []byte("zzz"),
236			PTinyint:   5,
237			PSmallint:  16000,
238			PInt:       20000000,
239			PBigint:    2000000020000000,
240			PBit:       true,
241			PFloat32:   -123.45,
242			PFloat64:   -123.45,
243			DTime:      time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
244			Pint:       455,
245		},
246		{
247			PBinary:       nil,
248			PVarcharNull:  &varcharNull,
249			PNvarcharNull: &nvarchar,
250			PIDNull:       &UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
251			PTinyintNull:  &i8,
252			PSmallintNull: &i16,
253			PIntNull:      &i32,
254			PBigintNull:   &i64,
255			PBitNull:      &bFalse,
256			PFloatNull32:  &floatValue32,
257			PFloatNull64:  &floatValue64,
258			DTime:         timeNow,
259			DTimeNull:     &timeNow,
260			PintNull:      &i,
261		},
262		{
263			PBinary:       []byte("www"),
264			PVarchar:      "eee",
265			PNvarchar:     "lll",
266			PIDNull:       &UniqueIdentifier{},
267			PVarbinary:    []byte("zzz"),
268			PTinyint:      5,
269			PSmallint:     16000,
270			PInt:          20000000,
271			PBigint:       2000000020000000,
272			PBit:          true,
273			PFloat64:      123.45,
274			DTime:         time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
275			PVarcharNull:  &varcharNull,
276			PNvarcharNull: &nvarchar,
277			PTinyintNull:  &i8,
278			PSmallintNull: &i16,
279			PIntNull:      &i32,
280			PBigintNull:   &i64,
281			PBitNull:      &bFalse,
282			PFloatNull32:  &floatValue32,
283			PFloatNull64:  &floatValue64,
284			DTimeNull:     &timeNow,
285			PintNull:      &i,
286		},
287	}
288
289	tvpType := TVP{
290		TypeName: "tvptable",
291		Value:    param1,
292	}
293	tvpTypeEmpty := TVP{
294		TypeName: "tvptable",
295		Value:    []TvptableRow{},
296	}
297
298	rows, err := db.QueryContext(ctx,
299		"exec spwithtvp @param1, @param2, @param3",
300		sql.Named("param1", tvpType),
301		sql.Named("param2", tvpTypeEmpty),
302		sql.Named("param3", "test"),
303	)
304
305	if err != nil {
306		t.Fatal(err)
307	}
308
309	var result1 []TvptableRow
310	for rows.Next() {
311		var val TvptableRow
312		err := rows.Scan(
313			&val.PBinary,
314			&val.PVarchar,
315			&val.PVarcharNull,
316			&val.PNvarchar,
317			&val.PNvarcharNull,
318			&val.PID,
319			&val.PIDNull,
320			&val.PVarbinary,
321			&val.PTinyint,
322			&val.PTinyintNull,
323			&val.PSmallint,
324			&val.PSmallintNull,
325			&val.PInt,
326			&val.PIntNull,
327			&val.PBigint,
328			&val.PBigintNull,
329			&val.PBit,
330			&val.PBitNull,
331			&val.PFloat32,
332			&val.PFloatNull32,
333			&val.PFloat64,
334			&val.PFloatNull64,
335			&val.DTime,
336			&val.DTimeNull,
337			&val.Pint,
338			&val.PintNull,
339		)
340		if err != nil {
341			t.Fatalf("scan failed with error: %s", err)
342		}
343
344		result1 = append(result1, val)
345	}
346
347	if !reflect.DeepEqual(param1, result1) {
348		t.Logf("expected: %+v", param1)
349		t.Logf("actual: %+v", result1)
350		t.Errorf("first resultset did not match param1")
351	}
352
353	if !rows.NextResultSet() {
354		t.Errorf("second resultset did not exist")
355	}
356
357	if rows.Next() {
358		t.Errorf("second resultset was not empty")
359	}
360
361	if !rows.NextResultSet() {
362		t.Errorf("third resultset did not exist")
363	}
364
365	if !rows.Next() {
366		t.Errorf("third resultset was empty")
367	}
368
369	var result3 string
370	if err := rows.Scan(&result3); err != nil {
371		t.Errorf("error scanning third result set: %s", err)
372	}
373	if result3 != "test" {
374		t.Errorf("third result set had wrong value expected: %s actual: %s", "test", result3)
375	}
376}
377
378func TestTVP_WithTag(t *testing.T) {
379	checkConnStr(t)
380	SetLogger(testLogger{t})
381
382	db, err := sql.Open("sqlserver", makeConnStr(t).String())
383	if err != nil {
384		t.Fatalf("failed to open driver sqlserver")
385	}
386	defer db.Close()
387
388	ctx, cancel := context.WithCancel(context.Background())
389	defer cancel()
390
391	sqltextcreatetable := `
392		CREATE TYPE tvptable AS TABLE
393		(
394			p_binary 			BINARY(3),
395			p_varchar 			VARCHAR(500),
396			p_varcharNull 		VARCHAR(500),
397			p_nvarchar 			NVARCHAR(100),
398			p_nvarcharNull 		NVARCHAR(100),
399			p_id 				UNIQUEIDENTIFIER,
400			p_idNull 			UNIQUEIDENTIFIER,
401			p_varbinary 		VARBINARY(MAX),
402			p_tinyint 			TINYINT,
403			p_tinyintNull 		TINYINT,
404			p_smallint          SMALLINT,
405			p_smallintNull      SMALLINT,
406			p_int               INT,
407			p_intNull           INT,
408			p_bigint            BIGINT,
409			p_bigintNull        BIGINT,
410			p_bit               BIT,
411			p_bitNull           BIT,
412			p_float32           FLOAT,
413			p_floatNull32       FLOAT,
414			p_float64           FLOAT,
415			p_floatNull64       FLOAT,
416			p_time 				datetime2,
417			p_timeNull			datetime2,
418			pInt              	INT,
419			pIntNull          	INT
420		); `
421
422	sqltextdroptable := `DROP TYPE tvptable;`
423
424	sqltextcreatesp := `
425	CREATE PROCEDURE spwithtvp
426		@param1 tvptable READONLY,
427		@param2 tvptable READONLY,
428		@param3 NVARCHAR(10)
429	AS
430	BEGIN
431		SET NOCOUNT ON;
432		SELECT * FROM @param1;
433		SELECT * FROM @param2;
434		SELECT @param3;
435	END;`
436
437	sqltextdropsp := `DROP PROCEDURE spwithtvp;`
438
439	db.ExecContext(ctx, sqltextdropsp)
440	db.ExecContext(ctx, sqltextdroptable)
441
442	_, err = db.ExecContext(ctx, sqltextcreatetable)
443	if err != nil {
444		t.Fatal(err)
445	}
446	defer db.ExecContext(ctx, sqltextdroptable)
447
448	_, err = db.ExecContext(ctx, sqltextcreatesp)
449	if err != nil {
450		t.Fatal(err)
451	}
452	defer db.ExecContext(ctx, sqltextdropsp)
453
454	varcharNull := "aaa"
455	nvarchar := "bbb"
456	bytesMock := []byte("ddd")
457	i8 := int8(1)
458	i16 := int16(2)
459	i32 := int32(3)
460	i64 := int64(4)
461	i := int(355)
462	bFalse := false
463	floatValue64 := 0.123
464	floatValue32 := float32(-10.123)
465	timeNow := time.Now().UTC()
466	param1 := []TvptableRowWithSkipTag{
467		{
468			PBinary:    []byte("ccc"),
469			PVarchar:   varcharNull,
470			PNvarchar:  nvarchar,
471			PID:        UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
472			PVarbinary: bytesMock,
473			PTinyint:   i8,
474			PSmallint:  i16,
475			PInt:       i32,
476			PBigint:    i64,
477			PBit:       bFalse,
478			PFloat32:   floatValue32,
479			PFloat64:   floatValue64,
480			DTime:      timeNow,
481			Pint:       i,
482			PintNull:   &i,
483		},
484		{
485			PBinary:    []byte("www"),
486			PVarchar:   "eee",
487			PNvarchar:  "lll",
488			PID:        UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
489			PVarbinary: []byte("zzz"),
490			PTinyint:   5,
491			PSmallint:  16000,
492			PInt:       20000000,
493			PBigint:    2000000020000000,
494			PBit:       true,
495			PFloat32:   -123.45,
496			PFloat64:   -123.45,
497			DTime:      time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
498			Pint:       3669,
499			PintNull:   &i,
500		},
501		{
502			PBinary:       nil,
503			PVarcharNull:  &varcharNull,
504			PNvarcharNull: &nvarchar,
505			PIDNull:       &UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
506			PTinyintNull:  &i8,
507			PSmallintNull: &i16,
508			PIntNull:      &i32,
509			PBigintNull:   &i64,
510			PBitNull:      &bFalse,
511			PFloatNull32:  &floatValue32,
512			PFloatNull64:  &floatValue64,
513			DTime:         timeNow,
514			DTimeNull:     &timeNow,
515			Pint:          969,
516		},
517		{
518			PBinary:       []byte("www"),
519			PVarchar:      "eee",
520			PNvarchar:     "lll",
521			PIDNull:       &UniqueIdentifier{},
522			PVarbinary:    []byte("zzz"),
523			PTinyint:      5,
524			PSmallint:     16000,
525			PInt:          20000000,
526			PBigint:       2000000020000000,
527			PBit:          true,
528			PFloat64:      123.45,
529			DTime:         time.Date(2001, 11, 16, 23, 59, 39, 0, time.UTC),
530			PVarcharNull:  &varcharNull,
531			PNvarcharNull: &nvarchar,
532			PTinyintNull:  &i8,
533			PSmallintNull: &i16,
534			PIntNull:      &i32,
535			PBigintNull:   &i64,
536			PBitNull:      &bFalse,
537			PFloatNull32:  &floatValue32,
538			PFloatNull64:  &floatValue64,
539			DTimeNull:     &timeNow,
540			PintNull:      &i,
541		},
542	}
543
544	tvpType := TVP{
545		TypeName: "tvptable",
546		Value:    param1,
547	}
548	tvpTypeEmpty := TVP{
549		TypeName: "tvptable",
550		Value:    []TvptableRowWithSkipTag{},
551	}
552
553	rows, err := db.QueryContext(ctx,
554		"exec spwithtvp @param1, @param2, @param3",
555		sql.Named("param1", tvpType),
556		sql.Named("param2", tvpTypeEmpty),
557		sql.Named("param3", "test"),
558	)
559
560	if err != nil {
561		t.Fatal(err)
562	}
563
564	var result1 []TvptableRowWithSkipTag
565	for rows.Next() {
566		var val TvptableRowWithSkipTag
567		err := rows.Scan(
568			&val.PBinary,
569			&val.PVarchar,
570			&val.PVarcharNull,
571			&val.PNvarchar,
572			&val.PNvarcharNull,
573			&val.PID,
574			&val.PIDNull,
575			&val.PVarbinary,
576			&val.PTinyint,
577			&val.PTinyintNull,
578			&val.PSmallint,
579			&val.PSmallintNull,
580			&val.PInt,
581			&val.PIntNull,
582			&val.PBigint,
583			&val.PBigintNull,
584			&val.PBit,
585			&val.PBitNull,
586			&val.PFloat32,
587			&val.PFloatNull32,
588			&val.PFloat64,
589			&val.PFloatNull64,
590			&val.DTime,
591			&val.DTimeNull,
592			&val.Pint,
593			&val.PintNull,
594		)
595		if err != nil {
596			t.Fatalf("scan failed with error: %s", err)
597		}
598
599		result1 = append(result1, val)
600	}
601
602	if !reflect.DeepEqual(param1, result1) {
603		t.Logf("expected: %+v", param1)
604		t.Logf("actual: %+v", result1)
605		t.Errorf("first resultset did not match param1")
606	}
607
608	if !rows.NextResultSet() {
609		t.Errorf("second resultset did not exist")
610	}
611
612	if rows.Next() {
613		t.Errorf("second resultset was not empty")
614	}
615
616	if !rows.NextResultSet() {
617		t.Errorf("third resultset did not exist")
618	}
619
620	if !rows.Next() {
621		t.Errorf("third resultset was empty")
622	}
623
624	var result3 string
625	if err := rows.Scan(&result3); err != nil {
626		t.Errorf("error scanning third result set: %s", err)
627	}
628	if result3 != "test" {
629		t.Errorf("third result set had wrong value expected: %s actual: %s", "test", result3)
630	}
631}
632
633type TvpExample struct {
634	Message string
635}
636
637func TestTVPSchema(t *testing.T) {
638	checkConnStr(t)
639	SetLogger(testLogger{t})
640
641	conn, err := sql.Open("sqlserver", makeConnStr(t).String())
642	if err != nil {
643		log.Fatal("Open connection failed:", err.Error())
644	}
645	defer conn.Close()
646
647	_, err = conn.Exec(crateSchema)
648	if err != nil {
649		log.Println(err)
650		return
651	}
652	defer conn.Exec(dropSchema)
653
654	_, err = conn.Exec(createTVP)
655	if err != nil {
656		log.Println(err)
657		return
658	}
659	defer conn.Exec(dropTVP)
660
661	_, err = conn.Exec(procedureWithTVP)
662	if err != nil {
663		log.Println(err)
664		return
665	}
666	defer conn.Exec(dropProcedure)
667
668	exempleData := []TvpExample{
669		{
670			Message: "Hello",
671		},
672		{
673			Message: "World",
674		},
675		{
676			Message: "TVP",
677		},
678	}
679
680	tvpType := TVP{
681		TypeName: "TestTVPSchema.exempleTVP",
682		Value:    exempleData,
683	}
684
685	rows, err := conn.Query(execTvp,
686		sql.Named("param1", tvpType),
687	)
688	if err != nil {
689		log.Println(err)
690		return
691	}
692
693	tvpResult := make([]TvpExample, 0)
694	for rows.Next() {
695		tvpExemple := TvpExample{}
696		err = rows.Scan(&tvpExemple.Message)
697		if err != nil {
698			log.Println(err)
699			return
700		}
701		tvpResult = append(tvpResult, tvpExemple)
702	}
703	log.Println(tvpResult)
704}
705
706func TestTVPObject(t *testing.T) {
707	checkConnStr(t)
708	SetLogger(testLogger{t})
709
710	conn, err := sql.Open("sqlserver", makeConnStr(t).String())
711	if err != nil {
712		log.Fatal("Open connection failed:", err.Error())
713	}
714	defer conn.Close()
715
716	tests := []struct {
717		name    string
718		tvp     TVP
719		wantErr bool
720	}{
721		{
722			name:    "empty name",
723			wantErr: true,
724			tvp:     TVP{TypeName: ""},
725		},
726		{
727			name:    "value is wrong type",
728			wantErr: true,
729			tvp:     TVP{TypeName: "type", Value: "wrong type"},
730		},
731		{
732			name:    "tvp type is wrong",
733			wantErr: true,
734			tvp:     TVP{TypeName: "[type", Value: []TvpExample{{}}},
735		},
736		{
737			name:    "tvp type is wrong",
738			wantErr: true,
739			tvp:     TVP{TypeName: "[type", Value: []TestFieldsUnsupportedTypes{{}}},
740		},
741	}
742	for _, tt := range tests {
743		t.Run(tt.name, func(t *testing.T) {
744			_, err := conn.Exec("somequery", tt.tvp)
745			if (err != nil) != tt.wantErr {
746				t.Errorf("TVP.encode() error = %v, wantErr %v", err, tt.wantErr)
747				return
748			}
749		})
750	}
751}
752