mirror of
https://github.com/documize/community.git
synced 2025-07-19 21:29:42 +02:00
Update SQL Server driver library
This commit is contained in:
parent
c538fc9eb1
commit
9c36241b58
37 changed files with 9138 additions and 1091 deletions
2076
embed/bindata.go
2076
embed/bindata.go
File diff suppressed because one or more lines are too long
23
vendor/github.com/denisenkom/go-mssqldb/README.md
generated
vendored
23
vendor/github.com/denisenkom/go-mssqldb/README.md
generated
vendored
|
@ -117,6 +117,27 @@ _, err := db.ExecContext(ctx, "sp_RunMe",
|
|||
)
|
||||
```
|
||||
|
||||
## Reading Output Parameters from a Stored Procedure with Resultset
|
||||
|
||||
To read output parameters from a stored procedure with resultset, make sure you read all the rows before reading the output parameters:
|
||||
```go
|
||||
sqltextcreate := `
|
||||
CREATE PROCEDURE spwithoutputandrows
|
||||
@bitparam BIT OUTPUT
|
||||
AS BEGIN
|
||||
SET @bitparam = 1
|
||||
SELECT 'Row 1'
|
||||
END
|
||||
`
|
||||
var bitout int64
|
||||
rows, err := db.QueryContext(ctx, "spwithoutputandrows", sql.Named("bitparam", sql.Out{Dest: &bitout}))
|
||||
var strrow string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&strrow)
|
||||
}
|
||||
fmt.Printf("bitparam is %d", bitout)
|
||||
```
|
||||
|
||||
## Caveat for local temporary tables
|
||||
|
||||
Due to protocol limitations, temporary tables will only be allocated on the connection
|
||||
|
@ -189,7 +210,7 @@ are supported:
|
|||
* "cloud.google.com/go/civil".Date -> date
|
||||
* "cloud.google.com/go/civil".DateTime -> datetime2
|
||||
* "cloud.google.com/go/civil".Time -> time
|
||||
* mssql.TVPType -> Table Value Parameter (TDS version dependent)
|
||||
* mssql.TVP -> Table Value Parameter (TDS version dependent)
|
||||
|
||||
## Important Notes
|
||||
|
||||
|
|
287
vendor/github.com/denisenkom/go-mssqldb/batch/batch.go
generated
vendored
Normal file
287
vendor/github.com/denisenkom/go-mssqldb/batch/batch.go
generated
vendored
Normal file
|
@ -0,0 +1,287 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// bach splits a single script containing multiple batches separated by
|
||||
// a keyword into multiple scripts.
|
||||
package batch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Split the provided SQL into multiple sql scripts based on a given
|
||||
// separator, often "GO". It also allows escaping newlines with a
|
||||
// backslash.
|
||||
func Split(sql, separator string) []string {
|
||||
if len(separator) == 0 || len(sql) < len(separator) {
|
||||
return []string{sql}
|
||||
}
|
||||
l := &lexer{
|
||||
Sql: sql,
|
||||
Sep: separator,
|
||||
At: 0,
|
||||
}
|
||||
state := stateWhitespace
|
||||
for state != nil {
|
||||
state = state(l)
|
||||
}
|
||||
l.AddCurrent(1)
|
||||
return l.Batch
|
||||
}
|
||||
|
||||
const debugPrintStateName = false
|
||||
|
||||
func printStateName(name string, l *lexer) {
|
||||
if debugPrintStateName {
|
||||
fmt.Printf("state %s At=%d\n", name, l.At)
|
||||
}
|
||||
}
|
||||
|
||||
func hasPrefixFold(s, sep string) bool {
|
||||
if len(s) < len(sep) {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(s[:len(sep)], sep)
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
Sql string
|
||||
Sep string
|
||||
At int
|
||||
Start int
|
||||
|
||||
Skip []int
|
||||
|
||||
Batch []string
|
||||
}
|
||||
|
||||
func (l *lexer) Add(b string) {
|
||||
if len(b) == 0 {
|
||||
return
|
||||
}
|
||||
l.Batch = append(l.Batch, b)
|
||||
}
|
||||
|
||||
func (l *lexer) Next() bool {
|
||||
l.At++
|
||||
return l.At < len(l.Sql)
|
||||
}
|
||||
|
||||
func (l *lexer) AddCurrent(count int64) bool {
|
||||
if count < 0 {
|
||||
count = 0
|
||||
}
|
||||
if l.At >= len(l.Sql) {
|
||||
l.At = len(l.Sql)
|
||||
}
|
||||
text := l.Sql[l.Start:l.At]
|
||||
if len(l.Skip) > 0 {
|
||||
buf := &bytes.Buffer{}
|
||||
nextSkipIndex := 0
|
||||
nextSkip := l.Skip[nextSkipIndex]
|
||||
for i, r := range text {
|
||||
if i == nextSkip {
|
||||
nextSkipIndex++
|
||||
if nextSkipIndex < len(l.Skip) {
|
||||
nextSkip = l.Skip[nextSkipIndex]
|
||||
}
|
||||
continue
|
||||
}
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
text = buf.String()
|
||||
l.Skip = nil
|
||||
}
|
||||
// Limit the number of counts for sanity.
|
||||
if count > 1000 {
|
||||
count = 1000
|
||||
}
|
||||
for i := int64(0); i < count; i++ {
|
||||
l.Add(text)
|
||||
}
|
||||
l.At += len(l.Sep)
|
||||
l.Start = l.At
|
||||
return (l.At < len(l.Sql))
|
||||
}
|
||||
|
||||
type stateFn func(*lexer) stateFn
|
||||
|
||||
const (
|
||||
lineComment = "--"
|
||||
leftComment = "/*"
|
||||
rightComment = "*/"
|
||||
)
|
||||
|
||||
func stateSep(l *lexer) stateFn {
|
||||
printStateName("sep", l)
|
||||
if l.At+len(l.Sep) >= len(l.Sql) {
|
||||
return nil
|
||||
}
|
||||
s := l.Sql[l.At+len(l.Sep):]
|
||||
|
||||
parseNumberStart := -1
|
||||
loop:
|
||||
for i, r := range s {
|
||||
switch {
|
||||
case r == '\n', r == '\r':
|
||||
l.AddCurrent(1)
|
||||
return stateWhitespace
|
||||
case unicode.IsSpace(r):
|
||||
case unicode.IsNumber(r):
|
||||
parseNumberStart = i
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if parseNumberStart < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
parseNumberCount := 0
|
||||
numLoop:
|
||||
for i, r := range s[parseNumberStart:] {
|
||||
switch {
|
||||
case unicode.IsNumber(r):
|
||||
parseNumberCount = i
|
||||
default:
|
||||
break numLoop
|
||||
}
|
||||
}
|
||||
parseNumberEnd := parseNumberStart + parseNumberCount + 1
|
||||
|
||||
count, err := strconv.ParseInt(s[parseNumberStart:parseNumberEnd], 10, 64)
|
||||
if err != nil {
|
||||
return stateText
|
||||
}
|
||||
for _, r := range s[parseNumberEnd:] {
|
||||
switch {
|
||||
case r == '\n', r == '\r':
|
||||
l.AddCurrent(count)
|
||||
l.At += parseNumberEnd
|
||||
l.Start = l.At
|
||||
return stateWhitespace
|
||||
case unicode.IsSpace(r):
|
||||
default:
|
||||
return stateText
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stateText(l *lexer) stateFn {
|
||||
printStateName("text", l)
|
||||
for {
|
||||
ch := l.Sql[l.At]
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(l.Sql[l.At:], lineComment):
|
||||
l.At += len(lineComment)
|
||||
return stateLineComment
|
||||
case strings.HasPrefix(l.Sql[l.At:], leftComment):
|
||||
l.At += len(leftComment)
|
||||
return stateMultiComment
|
||||
case ch == '\'':
|
||||
l.At += 1
|
||||
return stateString
|
||||
case ch == '\r', ch == '\n':
|
||||
l.At += 1
|
||||
return stateWhitespace
|
||||
default:
|
||||
if l.Next() == false {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stateWhitespace(l *lexer) stateFn {
|
||||
printStateName("whitespace", l)
|
||||
if l.At >= len(l.Sql) {
|
||||
return nil
|
||||
}
|
||||
ch := l.Sql[l.At]
|
||||
|
||||
switch {
|
||||
case unicode.IsSpace(rune(ch)):
|
||||
l.At += 1
|
||||
return stateWhitespace
|
||||
case hasPrefixFold(l.Sql[l.At:], l.Sep):
|
||||
return stateSep
|
||||
default:
|
||||
return stateText
|
||||
}
|
||||
}
|
||||
|
||||
func stateLineComment(l *lexer) stateFn {
|
||||
printStateName("line-comment", l)
|
||||
for {
|
||||
if l.At >= len(l.Sql) {
|
||||
return nil
|
||||
}
|
||||
ch := l.Sql[l.At]
|
||||
|
||||
switch {
|
||||
case ch == '\r', ch == '\n':
|
||||
l.At += 1
|
||||
return stateWhitespace
|
||||
default:
|
||||
if l.Next() == false {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stateMultiComment(l *lexer) stateFn {
|
||||
printStateName("multi-line-comment", l)
|
||||
for {
|
||||
switch {
|
||||
case strings.HasPrefix(l.Sql[l.At:], rightComment):
|
||||
l.At += len(leftComment)
|
||||
return stateWhitespace
|
||||
default:
|
||||
if l.Next() == false {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stateString(l *lexer) stateFn {
|
||||
printStateName("string", l)
|
||||
for {
|
||||
if l.At >= len(l.Sql) {
|
||||
return nil
|
||||
}
|
||||
ch := l.Sql[l.At]
|
||||
chNext := rune(-1)
|
||||
if l.At+1 < len(l.Sql) {
|
||||
chNext = rune(l.Sql[l.At+1])
|
||||
}
|
||||
|
||||
switch {
|
||||
case ch == '\\' && (chNext == '\r' || chNext == '\n'):
|
||||
next := 2
|
||||
l.Skip = append(l.Skip, l.At, l.At+1)
|
||||
if chNext == '\r' && l.At+2 < len(l.Sql) && l.Sql[l.At+2] == '\n' {
|
||||
l.Skip = append(l.Skip, l.At+2)
|
||||
next = 3
|
||||
}
|
||||
l.At += next
|
||||
case ch == '\'' && chNext == '\'':
|
||||
l.At += 2
|
||||
case ch == '\'' && chNext != '\'':
|
||||
l.At += 1
|
||||
return stateWhitespace
|
||||
default:
|
||||
if l.Next() == false {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
vendor/github.com/denisenkom/go-mssqldb/batch/batch_fuzz.go
generated
vendored
Normal file
12
vendor/github.com/denisenkom/go-mssqldb/batch/batch_fuzz.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gofuzz
|
||||
|
||||
package batch
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
Split(string(data), "GO")
|
||||
return 0
|
||||
}
|
120
vendor/github.com/denisenkom/go-mssqldb/batch/batch_test.go
generated
vendored
Normal file
120
vendor/github.com/denisenkom/go-mssqldb/batch/batch_test.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package batch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBatchSplit(t *testing.T) {
|
||||
type testItem struct {
|
||||
Sql string
|
||||
Expect []string
|
||||
}
|
||||
|
||||
list := []testItem{
|
||||
testItem{
|
||||
Sql: `use DB
|
||||
go
|
||||
select 1
|
||||
go
|
||||
select 2
|
||||
`,
|
||||
Expect: []string{`use DB
|
||||
`, `
|
||||
select 1
|
||||
`, `
|
||||
select 2
|
||||
`,
|
||||
},
|
||||
},
|
||||
testItem{
|
||||
Sql: `go
|
||||
use DB go
|
||||
`,
|
||||
Expect: []string{`
|
||||
use DB go
|
||||
`,
|
||||
},
|
||||
},
|
||||
testItem{
|
||||
Sql: `select 'It''s go time'
|
||||
go
|
||||
select top 1 1`,
|
||||
Expect: []string{`select 'It''s go time'
|
||||
`, `
|
||||
select top 1 1`,
|
||||
},
|
||||
},
|
||||
testItem{
|
||||
Sql: `select 1 /* go */
|
||||
go
|
||||
select top 1 1`,
|
||||
Expect: []string{`select 1 /* go */
|
||||
`, `
|
||||
select top 1 1`,
|
||||
},
|
||||
},
|
||||
testItem{
|
||||
Sql: `select 1 -- go
|
||||
go
|
||||
select top 1 1`,
|
||||
Expect: []string{`select 1 -- go
|
||||
`, `
|
||||
select top 1 1`,
|
||||
},
|
||||
},
|
||||
testItem{Sql: `"0'"`, Expect: []string{`"0'"`}},
|
||||
testItem{Sql: "0'", Expect: []string{"0'"}},
|
||||
testItem{Sql: "--", Expect: []string{"--"}},
|
||||
testItem{Sql: "GO", Expect: nil},
|
||||
testItem{Sql: "/*", Expect: []string{"/*"}},
|
||||
testItem{Sql: "gO\x01\x00O550655490663051008\n", Expect: []string{"\n"}},
|
||||
testItem{Sql: "select 1;\nGO 2\nselect 2;", Expect: []string{"select 1;\n", "select 1;\n", "\nselect 2;"}},
|
||||
testItem{Sql: "select 'hi\\\n-hello';", Expect: []string{"select 'hi-hello';"}},
|
||||
testItem{Sql: "select 'hi\\\r\n-hello';", Expect: []string{"select 'hi-hello';"}},
|
||||
testItem{Sql: "select 'hi\\\r-hello';", Expect: []string{"select 'hi-hello';"}},
|
||||
testItem{Sql: "select 'hi\\\n\nhello';", Expect: []string{"select 'hi\nhello';"}},
|
||||
}
|
||||
|
||||
index := -1
|
||||
|
||||
for i := range list {
|
||||
if index >= 0 && index != i {
|
||||
continue
|
||||
}
|
||||
sqltext := list[i].Sql
|
||||
t.Run(fmt.Sprintf("index-%d", i), func(t *testing.T) {
|
||||
ss := Split(sqltext, "go")
|
||||
if len(ss) != len(list[i].Expect) {
|
||||
t.Errorf("Test Item index %d; expect %d items, got %d %q", i, len(list[i].Expect), len(ss), ss)
|
||||
return
|
||||
}
|
||||
for j := 0; j < len(ss); j++ {
|
||||
if ss[j] != list[i].Expect[j] {
|
||||
t.Errorf("Test Item index %d, batch index %d; expect <%s>, got <%s>", i, j, list[i].Expect[j], ss[j])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefixFold(t *testing.T) {
|
||||
list := []struct {
|
||||
s, pre string
|
||||
is bool
|
||||
}{
|
||||
{"h", "H", true},
|
||||
{"h", "K", false},
|
||||
{"go 5\n", "go", true},
|
||||
}
|
||||
for _, item := range list {
|
||||
is := hasPrefixFold(item.s, item.pre)
|
||||
if is != item.is {
|
||||
t.Errorf("want (%q, %q)=%t got %t", item.s, item.pre, item.is, is)
|
||||
}
|
||||
}
|
||||
}
|
284
vendor/github.com/denisenkom/go-mssqldb/buf_test.go
generated
vendored
Normal file
284
vendor/github.com/denisenkom/go-mssqldb/buf_test.go
generated
vendored
Normal file
|
@ -0,0 +1,284 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type closableBuffer struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
func (closableBuffer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type failBuffer struct {
|
||||
}
|
||||
|
||||
func (failBuffer) Read([]byte) (int, error) {
|
||||
return 0, errors.New("read failed")
|
||||
}
|
||||
|
||||
func (failBuffer) Write([]byte) (int, error) {
|
||||
return 0, errors.New("write failed")
|
||||
}
|
||||
|
||||
func (failBuffer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeBuf(bufSize uint16, testData []byte) *tdsBuffer {
|
||||
buffer := closableBuffer{bytes.NewBuffer(testData)}
|
||||
return newTdsBuffer(bufSize, &buffer)
|
||||
}
|
||||
|
||||
func TestStreamShorterThanHeader(t *testing.T) {
|
||||
//buffer := closableBuffer{*bytes.NewBuffer([]byte{0xFF, 0xFF})}
|
||||
//buffer := closableBuffer{*bytes.NewBuffer([]byte{0x6F, 0x96, 0x19, 0xFF, 0x8B, 0x86, 0xD0, 0x11, 0xB4, 0x2D, 0x00, 0xC0, 0x4F, 0xC9, 0x64, 0xFF})}
|
||||
//tdsBuffer := newTdsBuffer(100, &buffer)
|
||||
buffer := makeBuf(100, []byte{0xFF, 0xFF})
|
||||
_, err := buffer.BeginRead()
|
||||
if err == nil {
|
||||
t.Fatal("BeginRead was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("BeginRead failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidLengthInHeaderTooLong(t *testing.T) {
|
||||
buffer := makeBuf(8, []byte{0xFF, 0xFF, 0x0, 0x9, 0xff, 0xff, 0xff, 0xff})
|
||||
_, err := buffer.BeginRead()
|
||||
if err == nil {
|
||||
t.Fatal("BeginRead was expected to return error but it didn't")
|
||||
} else {
|
||||
if err.Error() != "Invalid packet size, it is longer than buffer size" {
|
||||
t.Fatal("BeginRead failed with incorrect error", err)
|
||||
} else {
|
||||
t.Log("BeginRead failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidLengthInHeaderTooShort(t *testing.T) {
|
||||
buffer := makeBuf(100, []byte{0xFF, 0xFF, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff})
|
||||
_, err := buffer.BeginRead()
|
||||
if err == nil {
|
||||
t.Fatal("BeginRead was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("BeginRead failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidLengthInHeaderLongerThanIncomingBuffer(t *testing.T) {
|
||||
buffer := makeBuf(9, []byte{0xFF, 0xFF, 0x0, 0x9, 0xff, 0xff, 0xff, 0xff})
|
||||
_, err := buffer.BeginRead()
|
||||
if err == nil {
|
||||
t.Fatal("BeginRead was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("BeginRead failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeginReadSucceeds(t *testing.T) {
|
||||
buffer := makeBuf(9, []byte{0x01 /*id*/, 0xFF /*status*/, 0x0, 0x9 /*size*/, 0xff, 0xff, 0xff, 0xff, 0x02 /*test byte*/})
|
||||
|
||||
id, err := buffer.BeginRead()
|
||||
if err != nil {
|
||||
t.Fatal("BeginRead failed:", err.Error())
|
||||
}
|
||||
if id != 1 {
|
||||
t.Fatalf("Expected id to be 1 but it is %d", id)
|
||||
}
|
||||
|
||||
b, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
t.Fatal("ReadByte failed:", err.Error())
|
||||
}
|
||||
if b != 2 {
|
||||
t.Fatalf("Expected read byte to be 2 but it is %d", b)
|
||||
}
|
||||
|
||||
// should fail because no more bytes left
|
||||
_, err = buffer.ReadByte()
|
||||
if err == nil {
|
||||
t.Fatal("ReadByte was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("ReadByte failed as expected with error:", err.Error())
|
||||
}
|
||||
|
||||
testBuf := []byte{0, 1, 2}
|
||||
// should fail because no more bytes left
|
||||
_, err = buffer.Read(testBuf)
|
||||
if err == nil {
|
||||
t.Fatal("Read was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("Read failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadByteFailsOnSecondPacket(t *testing.T) {
|
||||
buffer := makeBuf(9, []byte{
|
||||
0x01 /*id*/, 0x0 /*not final*/, 0x0, 0x9 /*size*/, 0xff, 0xff, 0xff, 0xff, 0x02, /*test byte*/
|
||||
0x01 /*next id, this packet is invalid, it is too short*/})
|
||||
|
||||
_, err := buffer.BeginRead()
|
||||
if err != nil {
|
||||
t.Fatal("BeginRead failed:", err.Error())
|
||||
}
|
||||
|
||||
_, err = buffer.ReadByte()
|
||||
if err != nil {
|
||||
t.Fatal("ReadByte failed:", err.Error())
|
||||
}
|
||||
|
||||
_, err = buffer.ReadByte()
|
||||
if err == nil {
|
||||
t.Fatal("ReadByte was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("ReadByte failed as expected with error:", err.Error())
|
||||
}
|
||||
|
||||
t.Run("test byte() panic", func(t *testing.T) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
buffer.byte()
|
||||
t.Fatal("byte() should panic, but it didn't")
|
||||
})
|
||||
|
||||
t.Run("test ReadFull() panic", func(t *testing.T) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
buf := make([]byte, 10)
|
||||
buffer.ReadFull(buf)
|
||||
t.Fatal("ReadFull() should panic, but it didn't")
|
||||
})
|
||||
}
|
||||
|
||||
func TestReadFailsOnSecondPacket(t *testing.T) {
|
||||
buffer := makeBuf(9, []byte{
|
||||
0x01 /*id*/, 0x0 /*not final*/, 0x0, 0x9 /*size*/, 0xff, 0xff, 0xff, 0xff, 0x02, /*test byte*/
|
||||
0x01 /*next id, this packet is invalid, it is too short*/})
|
||||
|
||||
_, err := buffer.BeginRead()
|
||||
if err != nil {
|
||||
t.Fatal("BeginRead failed:", err.Error())
|
||||
}
|
||||
|
||||
testBuf := []byte{0}
|
||||
_, err = buffer.Read(testBuf)
|
||||
if err != nil {
|
||||
t.Fatal("Read failed:", err.Error())
|
||||
}
|
||||
if testBuf[0] != 2 {
|
||||
t.Fatal("Read returned invalid value")
|
||||
}
|
||||
|
||||
_, err = buffer.Read(testBuf)
|
||||
if err == nil {
|
||||
t.Fatal("ReadByte was expected to return error but it didn't")
|
||||
} else {
|
||||
t.Log("ReadByte failed as expected with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
memBuf := bytes.NewBuffer([]byte{})
|
||||
buf := newTdsBuffer(11, closableBuffer{memBuf})
|
||||
buf.BeginPacket(1, false)
|
||||
err := buf.WriteByte(2)
|
||||
if err != nil {
|
||||
t.Fatal("WriteByte failed:", err.Error())
|
||||
}
|
||||
wrote, err := buf.Write([]byte{3, 4})
|
||||
if err != nil {
|
||||
t.Fatal("Write failed:", err.Error())
|
||||
}
|
||||
if wrote != 2 {
|
||||
t.Fatalf("Write returned invalid value of written bytes %d", wrote)
|
||||
}
|
||||
|
||||
err = buf.FinishPacket()
|
||||
if err != nil {
|
||||
t.Fatal("FinishPacket failed:", err.Error())
|
||||
}
|
||||
if bytes.Compare(memBuf.Bytes(), []byte{1, 1, 0, 11, 0, 0, 1, 0, 2, 3, 4}) != 0 {
|
||||
t.Fatalf("Written buffer has invalid content: %v", memBuf.Bytes())
|
||||
}
|
||||
|
||||
buf.BeginPacket(2, false)
|
||||
wrote, err = buf.Write([]byte{3, 4, 5, 6})
|
||||
if err != nil {
|
||||
t.Fatal("Write failed:", err.Error())
|
||||
}
|
||||
if wrote != 4 {
|
||||
t.Fatalf("Write returned invalid value of written bytes %d", wrote)
|
||||
}
|
||||
err = buf.FinishPacket()
|
||||
if err != nil {
|
||||
t.Fatal("FinishPacket failed:", err.Error())
|
||||
}
|
||||
expectedBuf := []byte{
|
||||
1, 1, 0, 11, 0, 0, 1, 0, 2, 3, 4, // packet 1
|
||||
2, 0, 0, 11, 0, 0, 1, 0, 3, 4, 5, // packet 2
|
||||
2, 1, 0, 9, 0, 0, 2, 0, 6, // packet 3
|
||||
}
|
||||
if bytes.Compare(memBuf.Bytes(), expectedBuf) != 0 {
|
||||
t.Fatalf("Written buffer has invalid content:\n got: %v\nwant: %v", memBuf.Bytes(), expectedBuf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteErrors(t *testing.T) {
|
||||
// write should fail if underlying transport fails
|
||||
buf := newTdsBuffer(uint16(headerSize)+1, failBuffer{})
|
||||
buf.BeginPacket(1, false)
|
||||
wrote, err := buf.Write([]byte{0, 0})
|
||||
// may change from error to panic in future
|
||||
if err == nil {
|
||||
t.Fatal("Write should fail but it didn't")
|
||||
}
|
||||
if wrote != 1 {
|
||||
t.Fatal("Should write 1 byte but it wrote ", wrote)
|
||||
}
|
||||
|
||||
// writebyte should fail if underlying transport fails
|
||||
buf = newTdsBuffer(uint16(headerSize)+1, failBuffer{})
|
||||
buf.BeginPacket(1, false)
|
||||
// first write should not fail because if fits in the buffer
|
||||
err = buf.WriteByte(0)
|
||||
if err != nil {
|
||||
t.Fatal("First WriteByte should not fail because it should fit in the buffer, but it failed", err)
|
||||
}
|
||||
err = buf.WriteByte(0)
|
||||
// may change from error to panic in future
|
||||
if err == nil {
|
||||
t.Fatal("Second WriteByte should fail but it didn't")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite_BufferBounds(t *testing.T) {
|
||||
memBuf := bytes.NewBuffer([]byte{})
|
||||
buf := newTdsBuffer(11, closableBuffer{memBuf})
|
||||
|
||||
buf.BeginPacket(1, false)
|
||||
// write bytes enough to complete a package
|
||||
_, err := buf.Write([]byte{1, 1, 1})
|
||||
if err != nil {
|
||||
t.Fatal("Write failed:", err.Error())
|
||||
}
|
||||
err = buf.WriteByte(1)
|
||||
if err != nil {
|
||||
t.Fatal("WriteByte failed:", err.Error())
|
||||
}
|
||||
_, err = buf.Write([]byte{1, 1, 1})
|
||||
if err != nil {
|
||||
t.Fatal("Write failed:", err.Error())
|
||||
}
|
||||
err = buf.FinishPacket()
|
||||
if err != nil {
|
||||
t.Fatal("FinishPacket failed:", err.Error())
|
||||
}
|
||||
}
|
237
vendor/github.com/denisenkom/go-mssqldb/bulkcopy_test.go
generated
vendored
Normal file
237
vendor/github.com/denisenkom/go-mssqldb/bulkcopy_test.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
|||
// +build go1.9
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBulkcopy(t *testing.T) {
|
||||
// TDS level Bulk Insert is not supported on Azure SQL Server.
|
||||
if dsn := makeConnStr(t); strings.HasSuffix(strings.Split(dsn.Host, ":")[0], ".database.windows.net") {
|
||||
t.Skip("TDS level bulk copy is not supported on Azure SQL Server")
|
||||
}
|
||||
type testValue struct {
|
||||
colname string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
tableName := "#table_test"
|
||||
geom, _ := hex.DecodeString("E6100000010C00000000000034400000000000004440")
|
||||
bin, _ := hex.DecodeString("ba8b7782168d4033a299333aec17bd33")
|
||||
testValues := []testValue{
|
||||
|
||||
{"test_nvarchar", "ab©ĎéⒻghïjklmnopqЯ☀tuvwxyz"},
|
||||
{"test_varchar", "abcdefg"},
|
||||
{"test_char", "abcdefg "},
|
||||
{"test_nchar", "abcdefg "},
|
||||
{"test_text", "abcdefg"},
|
||||
{"test_ntext", "abcdefg"},
|
||||
{"test_float", 1234.56},
|
||||
{"test_floatn", 1234.56},
|
||||
{"test_real", 1234.56},
|
||||
{"test_realn", 1234.56},
|
||||
{"test_bit", true},
|
||||
{"test_bitn", nil},
|
||||
{"test_smalldatetime", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
|
||||
{"test_smalldatetimen", time.Date(2010, 11, 12, 13, 14, 0, 0, time.UTC)},
|
||||
{"test_datetime", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
|
||||
{"test_datetimen", time.Date(2010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
|
||||
{"test_datetimen_1", time.Date(4010, 11, 12, 13, 14, 15, 120000000, time.UTC)},
|
||||
{"test_datetime2_1", time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC)},
|
||||
{"test_datetime2_3", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
|
||||
{"test_datetime2_7", time.Date(2010, 11, 12, 13, 14, 15, 123000000, time.UTC)},
|
||||
{"test_date", time.Date(2010, 11, 12, 00, 00, 00, 0, time.UTC)},
|
||||
{"test_tinyint", 255},
|
||||
{"test_smallint", 32767},
|
||||
{"test_smallintn", nil},
|
||||
{"test_int", 2147483647},
|
||||
{"test_bigint", 9223372036854775807},
|
||||
{"test_bigintn", nil},
|
||||
{"test_geom", geom},
|
||||
{"test_uniqueidentifier", []byte{0x6F, 0x96, 0x19, 0xFF, 0x8B, 0x86, 0xD0, 0x11, 0xB4, 0x2D, 0x00, 0xC0, 0x4F, 0xC9, 0x64, 0xFF}},
|
||||
// {"test_smallmoney", 1234.56},
|
||||
// {"test_money", 1234.56},
|
||||
{"test_decimal_18_0", 1234.0001},
|
||||
{"test_decimal_9_2", 1234.560001},
|
||||
{"test_decimal_20_0", 1234.0001},
|
||||
{"test_numeric_30_10", 1234567.1234567},
|
||||
{"test_varbinary", []byte("1")},
|
||||
{"test_varbinary_16", bin},
|
||||
{"test_varbinary_max", bin},
|
||||
{"test_binary", []byte("1")},
|
||||
{"test_binary_16", bin},
|
||||
}
|
||||
|
||||
columns := make([]string, len(testValues))
|
||||
for i, val := range testValues {
|
||||
columns[i] = val.colname
|
||||
}
|
||||
|
||||
values := make([]interface{}, len(testValues))
|
||||
for i, val := range testValues {
|
||||
values[i] = val.val
|
||||
}
|
||||
|
||||
pool := open(t)
|
||||
defer pool.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Now that session resetting is supported, the use of the per session
|
||||
// temp table requires the use of a dedicated connection from the connection
|
||||
// pool.
|
||||
conn, err := pool.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("failed to pull connection from pool", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = setupTable(ctx, t, conn, tableName)
|
||||
if err != nil {
|
||||
t.Error("Setup table failed: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Log("Preparing copy in statement")
|
||||
|
||||
stmt, err := conn.PrepareContext(ctx, CopyIn(tableName, BulkOptions{}, columns...))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Logf("Executing copy in statement %d time with %d values", i+1, len(values))
|
||||
_, err = stmt.Exec(values...)
|
||||
if err != nil {
|
||||
t.Error("AddRow failed: ", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err := stmt.Exec()
|
||||
if err != nil {
|
||||
t.Fatal("bulkcopy failed: ", err.Error())
|
||||
}
|
||||
|
||||
insertedRowCount, _ := result.RowsAffected()
|
||||
if insertedRowCount == 0 {
|
||||
t.Fatal("0 row inserted!")
|
||||
}
|
||||
|
||||
//check that all rows are present
|
||||
var rowCount int
|
||||
err = conn.QueryRowContext(ctx, "select count(*) c from "+tableName).Scan(&rowCount)
|
||||
|
||||
if rowCount != 10 {
|
||||
t.Errorf("unexpected row count %d", rowCount)
|
||||
}
|
||||
|
||||
//data verification
|
||||
rows, err := conn.QueryContext(ctx, "select "+strings.Join(columns, ",")+" from "+tableName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
|
||||
ptrs := make([]interface{}, len(columns))
|
||||
container := make([]interface{}, len(columns))
|
||||
for i, _ := range ptrs {
|
||||
ptrs[i] = &container[i]
|
||||
}
|
||||
if err := rows.Scan(ptrs...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, c := range testValues {
|
||||
if !compareValue(container[i], c.val) {
|
||||
t.Errorf("columns %s : expected: %v, got: %v\n", c.colname, c.val, container[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func compareValue(a interface{}, expected interface{}) bool {
|
||||
switch expected := expected.(type) {
|
||||
case int:
|
||||
return int64(expected) == a
|
||||
case int32:
|
||||
return int64(expected) == a
|
||||
case int64:
|
||||
return int64(expected) == a
|
||||
case float64:
|
||||
if got, ok := a.([]uint8); ok {
|
||||
var nf sql.NullFloat64
|
||||
nf.Scan(got)
|
||||
a = nf.Float64
|
||||
}
|
||||
return math.Abs(expected-a.(float64)) < 0.0001
|
||||
default:
|
||||
return reflect.DeepEqual(expected, a)
|
||||
}
|
||||
}
|
||||
|
||||
func setupTable(ctx context.Context, t *testing.T, conn *sql.Conn, tableName string) (err error) {
|
||||
tablesql := `CREATE TABLE ` + tableName + ` (
|
||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[test_nvarchar] [nvarchar](50) NULL,
|
||||
[test_varchar] [varchar](50) NULL,
|
||||
[test_char] [char](10) NULL,
|
||||
[test_nchar] [nchar](10) NULL,
|
||||
[test_text] [text] NULL,
|
||||
[test_ntext] [ntext] NULL,
|
||||
[test_float] [float] NOT NULL,
|
||||
[test_floatn] [float] NULL,
|
||||
[test_real] [real] NULL,
|
||||
[test_realn] [real] NULL,
|
||||
[test_bit] [bit] NOT NULL,
|
||||
[test_bitn] [bit] NULL,
|
||||
[test_smalldatetime] [smalldatetime] NOT NULL,
|
||||
[test_smalldatetimen] [smalldatetime] NULL,
|
||||
[test_datetime] [datetime] NOT NULL,
|
||||
[test_datetimen] [datetime] NULL,
|
||||
[test_datetimen_1] [datetime] NULL,
|
||||
[test_datetime2_1] [datetime2](1) NULL,
|
||||
[test_datetime2_3] [datetime2](3) NULL,
|
||||
[test_datetime2_7] [datetime2](7) NULL,
|
||||
[test_date] [date] NULL,
|
||||
[test_smallmoney] [smallmoney] NULL,
|
||||
[test_money] [money] NULL,
|
||||
[test_tinyint] [tinyint] NULL,
|
||||
[test_smallint] [smallint] NOT NULL,
|
||||
[test_smallintn] [smallint] NULL,
|
||||
[test_int] [int] NULL,
|
||||
[test_bigint] [bigint] NOT NULL,
|
||||
[test_bigintn] [bigint] NULL,
|
||||
[test_geom] [geometry] NULL,
|
||||
[test_geog] [geography] NULL,
|
||||
[text_xml] [xml] NULL,
|
||||
[test_uniqueidentifier] [uniqueidentifier] NULL,
|
||||
[test_decimal_18_0] [decimal](18, 0) NULL,
|
||||
[test_decimal_9_2] [decimal](9, 2) NULL,
|
||||
[test_decimal_20_0] [decimal](20, 0) NULL,
|
||||
[test_numeric_30_10] [decimal](30, 10) NULL,
|
||||
[test_varbinary] VARBINARY NOT NULL,
|
||||
[test_varbinary_16] VARBINARY(16) NOT NULL,
|
||||
[test_varbinary_max] VARBINARY(max) NOT NULL,
|
||||
[test_binary] BINARY NOT NULL,
|
||||
[test_binary_16] BINARY(16) NOT NULL,
|
||||
CONSTRAINT [PK_` + tableName + `_id] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[id] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];`
|
||||
_, err = conn.ExecContext(ctx, tablesql)
|
||||
if err != nil {
|
||||
t.Fatal("tablesql failed:", err)
|
||||
}
|
||||
return
|
||||
}
|
116
vendor/github.com/denisenkom/go-mssqldb/bulkimport_example_test.go
generated
vendored
Normal file
116
vendor/github.com/denisenkom/go-mssqldb/bulkimport_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
const (
|
||||
createTestTable = `CREATE TABLE test_table(
|
||||
id int IDENTITY(1,1) NOT NULL,
|
||||
test_nvarchar nvarchar(50) NULL,
|
||||
test_varchar varchar(50) NULL,
|
||||
test_float float NULL,
|
||||
test_datetime2_3 datetime2(3) NULL,
|
||||
test_bitn bit NULL,
|
||||
test_bigint bigint NOT NULL,
|
||||
test_geom geometry NULL,
|
||||
CONSTRAINT PK_table_test_id PRIMARY KEY CLUSTERED
|
||||
(
|
||||
id ASC
|
||||
) ON [PRIMARY]);`
|
||||
dropTestTable = "IF OBJECT_ID('test_table', 'U') IS NOT NULL DROP TABLE test_table;"
|
||||
)
|
||||
|
||||
// This example shows how to perform bulk imports
|
||||
func ExampleCopyIn() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := makeConnURL().String()
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlserver", connString)
|
||||
if err != nil {
|
||||
log.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
txn, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create table
|
||||
_, err = db.Exec(createTestTable)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Exec(dropTestTable)
|
||||
|
||||
// mssqldb.CopyIn creates string to be consumed by Prepare
|
||||
stmt, err := txn.Prepare(mssql.CopyIn("test_table", mssql.BulkOptions{}, "test_varchar", "test_nvarchar", "test_float", "test_bigint"))
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err = stmt.Exec(generateString(0, 30), generateStringUnicode(0, 30), i, i)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
result, err := stmt.Exec()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = stmt.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = txn.Commit()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rowCount, _ := result.RowsAffected()
|
||||
log.Printf("%d row copied\n", rowCount)
|
||||
log.Printf("bye\n")
|
||||
}
|
||||
|
||||
func generateString(x int, n int) string {
|
||||
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letters[(x+i)%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
func generateStringUnicode(x int, n int) string {
|
||||
letters := []byte("ab©💾é?ghïjklmnopqЯ☀tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
b := &strings.Builder{}
|
||||
for i := 0; i < n; i++ {
|
||||
r, sz := utf8.DecodeRune(letters[x%len(letters):])
|
||||
x += sz
|
||||
b.WriteRune(r)
|
||||
}
|
||||
return b.String()
|
||||
}
|
121
vendor/github.com/denisenkom/go-mssqldb/datetimeoffset_example_test.go
generated
vendored
Normal file
121
vendor/github.com/denisenkom/go-mssqldb/datetimeoffset_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/civil"
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
// This example shows how to insert and retrieve date and time types data
|
||||
func ExampleDateTimeOffset() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := makeConnURL().String()
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlserver", connString)
|
||||
if err != nil {
|
||||
log.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
insertDateTime(db)
|
||||
retrieveDateTime(db)
|
||||
retrieveDateTimeOutParam(db)
|
||||
}
|
||||
|
||||
func insertDateTime(db *sql.DB) {
|
||||
_, err := db.Exec("CREATE TABLE datetimeTable (timeCol TIME, dateCol DATE, smalldatetimeCol SMALLDATETIME, datetimeCol DATETIME, datetime2Col DATETIME2, datetimeoffsetCol DATETIMEOFFSET)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
stmt, err := db.Prepare("INSERT INTO datetimeTable VALUES(@p1, @p2, @p3, @p4, @p5, @p6)")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05.787-07:00")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var timeCol civil.Time = civil.TimeOf(tin)
|
||||
var dateCol civil.Date = civil.DateOf(tin)
|
||||
var smalldatetimeCol string = "2006-01-02 22:04:00"
|
||||
var datetimeCol mssql.DateTime1 = mssql.DateTime1(tin)
|
||||
var datetime2Col civil.DateTime = civil.DateTimeOf(tin)
|
||||
var datetimeoffsetCol mssql.DateTimeOffset = mssql.DateTimeOffset(tin)
|
||||
_, err = stmt.Exec(timeCol, dateCol, smalldatetimeCol, datetimeCol, datetime2Col, datetimeoffsetCol)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveDateTime(db *sql.DB) {
|
||||
rows, err := db.Query("SELECT timeCol, dateCol, smalldatetimeCol, datetimeCol, datetime2Col, datetimeoffsetCol FROM datetimeTable")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var c1, c2, c3, c4, c5, c6 time.Time
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&c1, &c2, &c3, &c4, &c5, &c6)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("c1: %+v; c2: %+v; c3: %+v; c4: %+v; c5: %+v; c6: %+v;\n", c1, c2, c3, c4, c5, c6)
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveDateTimeOutParam(db *sql.DB) {
|
||||
CreateProcSql := `
|
||||
CREATE PROCEDURE OutDatetimeProc
|
||||
@timeOutParam TIME OUTPUT,
|
||||
@dateOutParam DATE OUTPUT,
|
||||
@smalldatetimeOutParam SMALLDATETIME OUTPUT,
|
||||
@datetimeOutParam DATETIME OUTPUT,
|
||||
@datetime2OutParam DATETIME2 OUTPUT,
|
||||
@datetimeoffsetOutParam DATETIMEOFFSET OUTPUT
|
||||
AS
|
||||
SET NOCOUNT ON
|
||||
SET @timeOutParam = '22:04:05.7870015'
|
||||
SET @dateOutParam = '2006-01-02'
|
||||
SET @smalldatetimeOutParam = '2006-01-02 22:04:00'
|
||||
SET @datetimeOutParam = '2006-01-02 22:04:05.787'
|
||||
SET @datetime2OutParam = '2006-01-02 22:04:05.7870015'
|
||||
SET @datetimeoffsetOutParam = '2006-01-02 22:04:05.7870015 -07:00'`
|
||||
_, err := db.Exec(CreateProcSql)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var (
|
||||
timeOutParam, datetime2OutParam, datetimeoffsetOutParam mssql.DateTimeOffset
|
||||
dateOutParam, datetimeOutParam mssql.DateTime1
|
||||
smalldatetimeOutParam string
|
||||
)
|
||||
_, err = db.Exec("OutDatetimeProc",
|
||||
sql.Named("timeOutParam", sql.Out{Dest: &timeOutParam}),
|
||||
sql.Named("dateOutParam", sql.Out{Dest: &dateOutParam}),
|
||||
sql.Named("smalldatetimeOutParam", sql.Out{Dest: &smalldatetimeOutParam}),
|
||||
sql.Named("datetimeOutParam", sql.Out{Dest: &datetimeOutParam}),
|
||||
sql.Named("datetime2OutParam", sql.Out{Dest: &datetime2OutParam}),
|
||||
sql.Named("datetimeoffsetOutParam", sql.Out{Dest: &datetimeoffsetOutParam}))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("timeOutParam: %+v; dateOutParam: %+v; smalldatetimeOutParam: %s; datetimeOutParam: %+v; datetime2OutParam: %+v; datetimeoffsetOutParam: %+v;\n", time.Time(timeOutParam), time.Time(dateOutParam), smalldatetimeOutParam, time.Time(datetimeOutParam), time.Time(datetime2OutParam), time.Time(datetimeoffsetOutParam))
|
||||
}
|
107
vendor/github.com/denisenkom/go-mssqldb/decimal_test.go
generated
vendored
Normal file
107
vendor/github.com/denisenkom/go-mssqldb/decimal_test.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
values := []struct {
|
||||
dec Decimal
|
||||
s string
|
||||
}{
|
||||
{Decimal{positive: true, prec: 10, scale: 0, integer: [4]uint32{1, 0, 0, 0}}, "1"},
|
||||
{Decimal{positive: false, prec: 10, scale: 0, integer: [4]uint32{1, 0, 0, 0}}, "-1"},
|
||||
{Decimal{positive: true, prec: 10, scale: 1, integer: [4]uint32{1, 0, 0, 0}}, "0.1"},
|
||||
{Decimal{positive: true, prec: 10, scale: 2, integer: [4]uint32{1, 0, 0, 0}}, "0.01"},
|
||||
{Decimal{positive: false, prec: 10, scale: 1, integer: [4]uint32{1, 0, 0, 0}}, "-0.1"},
|
||||
{Decimal{positive: true, prec: 10, scale: 2, integer: [4]uint32{100, 0, 0, 0}}, "1.00"},
|
||||
{Decimal{positive: false, prec: 10, scale: 2, integer: [4]uint32{100, 0, 0, 0}}, "-1.00"},
|
||||
{Decimal{positive: true, prec: 30, scale: 0, integer: [4]uint32{0, 1, 0, 0}}, "4294967296"}, // 2^32
|
||||
{Decimal{positive: true, prec: 30, scale: 0, integer: [4]uint32{0, 0, 1, 0}}, "18446744073709551616"}, // 2^64
|
||||
{Decimal{positive: true, prec: 30, scale: 0, integer: [4]uint32{0, 1, 1, 0}}, "18446744078004518912"}, // 2^64+2^32
|
||||
}
|
||||
for _, v := range values {
|
||||
if v.dec.String() != v.s {
|
||||
t.Error("String values don't match ", v.dec.String(), v.s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFloat64(t *testing.T) {
|
||||
values := []struct {
|
||||
dec Decimal
|
||||
flt float64
|
||||
}{
|
||||
{Decimal{positive: true, prec: 1},
|
||||
0.0},
|
||||
{Decimal{positive: true, prec: 1, integer: [4]uint32{1}},
|
||||
1.0},
|
||||
{Decimal{positive: false, prec: 1, integer: [4]uint32{1}},
|
||||
-1.0},
|
||||
{Decimal{positive: true, prec: 1, scale: 1, integer: [4]uint32{5}},
|
||||
0.5},
|
||||
{Decimal{positive: true, prec: 38, integer: [4]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}},
|
||||
3.402823669209385e+38},
|
||||
{Decimal{positive: true, prec: 38, scale: 3, integer: [4]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}},
|
||||
3.402823669209385e+35},
|
||||
}
|
||||
for _, v := range values {
|
||||
if v.dec.ToFloat64() != v.flt {
|
||||
t.Error("ToFloat values don't match ", v.dec.ToFloat64(), v.flt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromFloat64(t *testing.T) {
|
||||
values := []struct {
|
||||
dec Decimal
|
||||
flt float64
|
||||
}{
|
||||
{Decimal{positive: true, prec: 20},
|
||||
0.0},
|
||||
{Decimal{positive: true, prec: 20, integer: [4]uint32{1}},
|
||||
1.0},
|
||||
{Decimal{positive: false, prec: 20, integer: [4]uint32{1}},
|
||||
-1.0},
|
||||
{Decimal{positive: true, prec: 20, scale: 1, integer: [4]uint32{5}},
|
||||
0.5},
|
||||
{Decimal{positive: true, prec: 20, integer: [4]uint32{0, 0, 0xfffff000, 0xffffffff}},
|
||||
3.402823669209384e+38},
|
||||
//{Decimal{positive: true, prec: 20, scale: 3, integer: [4]uint32{0, 0, 0xfffff000, 0xffffffff}},
|
||||
// 3.402823669209385e+35},
|
||||
}
|
||||
for _, v := range values {
|
||||
decfromflt, err := Float64ToDecimal(v.flt)
|
||||
if err == nil {
|
||||
if decfromflt != v.dec {
|
||||
t.Error("FromFloat values don't match ", decfromflt, v.dec)
|
||||
}
|
||||
} else {
|
||||
t.Error("Float64ToDecimal failed with error:", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
_, err := Float64ToDecimal(math.NaN())
|
||||
if err == nil {
|
||||
t.Error("Expected to get error for conversion from NaN, but didn't")
|
||||
}
|
||||
|
||||
_, err = Float64ToDecimal(math.Inf(1))
|
||||
if err == nil {
|
||||
t.Error("Expected to get error for conversion from positive infinity, but didn't")
|
||||
}
|
||||
|
||||
_, err = Float64ToDecimal(math.Inf(-1))
|
||||
if err == nil {
|
||||
t.Error("Expected to get error for conversion from negative infinity, but didn't")
|
||||
}
|
||||
_, err = Float64ToDecimal(3.402823669209386e+38)
|
||||
if err == nil {
|
||||
t.Error("Expected to get error for conversion from too big number, but didn't")
|
||||
}
|
||||
_, err = Float64ToDecimal(-3.402823669209386e+38)
|
||||
if err == nil {
|
||||
t.Error("Expected to get error for conversion from too big number, but didn't")
|
||||
}
|
||||
}
|
70
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-handle-date-and-time-types.md
generated
vendored
Normal file
70
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-handle-date-and-time-types.md
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
# How to Handle Date and Time Types
|
||||
|
||||
SQL Server has six date and time datatypes: date, time, smalldatetime, datetime, datetime2 and datetimeoffset. Some of these datatypes may contain more information than others (for example, datetimeoffset is the only type that has time zone awareness), higher ranges, or larger precisions. In a Go application using the mssql driver, the data types used to hold these data must be chosen carefully so no data is lost.
|
||||
|
||||
## Inserting Date and Time Data
|
||||
|
||||
The following is a list of datatypes that can be used to insert data into a SQL Server date and/or time type column:
|
||||
- string
|
||||
- time.Time
|
||||
- mssql.DateTime1
|
||||
- mssql.DateTimeOffset
|
||||
- "cloud.google.com/go/civil".Date
|
||||
- "cloud.google.com/go/civil".Time
|
||||
- "cloud.google.com/go/civil".DateTime
|
||||
|
||||
`time.Time` and `mssql.DateTimeOffset` contain the most information (time zone and over 7 digits precision). Designed to match the SQL Server `datetime` type, `mssql.DateTime1` does not have time zone information, only has up to 3 digits precision and they are rouded to increments of .000, .003 or .007 seconds when the data is passed to SQL Server. If you use `mssql.DateTime1` to hold time zone information or very precised time data (more than 3 decimal digits), you will see data lost when inserting into columns with types that can hold more information. For example:
|
||||
|
||||
```
|
||||
// all these types have up to 7 digits precision points
|
||||
// datetimeoffset can hold information about time zone
|
||||
_, err := db.Exec("CREATE TABLE datetimeTable (timeCol TIME, datetime2Col DATETIME2, datetimeoffsetCol DATETIMEOFFSET)")
|
||||
stmt, err := db.Prepare("INSERT INTO datetimeTable VALUES (@p1, @p2, @p3))
|
||||
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05.7870015-07:00") // data containing 7 decimal digits and has time zone awareness
|
||||
param := mssql.DateTime1(tin) // data is stored in mssql.DateTime1 type
|
||||
_, err = stmt.Exec(param, param, param)
|
||||
// result in database:
|
||||
// timeCol: 22:04:05.7866667
|
||||
// datetime2Col: 2006-01-02 22:04:05.7866667
|
||||
// datetimeoffsetCol: 2006-01-02 22:04:05.7866667 +00:00
|
||||
// precisions are lost in all columns. Also, time zone information is lost in datetimeoffsetCol
|
||||
```
|
||||
|
||||
`"cloud.google.com/go/civil".DateTime` does not have time zone information. `"cloud.google.com/go/civil".Date` only has the date information, and `"cloud.google.com/go/civil".Time` only has the time information. `string` can also be used to insert data into date and time types columns, but you have to make sure the format is accepted by SQL Server.
|
||||
|
||||
## Retrieving Date and Time Data
|
||||
|
||||
The following is a list of datatypes that can be used to retrieved data from a SQL Server date and/or time type column:
|
||||
- string
|
||||
- sql.RawBytes
|
||||
- time.Time
|
||||
- mssql.DateTime1
|
||||
- mssql.DateTiimeOffset
|
||||
|
||||
When using these data types to retrieve information from a date and/or time type column, you may end up with some extra unexpected information. For example, if you use Go type `time.Time` to retrieve information from a SQL Server `date` column:
|
||||
|
||||
```
|
||||
var c2 time.Time
|
||||
rows, err := db.Query("SELECT dateCol FROM datetimeTable") // dateCol has data `2006-01-02`
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&c1)
|
||||
fmr.Printf("c2: %+v")
|
||||
// c2: 2006-01-02 00:00:00 +0000 UTC
|
||||
// you get extra time and time zone information defaulty set to 0
|
||||
}
|
||||
```
|
||||
|
||||
## Output parameters with Date and Time Data
|
||||
|
||||
The following is a list of datatypes that can be used as buffer to hold a output parameter of SQL Server date and/or time type
|
||||
- string
|
||||
- time.Time
|
||||
- mssql.DateTime1
|
||||
- mssql.DateTimeOffset
|
||||
|
||||
The only type that can be used to retrieve an output of `smalldatetime` is `string`, otherwise you will get a `mssql: Error converting data type datetimeoffset/datetime1 to smalldatetime` error. Furthermore, `string` and `mssql.DateTime1` are the only types that can be used to retrieve output of `datetime` type, otherwise you will get a `mssql: Error converting data type datetimeoffset to datetime` error.
|
||||
|
||||
Similar to retrieving data from a result set, when retrieving data as a output parameter, you may end up with some extra unexpected information when the Go type you use contains more information than the data you retrieved from SQL Server.
|
||||
|
||||
## Example
|
||||
[DateTime handling example](../datetimeoffset_example_test.go)
|
48
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-perform-bulk-imports.md
generated
vendored
Normal file
48
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-perform-bulk-imports.md
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
# How to perform bulk imports
|
||||
|
||||
To use the bulk imports feature in go-mssqldb, you need to import the sql and go-mssqldb packages.
|
||||
|
||||
```
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
```
|
||||
|
||||
The `mssql.CopyIn` function creates a string which can be prepared by passing it to `Prepare`. The string returned contains information such as the name of the table and columns to bulk import data into, and bulk options.
|
||||
|
||||
```
|
||||
bulkImportStr := mssql.CopyIn("tablename", mssql.BulkOptions{}, "column1", "column2", "column3")
|
||||
stmt, err := db.Prepare(bulkImportStr)
|
||||
```
|
||||
|
||||
Bulk options can be specified using the `mssql.BulkOptions` type. The following is how the `BulkOptions` type is defined:
|
||||
|
||||
```
|
||||
type BulkOptions struct {
|
||||
CheckConstraints bool
|
||||
FireTriggers bool
|
||||
KeepNulls bool
|
||||
KilobytesPerBatch int
|
||||
RowsPerBatch int
|
||||
Order []string
|
||||
Tablock bool
|
||||
}
|
||||
```
|
||||
|
||||
The statement can be executed many times to copy data into the table specified.
|
||||
|
||||
```
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err = stmt.Exec(col1Data[i], col2Data[i], col3Data[i])
|
||||
}
|
||||
```
|
||||
|
||||
After all the data is processed, call `Exec` once with no arguments to flush all the buffered data.
|
||||
|
||||
```
|
||||
_, err = stmt.Exec()
|
||||
```
|
||||
|
||||
## Example
|
||||
[Bulk import example](../bulkimport_example_test.go)
|
32
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-use-newconnector.md
generated
vendored
Normal file
32
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-use-newconnector.md
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# How to use the Connector object
|
||||
|
||||
A Connector holds information in a DSN and is ready to make a new connection at any time. Connector implements the database/sql/driver Connector interface so it can be passed to the database/sql `OpenDB` function. One property on the Connector is the `SessionInitSQL` field, which may be used to set any options that cannot be passed through a DSN string.
|
||||
|
||||
To use the Connector type, first you need to import the sql and go-mssqldb packages
|
||||
|
||||
```
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
```
|
||||
|
||||
Now you can create a Connector object by calling `NewConnector`, which creates a new connector from a DSN.
|
||||
|
||||
```
|
||||
dsn := "sqlserver://username:password@hostname/instance?database=databasename"
|
||||
connector, err := mssql.NewConnector(dsn)
|
||||
```
|
||||
|
||||
You can set `connector.SessionInitSQL` for any options that cannot be passed through in the dsn string.
|
||||
|
||||
`connector.SessionInitSQL = "SET ANSI_NULLS ON"`
|
||||
|
||||
Open a database by passing connector to `sql.OpenDB`.
|
||||
|
||||
`db := sql.OpenDB(connector)`
|
||||
|
||||
The returned DB maintains its own pool of idle connections. Now you can use the `sql.DB` object for querying and executing queries.
|
||||
|
||||
## Example
|
||||
[NewConnector example](../newconnector_example_test.go)
|
91
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-use-table-valued-parameters.md
generated
vendored
Normal file
91
vendor/github.com/denisenkom/go-mssqldb/doc/how-to-use-table-valued-parameters.md
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
# How to use Table-Valued Parameters
|
||||
|
||||
Table-valued parameters are declared by using user-defined table types. You can use table-valued parameters to send multiple rows of data to a Transact-SQL statement or a routine, such as a stored procedure or function, without creating a temporary table or many parameters.
|
||||
|
||||
To make use of the TVP functionality, first you need to create a table type, and a procedure or function to receive data from the table-valued parameter.
|
||||
|
||||
```
|
||||
createTVP = "CREATE TYPE LocationTableType AS TABLE (LocationName VARCHAR(50), CostRate INT)"
|
||||
_, err = db.Exec(createTable)
|
||||
|
||||
createProc = `
|
||||
CREATE PROCEDURE dbo.usp_InsertProductionLocation
|
||||
@TVP LocationTableType READONLY
|
||||
AS
|
||||
SET NOCOUNT ON
|
||||
INSERT INTO Location
|
||||
(
|
||||
Name,
|
||||
CostRate,
|
||||
Availability,
|
||||
ModifiedDate)
|
||||
SELECT *, 0,GETDATE()
|
||||
FROM @TVP`
|
||||
_, err = db.Exec(createProc)
|
||||
```
|
||||
|
||||
In your go application, create a struct that corresponds to the table type you have created. Create a slice of these structs which contain the data you want to pass to the stored procedure.
|
||||
|
||||
```
|
||||
type LocationTableTvp struct {
|
||||
LocationName string
|
||||
CostRate int64
|
||||
}
|
||||
|
||||
locationTableTypeData := []LocationTableTvp{
|
||||
{
|
||||
LocationName: "Alberta",
|
||||
CostRate: 0,
|
||||
},
|
||||
{
|
||||
LocationName: "British Columbia",
|
||||
CostRate: 1,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Create a `mssql.TVP` object, and pass the slice of structs into the `Value` member. Set `TypeName` to the table type name.
|
||||
|
||||
```
|
||||
tvpType := mssql.TVP{
|
||||
TypeName: "LocationTableType",
|
||||
Value: locationTableTypeData,
|
||||
}
|
||||
```
|
||||
|
||||
Finally, execute the stored procedure and pass the `mssql.TVPType` object you have created as a parameter.
|
||||
|
||||
`_, err = db.Exec("exec dbo.usp_InsertProductionLocation @TVP;", sql.Named("TVP", tvpType))`
|
||||
|
||||
## Using Tags to Omit Fields in a Struct
|
||||
|
||||
Sometimes users may find it useful to include fields in the struct that do not have corresponding columns in the table type. The driver supports this feature by using tags. To omit a field from a struct, use the `json` or `tvp` tag key and the `"-"` tag value.
|
||||
|
||||
For example, the user wants to define a struct with two more fields: `LocationCountry` and `Currency`. However, the `LocationTableType` table type do not have these corresponding columns. The user can omit the two new fields from being read by using the `json` or `tvp` tag.
|
||||
|
||||
```
|
||||
type LocationTableTvpDetailed struct {
|
||||
LocationName string
|
||||
LocationCountry string `tvp:"-"`
|
||||
CostRate int64
|
||||
Currency string `json:"-"`
|
||||
}
|
||||
```
|
||||
|
||||
The `tvp` tag is the highest priority. Therefore if there is a field with tag `json:"-" tvp:"any"`, the field is not omitted. The following struct demonstrates different scenarios of using the `json` and `tvp` tags.
|
||||
|
||||
```
|
||||
type T struct {
|
||||
F1 string `json:"f1" tvp:"f1"` // not omitted
|
||||
F2 string `json:"-" tvp:"f2"` // tvp tag takes precedence; not omitted
|
||||
F3 string `json:"f3" tvp:"-"` // tvp tag takes precedence; omitted
|
||||
F4 string `json:"-" tvp:"-"` // omitted
|
||||
F5 string `json:"f5"` // not omitted
|
||||
F6 string `json:"-"` // omitted
|
||||
F7 string `tvp:"f7"` // not omitted
|
||||
F8 string `tvp:"-"` // omitted
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
[TVPType example](../tvp_example_test.go)
|
38
vendor/github.com/denisenkom/go-mssqldb/error_example_test.go
generated
vendored
Normal file
38
vendor/github.com/denisenkom/go-mssqldb/error_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package mssql
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleError_1() {
|
||||
// call a function that might return a mssql error
|
||||
err := callUsingMSSQL()
|
||||
|
||||
type ErrorWithNumber interface {
|
||||
SQLErrorNumber() int32
|
||||
}
|
||||
|
||||
if errorWithNumber, ok := err.(ErrorWithNumber); ok {
|
||||
if errorWithNumber.SQLErrorNumber() == 1205 {
|
||||
fmt.Println("deadlock error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleError_2() {
|
||||
// call a function that might return a mssql error
|
||||
err := callUsingMSSQL()
|
||||
|
||||
type SQLError interface {
|
||||
SQLErrorNumber() int32
|
||||
SQLErrorMessage() string
|
||||
}
|
||||
|
||||
if sqlError, ok := err.(SQLError); ok {
|
||||
if sqlError.SQLErrorNumber() == 1205 {
|
||||
fmt.Println("deadlock error", sqlError.SQLErrorMessage())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callUsingMSSQL() error {
|
||||
return nil
|
||||
}
|
111
vendor/github.com/denisenkom/go-mssqldb/examples/bulk/bulk.go
generated
vendored
Normal file
111
vendor/github.com/denisenkom/go-mssqldb/examples/bulk/bulk.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = flag.Bool("debug", true, "enable debugging")
|
||||
password = flag.String("password", "osmtest", "the database password")
|
||||
port *int = flag.Int("port", 1433, "the database port")
|
||||
server = flag.String("server", "localhost", "the database server")
|
||||
user = flag.String("user", "osmtest", "the database user")
|
||||
database = flag.String("database", "bulktest", "the database name")
|
||||
)
|
||||
|
||||
/*
|
||||
CREATE TABLE test_table(
|
||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[test_nvarchar] [nvarchar](50) NULL,
|
||||
[test_varchar] [varchar](50) NULL,
|
||||
[test_float] [float] NULL,
|
||||
[test_datetime2_3] [datetime2](3) NULL,
|
||||
[test_bitn] [bit] NULL,
|
||||
[test_bigint] [bigint] NOT NULL,
|
||||
[test_geom] [geometry] NULL,
|
||||
CONSTRAINT [PK_table_test_id] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[id] ASC
|
||||
) ON [PRIMARY]);
|
||||
*/
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
fmt.Printf(" database:%s\n", *database)
|
||||
}
|
||||
|
||||
connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s", *server, *user, *password, *port, *database)
|
||||
if *debug {
|
||||
fmt.Printf("connString:%s\n", connString)
|
||||
}
|
||||
conn, err := sql.Open("mssql", connString)
|
||||
if err != nil {
|
||||
log.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
txn, err := conn.Begin()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, err := txn.Prepare(mssql.CopyIn("test_table", mssql.BulkOptions{}, "test_varchar", "test_nvarchar", "test_float", "test_bigint"))
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err = stmt.Exec(generateString(0, 30), generateStringUnicode(0, 30), i, i)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
result, err := stmt.Exec()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = stmt.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = txn.Commit()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rowCount, _ := result.RowsAffected()
|
||||
log.Printf("%d row copied\n", rowCount)
|
||||
log.Printf("bye\n")
|
||||
|
||||
}
|
||||
|
||||
func generateString(x int, n int) string {
|
||||
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letters[i%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
func generateStringUnicode(x int, n int) string {
|
||||
letters := "ab©💾é?ghïjklmnopqЯ☀tuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letters[i%len(letters)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
57
vendor/github.com/denisenkom/go-mssqldb/examples/simple/simple.go
generated
vendored
Normal file
57
vendor/github.com/denisenkom/go-mssqldb/examples/simple/simple.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = flag.Bool("debug", false, "enable debugging")
|
||||
password = flag.String("password", "", "the database password")
|
||||
port *int = flag.Int("port", 1433, "the database port")
|
||||
server = flag.String("server", "", "the database server")
|
||||
user = flag.String("user", "", "the database user")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d", *server, *user, *password, *port)
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
conn, err := sql.Open("mssql", connString)
|
||||
if err != nil {
|
||||
log.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
stmt, err := conn.Prepare("select 1, 'abc'")
|
||||
if err != nil {
|
||||
log.Fatal("Prepare failed:", err.Error())
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
row := stmt.QueryRow()
|
||||
var somenumber int64
|
||||
var somechars string
|
||||
err = row.Scan(&somenumber, &somechars)
|
||||
if err != nil {
|
||||
log.Fatal("Scan failed:", err.Error())
|
||||
}
|
||||
fmt.Printf("somenumber:%d\n", somenumber)
|
||||
fmt.Printf("somechars:%s\n", somechars)
|
||||
|
||||
fmt.Printf("bye\n")
|
||||
}
|
119
vendor/github.com/denisenkom/go-mssqldb/examples/tsql/tsql.go
generated
vendored
Normal file
119
vendor/github.com/denisenkom/go-mssqldb/examples/tsql/tsql.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
userid = flag.String("U", "", "login_id")
|
||||
password = flag.String("P", "", "password")
|
||||
server = flag.String("S", "localhost", "server_name[\\instance_name]")
|
||||
database = flag.String("d", "", "db_name")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
dsn := "server=" + *server + ";user id=" + *userid + ";password=" + *password + ";database=" + *database
|
||||
db, err := sql.Open("mssql", dsn)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot connect: ", err.Error())
|
||||
return
|
||||
}
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
fmt.Println("Cannot connect: ", err.Error())
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
r := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
_, err = os.Stdout.Write([]byte("> "))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
cmd, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
fmt.Println()
|
||||
return
|
||||
}
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
err = exec(db, cmd)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func exec(db *sql.DB, cmd string) error {
|
||||
rows, err := db.Query(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cols == nil {
|
||||
return nil
|
||||
}
|
||||
vals := make([]interface{}, len(cols))
|
||||
for i := 0; i < len(cols); i++ {
|
||||
vals[i] = new(interface{})
|
||||
if i != 0 {
|
||||
fmt.Print("\t")
|
||||
}
|
||||
fmt.Print(cols[i])
|
||||
}
|
||||
fmt.Println()
|
||||
for rows.Next() {
|
||||
err = rows.Scan(vals...)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
for i := 0; i < len(vals); i++ {
|
||||
if i != 0 {
|
||||
fmt.Print("\t")
|
||||
}
|
||||
printValue(vals[i].(*interface{}))
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
return rows.Err()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printValue(pval *interface{}) {
|
||||
switch v := (*pval).(type) {
|
||||
case nil:
|
||||
fmt.Print("NULL")
|
||||
case bool:
|
||||
if v {
|
||||
fmt.Print("1")
|
||||
} else {
|
||||
fmt.Print("0")
|
||||
}
|
||||
case []byte:
|
||||
fmt.Print(string(v))
|
||||
case time.Time:
|
||||
fmt.Print(v.Format("2006-01-02 15:04:05.999"))
|
||||
default:
|
||||
fmt.Print(v)
|
||||
}
|
||||
}
|
151
vendor/github.com/denisenkom/go-mssqldb/examples/tvp/tvp.go
generated
vendored
Normal file
151
vendor/github.com/denisenkom/go-mssqldb/examples/tvp/tvp.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/denisenkom/go-mssqldb"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = flag.Bool("debug", false, "enable debugging")
|
||||
password = flag.String("password", "", "the database password")
|
||||
port = flag.Int("port", 1433, "the database port")
|
||||
server = flag.String("server", "", "the database server")
|
||||
user = flag.String("user", "", "the database user")
|
||||
)
|
||||
|
||||
type TvpExample struct {
|
||||
MessageWithoutAnyTag string
|
||||
MessageWithJSONTag string `json:"message"`
|
||||
MessageWithTVPTag string `tvp:"message"`
|
||||
MessageJSONSkipWithTVPTag string `json:"-" tvp:"message"`
|
||||
|
||||
OmitFieldJSONTag string `json:"-"`
|
||||
OmitFieldTVPTag string `json:"any" tvp:"-"`
|
||||
OmitFieldTVPTag2 string `tvp:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
crateSchema = `create schema TestTVPSchema;`
|
||||
|
||||
dropSchema = `drop schema TestTVPSchema;`
|
||||
|
||||
createTVP = `
|
||||
CREATE TYPE TestTVPSchema.exampleTVP AS TABLE
|
||||
(
|
||||
message1 NVARCHAR(100),
|
||||
message2 NVARCHAR(100),
|
||||
message3 NVARCHAR(100),
|
||||
message4 NVARCHAR(100)
|
||||
)`
|
||||
|
||||
dropTVP = `DROP TYPE TestTVPSchema.exampleTVP;`
|
||||
|
||||
procedureWithTVP = `
|
||||
CREATE PROCEDURE ExecTVP
|
||||
@param1 TestTVPSchema.exampleTVP READONLY
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT * FROM @param1;
|
||||
END;
|
||||
`
|
||||
|
||||
dropProcedure = `drop PROCEDURE ExecTVP`
|
||||
|
||||
execTvp = `exec ExecTVP @param1;`
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d", *server, *user, *password, *port)
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
conn, err := sql.Open("sqlserver", connString)
|
||||
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)
|
||||
|
||||
exampleData := []TvpExample{
|
||||
{
|
||||
MessageWithoutAnyTag: "Hello1",
|
||||
MessageWithJSONTag: "Hello2",
|
||||
MessageWithTVPTag: "Hello3",
|
||||
MessageJSONSkipWithTVPTag: "Hello4",
|
||||
OmitFieldJSONTag: "Hello5",
|
||||
OmitFieldTVPTag: "Hello6",
|
||||
OmitFieldTVPTag2: "Hello7",
|
||||
},
|
||||
{
|
||||
MessageWithoutAnyTag: "World1",
|
||||
MessageWithJSONTag: "World2",
|
||||
MessageWithTVPTag: "World3",
|
||||
MessageJSONSkipWithTVPTag: "World4",
|
||||
OmitFieldJSONTag: "World5",
|
||||
OmitFieldTVPTag: "World6",
|
||||
OmitFieldTVPTag2: "World7",
|
||||
},
|
||||
}
|
||||
|
||||
tvpType := mssql.TVP{
|
||||
TypeName: "TestTVPSchema.exampleTVP",
|
||||
Value: exampleData,
|
||||
}
|
||||
|
||||
rows, err := conn.Query(execTvp,
|
||||
sql.Named("param1", tvpType),
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
tvpResult := make([]TvpExample, 0)
|
||||
for rows.Next() {
|
||||
tvpExample := TvpExample{}
|
||||
err = rows.Scan(&tvpExample.MessageWithoutAnyTag,
|
||||
&tvpExample.MessageWithJSONTag,
|
||||
&tvpExample.MessageWithTVPTag,
|
||||
&tvpExample.MessageJSONSkipWithTVPTag,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
tvpResult = append(tvpResult, tvpExample)
|
||||
}
|
||||
fmt.Println(tvpResult)
|
||||
}
|
10
vendor/github.com/denisenkom/go-mssqldb/go.mod
generated
vendored
Normal file
10
vendor/github.com/denisenkom/go-mssqldb/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
module github.com/denisenkom/go-mssqldb
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.37.4
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
168
vendor/github.com/denisenkom/go-mssqldb/go.sum
generated
vendored
Normal file
168
vendor/github.com/denisenkom/go-mssqldb/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.2 h1:4y4L7BdHenTfZL0HervofNTHh9Ad6mNX72cQvl+5eH0=
|
||||
cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
|
||||
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
|
||||
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
22
vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go
generated
vendored
22
vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go
generated
vendored
|
@ -112,7 +112,7 @@ func (c *Conn) CheckNamedValue(nv *driver.NamedValue) error {
|
|||
*v = 0 // By default the return value should be zero.
|
||||
c.returnStatus = v
|
||||
return driver.ErrRemoveArgument
|
||||
case TVPType:
|
||||
case TVP:
|
||||
return nil
|
||||
default:
|
||||
var err error
|
||||
|
@ -162,15 +162,27 @@ func (s *Stmt) makeParamExtra(val driver.Value) (res param, err error) {
|
|||
case sql.Out:
|
||||
res, err = s.makeParam(val.Dest)
|
||||
res.Flags = fByRevValue
|
||||
case TVPType:
|
||||
case TVP:
|
||||
err = val.check()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.ti.UdtInfo.TypeName = val.TVPTypeName
|
||||
res.ti.UdtInfo.SchemaName = val.TVPScheme
|
||||
schema, name, errGetName := getSchemeAndName(val.TypeName)
|
||||
if errGetName != nil {
|
||||
return
|
||||
}
|
||||
res.ti.UdtInfo.TypeName = name
|
||||
res.ti.UdtInfo.SchemaName = schema
|
||||
res.ti.TypeId = typeTvp
|
||||
res.buffer, err = val.encode()
|
||||
columnStr, tvpFieldIndexes, errCalTypes := val.columnTypes()
|
||||
if errCalTypes != nil {
|
||||
err = errCalTypes
|
||||
return
|
||||
}
|
||||
res.buffer, err = val.encode(schema, name, columnStr, tvpFieldIndexes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res.ti.Size = len(res.buffer)
|
||||
|
||||
default:
|
||||
|
|
39
vendor/github.com/denisenkom/go-mssqldb/mssql_test.go
generated
vendored
Normal file
39
vendor/github.com/denisenkom/go-mssqldb/mssql_test.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBadOpen(t *testing.T) {
|
||||
drv := driverWithProcess(t)
|
||||
_, err := drv.open(context.Background(), "port=bad")
|
||||
if err == nil {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsProc(t *testing.T) {
|
||||
list := []struct {
|
||||
s string
|
||||
is bool
|
||||
}{
|
||||
{"proc", true},
|
||||
{"select 1;", false},
|
||||
{"select 1", false},
|
||||
{"[proc 1]", true},
|
||||
{"[proc\n1]", false},
|
||||
{"schema.name", true},
|
||||
{"[schema].[name]", true},
|
||||
{"schema.[name]", true},
|
||||
{"[schema].name", true},
|
||||
{"schema.[proc name]", true},
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
got := isProc(item.s)
|
||||
if got != item.is {
|
||||
t.Errorf("for %q, got %t want %t", item.s, got, item.is)
|
||||
}
|
||||
}
|
||||
}
|
143
vendor/github.com/denisenkom/go-mssqldb/newconnector_example_test.go
generated
vendored
Normal file
143
vendor/github.com/denisenkom/go-mssqldb/newconnector_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
mssql "github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = flag.Bool("debug", false, "enable debugging")
|
||||
password = flag.String("password", "", "the database password")
|
||||
port *int = flag.Int("port", 1433, "the database port")
|
||||
server = flag.String("server", "", "the database server")
|
||||
user = flag.String("user", "", "the database user")
|
||||
)
|
||||
|
||||
const (
|
||||
createTableSql = "CREATE TABLE TestAnsiNull (bitcol bit, charcol char(1));"
|
||||
dropTableSql = "IF OBJECT_ID('TestAnsiNull', 'U') IS NOT NULL DROP TABLE TestAnsiNull;"
|
||||
insertQuery1 = "INSERT INTO TestAnsiNull VALUES (0, NULL);"
|
||||
insertQuery2 = "INSERT INTO TestAnsiNull VALUES (1, 'a');"
|
||||
selectNullFilter = "SELECT bitcol FROM TestAnsiNull WHERE charcol = NULL;"
|
||||
selectNotNullFilter = "SELECT bitcol FROM TestAnsiNull WHERE charcol <> NULL;"
|
||||
)
|
||||
|
||||
func makeConnURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "sqlserver",
|
||||
Host: *server + ":" + strconv.Itoa(*port),
|
||||
User: url.UserPassword(*user, *password),
|
||||
}
|
||||
}
|
||||
|
||||
// This example shows the usage of Connector type
|
||||
func ExampleConnector() {
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := makeConnURL().String()
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
|
||||
// Create a new connector object by calling NewConnector
|
||||
connector, err := mssql.NewConnector(connString)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Use SessionInitSql to set any options that cannot be set with the dsn string
|
||||
// With ANSI_NULLS set to ON, compare NULL data with = NULL or <> NULL will return 0 rows
|
||||
connector.SessionInitSQL = "SET ANSI_NULLS ON"
|
||||
|
||||
// Pass connector to sql.OpenDB to get a sql.DB object
|
||||
db := sql.OpenDB(connector)
|
||||
defer db.Close()
|
||||
|
||||
// Create and populate table
|
||||
_, err = db.Exec(createTableSql)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer db.Exec(dropTableSql)
|
||||
_, err = db.Exec(insertQuery1)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
_, err = db.Exec(insertQuery2)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
var bitval bool
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// (*Row) Scan should return ErrNoRows since ANSI_NULLS is set to ON
|
||||
err = db.QueryRowContext(ctx, selectNullFilter).Scan(&bitval)
|
||||
if err != nil {
|
||||
if err.Error() != "sql: no rows in result set" {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Println("Expects an ErrNoRows error. No error is returned")
|
||||
return
|
||||
}
|
||||
|
||||
// (*Row) Scan should return ErrNoRows since ANSI_NULLS is set to ON
|
||||
err = db.QueryRowContext(ctx, selectNotNullFilter).Scan(&bitval)
|
||||
if err != nil {
|
||||
if err.Error() != "sql: no rows in result set" {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Println("Expects an ErrNoRows error. No error is returned")
|
||||
return
|
||||
}
|
||||
|
||||
// Set ANSI_NULLS to OFF
|
||||
connector.SessionInitSQL = "SET ANSI_NULLS OFF"
|
||||
|
||||
// (*Row) Scan should copy data to bitval
|
||||
err = db.QueryRowContext(ctx, selectNullFilter).Scan(&bitval)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if bitval != false {
|
||||
log.Println("Incorrect value retrieved.")
|
||||
return
|
||||
}
|
||||
|
||||
// (*Row) Scan should copy data to bitval
|
||||
err = db.QueryRowContext(ctx, selectNotNullFilter).Scan(&bitval)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if bitval != true {
|
||||
log.Println("Incorrect value retrieved.")
|
||||
return
|
||||
}
|
||||
}
|
76
vendor/github.com/denisenkom/go-mssqldb/ntlm_test.go
generated
vendored
Normal file
76
vendor/github.com/denisenkom/go-mssqldb/ntlm_test.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
// +build !windows
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLMOWFv1(t *testing.T) {
|
||||
hash := lmHash("Password")
|
||||
val := [21]byte{
|
||||
0xe5, 0x2c, 0xac, 0x67, 0x41, 0x9a, 0x9a, 0x22,
|
||||
0x4a, 0x3b, 0x10, 0x8f, 0x3f, 0xa6, 0xcb, 0x6d,
|
||||
0, 0, 0, 0, 0,
|
||||
}
|
||||
if hash != val {
|
||||
t.Errorf("got:\n%sexpected:\n%s", hex.Dump(hash[:]), hex.Dump(val[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNTLMOWFv1(t *testing.T) {
|
||||
hash := ntlmHash("Password")
|
||||
val := [21]byte{
|
||||
0xa4, 0xf4, 0x9c, 0x40, 0x65, 0x10, 0xbd, 0xca, 0xb6, 0x82, 0x4e, 0xe7, 0xc3, 0x0f, 0xd8, 0x52,
|
||||
0, 0, 0, 0, 0,
|
||||
}
|
||||
if hash != val {
|
||||
t.Errorf("got:\n%sexpected:\n%s", hex.Dump(hash[:]), hex.Dump(val[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNTLMv1Response(t *testing.T) {
|
||||
challenge := [8]byte{
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
|
||||
}
|
||||
nt := ntResponse(challenge, "Password")
|
||||
val := [24]byte{
|
||||
0x67, 0xc4, 0x30, 0x11, 0xf3, 0x02, 0x98, 0xa2, 0xad, 0x35, 0xec, 0xe6, 0x4f, 0x16, 0x33, 0x1c,
|
||||
0x44, 0xbd, 0xbe, 0xd9, 0x27, 0x84, 0x1f, 0x94,
|
||||
}
|
||||
if nt != val {
|
||||
t.Errorf("got:\n%sexpected:\n%s", hex.Dump(nt[:]), hex.Dump(val[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLMv1Response(t *testing.T) {
|
||||
challenge := [8]byte{
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
|
||||
}
|
||||
nt := lmResponse(challenge, "Password")
|
||||
val := [24]byte{
|
||||
0x98, 0xde, 0xf7, 0xb8, 0x7f, 0x88, 0xaa, 0x5d, 0xaf, 0xe2, 0xdf, 0x77, 0x96, 0x88, 0xa1, 0x72,
|
||||
0xde, 0xf1, 0x1c, 0x7d, 0x5c, 0xcd, 0xef, 0x13,
|
||||
}
|
||||
if nt != val {
|
||||
t.Errorf("got:\n%sexpected:\n%s", hex.Dump(nt[:]), hex.Dump(val[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNTLMSessionResponse(t *testing.T) {
|
||||
challenge := [8]byte{
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
|
||||
}
|
||||
nonce := [8]byte{
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
}
|
||||
nt := ntlmSessionResponse(nonce, challenge, "Password")
|
||||
val := [24]byte{
|
||||
0x75, 0x37, 0xf8, 0x03, 0xae, 0x36, 0x71, 0x28, 0xca, 0x45, 0x82, 0x04, 0xbd, 0xe7, 0xca, 0xf8,
|
||||
0x1e, 0x97, 0xed, 0x26, 0x83, 0x26, 0x72, 0x32,
|
||||
}
|
||||
if nt != val {
|
||||
t.Errorf("got:\n%sexpected:\n%s", hex.Dump(nt[:]), hex.Dump(val[:]))
|
||||
}
|
||||
}
|
60
vendor/github.com/denisenkom/go-mssqldb/parser_test.go
generated
vendored
Normal file
60
vendor/github.com/denisenkom/go-mssqldb/parser_test.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseParams(t *testing.T) {
|
||||
values := []struct {
|
||||
s string
|
||||
d string
|
||||
n int
|
||||
}{
|
||||
{"select ?", "select @p1", 1},
|
||||
{"select ?, ?", "select @p1, @p2", 2},
|
||||
{"select ? -- ?", "select @p1 -- ?", 1},
|
||||
{"select ? -- ?\n, ?", "select @p1 -- ?\n, @p2", 2},
|
||||
{"select ? - ?", "select @p1 - @p2", 2},
|
||||
{"select ? /* ? */, ?", "select @p1 /* ? */, @p2", 2},
|
||||
{"select ? /* ? * ? */, ?", "select @p1 /* ? * ? */, @p2", 2},
|
||||
{"select \"foo?\", [foo?], 'foo?', ?", "select \"foo?\", [foo?], 'foo?', @p1", 1},
|
||||
{"select \"x\"\"y\", [x]]y], 'x''y', ?", "select \"x\"\"y\", [x]]y], 'x''y', @p1", 1},
|
||||
{"select \"foo?\", ?", "select \"foo?\", @p1", 1},
|
||||
{"select 'foo?', ?", "select 'foo?', @p1", 1},
|
||||
{"select [foo?], ?", "select [foo?], @p1", 1},
|
||||
{"select $1", "select @p1", 1},
|
||||
{"select $1, $2", "select @p1, @p2", 2},
|
||||
{"select $1, $1", "select @p1, @p1", 1},
|
||||
{"select :1", "select @p1", 1},
|
||||
{"select :1, :2", "select @p1, @p2", 2},
|
||||
{"select :1, :1", "select @p1, @p1", 1},
|
||||
{"select ?1", "select @p1", 1},
|
||||
{"select ?1, ?2", "select @p1, @p2", 2},
|
||||
{"select ?1, ?1", "select @p1, @p1", 1},
|
||||
{"select $12", "select @p12", 12},
|
||||
{"select ? /* ? /* ? */ ? */ ?", "select @p1 /* ? /* ? */ ? */ @p2", 2},
|
||||
{"select ? /* ? / ? */ ?", "select @p1 /* ? / ? */ @p2", 2},
|
||||
{"select $", "select $", 0},
|
||||
{"select x::y", "select x:@y", 1},
|
||||
{"select '", "select '", 0},
|
||||
{"select \"", "select \"", 0},
|
||||
{"select [", "select [", 0},
|
||||
{"select []", "select []", 0},
|
||||
{"select -", "select -", 0},
|
||||
{"select /", "select /", 0},
|
||||
{"select 1/1", "select 1/1", 0},
|
||||
{"select /*", "select /*", 0},
|
||||
{"select /**", "select /**", 0},
|
||||
{"select /*/", "select /*/", 0},
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
d, n := parseParams(v.s)
|
||||
if d != v.d {
|
||||
t.Errorf("Parse params don't match for %s, got %s but expected %s", v.s, d, v.d)
|
||||
}
|
||||
if n != v.n {
|
||||
t.Errorf("Parse number of params don't match for %s, got %d but expected %d", v.s, n, v.n)
|
||||
}
|
||||
}
|
||||
}
|
189
vendor/github.com/denisenkom/go-mssqldb/queries_go110_test.go
generated
vendored
Normal file
189
vendor/github.com/denisenkom/go-mssqldb/queries_go110_test.go
generated
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/civil"
|
||||
)
|
||||
|
||||
func TestSessionInitSQL(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
|
||||
d := &Driver{}
|
||||
connector, err := d.OpenConnector(makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Fatal("unable to open connector", err)
|
||||
}
|
||||
|
||||
// Do not use these settings in your application
|
||||
// unless you know what they do.
|
||||
// Thes are for this unit test only.
|
||||
//
|
||||
// Sessions will be reset even if SessionInitSQL is not set.
|
||||
connector.SessionInitSQL = `
|
||||
SET XACT_ABORT ON; -- 16384
|
||||
SET ANSI_NULLS ON; -- 32
|
||||
SET ARITHIGNORE ON; -- 128
|
||||
`
|
||||
|
||||
pool := sql.OpenDB(connector)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var opt int32
|
||||
err = pool.QueryRowContext(ctx, `
|
||||
select Options = @@OPTIONS;
|
||||
`).Scan(&opt)
|
||||
if err != nil {
|
||||
t.Fatal("failed to run query", err)
|
||||
}
|
||||
mask := int32(16384 | 128 | 32)
|
||||
|
||||
if opt&mask != mask {
|
||||
t.Fatal("incorrect session settings", opt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameterTypes(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
pool, err := sql.Open("sqlserver", makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05-07:00")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var nv, v, nvcm, vcm, dt1, dt2, tm, d, dto string
|
||||
row := pool.QueryRow(`
|
||||
select
|
||||
nv = SQL_VARIANT_PROPERTY(@nv,'BaseType'),
|
||||
v = SQL_VARIANT_PROPERTY(@v,'BaseType'),
|
||||
@nvcm,
|
||||
@vcm,
|
||||
dt1 = SQL_VARIANT_PROPERTY(@dt1,'BaseType'),
|
||||
dt2 = SQL_VARIANT_PROPERTY(@dt2,'BaseType'),
|
||||
d = SQL_VARIANT_PROPERTY(@d,'BaseType'),
|
||||
tm = SQL_VARIANT_PROPERTY(@tm,'BaseType'),
|
||||
dto = SQL_VARIANT_PROPERTY(@dto,'BaseType')
|
||||
;
|
||||
`,
|
||||
sql.Named("nv", "base type nvarchar"),
|
||||
sql.Named("v", VarChar("base type varchar")),
|
||||
sql.Named("nvcm", NVarCharMax(strings.Repeat("x", 5000))),
|
||||
sql.Named("vcm", VarCharMax(strings.Repeat("x", 5000))),
|
||||
sql.Named("dt1", DateTime1(tin)),
|
||||
sql.Named("dt2", civil.DateTimeOf(tin)),
|
||||
sql.Named("d", civil.DateOf(tin)),
|
||||
sql.Named("tm", civil.TimeOf(tin)),
|
||||
sql.Named("dto", DateTimeOffset(tin)),
|
||||
)
|
||||
err = row.Scan(&nv, &v, &nvcm, &vcm, &dt1, &dt2, &d, &tm, &dto)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if nv != "nvarchar" {
|
||||
t.Errorf(`want "nvarchar" got %q`, nv)
|
||||
}
|
||||
if v != "varchar" {
|
||||
t.Errorf(`want "varchar" got %q`, v)
|
||||
}
|
||||
if nvcm != strings.Repeat("x", 5000) {
|
||||
t.Errorf(`incorrect value returned for nvarchar(max): %q`, nvcm)
|
||||
}
|
||||
if vcm != strings.Repeat("x", 5000) {
|
||||
t.Errorf(`incorrect value returned for varchar(max): %q`, vcm)
|
||||
}
|
||||
if dt1 != "datetime" {
|
||||
t.Errorf(`want "datetime" got %q`, dt1)
|
||||
}
|
||||
if dt2 != "datetime2" {
|
||||
t.Errorf(`want "datetime2" got %q`, dt2)
|
||||
}
|
||||
if d != "date" {
|
||||
t.Errorf(`want "date" got %q`, d)
|
||||
}
|
||||
if tm != "time" {
|
||||
t.Errorf(`want "time" got %q`, tm)
|
||||
}
|
||||
if dto != "datetimeoffset" {
|
||||
t.Errorf(`want "datetimeoffset" got %q`, dto)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParameterValues(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
pool, err := sql.Open("sqlserver", makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
sin := "high five"
|
||||
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05-07:00")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var nv, v, tgo, dt1, dt2, tm, d, dto string
|
||||
err = pool.QueryRow(`
|
||||
select
|
||||
nv = @nv,
|
||||
v = @v,
|
||||
tgo = @tgo,
|
||||
dt1 = convert(nvarchar(200), @dt1, 121),
|
||||
dt2 = convert(nvarchar(200), @dt2, 121),
|
||||
d = convert(nvarchar(200), @d, 121),
|
||||
tm = convert(nvarchar(200), @tm, 121),
|
||||
dto = convert(nvarchar(200), @dto, 121)
|
||||
;
|
||||
`,
|
||||
sql.Named("nv", sin),
|
||||
sql.Named("v", sin),
|
||||
sql.Named("tgo", tin),
|
||||
sql.Named("dt1", DateTime1(tin)),
|
||||
sql.Named("dt2", civil.DateTimeOf(tin)),
|
||||
sql.Named("d", civil.DateOf(tin)),
|
||||
sql.Named("tm", civil.TimeOf(tin)),
|
||||
sql.Named("dto", DateTimeOffset(tin)),
|
||||
).Scan(&nv, &v, &tgo, &dt1, &dt2, &d, &tm, &dto)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := sin; nv != want {
|
||||
t.Errorf(`want %q got %q`, want, nv)
|
||||
}
|
||||
if want := sin; v != want {
|
||||
t.Errorf(`want %q got %q`, want, v)
|
||||
}
|
||||
if want := "2006-01-02T22:04:05-07:00"; tgo != want {
|
||||
t.Errorf(`want %q got %q`, want, tgo)
|
||||
}
|
||||
if want := "2006-01-02 22:04:05.000"; dt1 != want {
|
||||
t.Errorf(`want %q got %q`, want, dt1)
|
||||
}
|
||||
if want := "2006-01-02 22:04:05.0000000"; dt2 != want {
|
||||
t.Errorf(`want %q got %q`, want, dt2)
|
||||
}
|
||||
if want := "2006-01-02"; d != want {
|
||||
t.Errorf(`want %q got %q`, want, d)
|
||||
}
|
||||
if want := "22:04:05.0000000"; tm != want {
|
||||
t.Errorf(`want %q got %q`, want, tm)
|
||||
}
|
||||
if want := "2006-01-02 22:04:05.0000000 -07:00"; dto != want {
|
||||
t.Errorf(`want %q got %q`, want, dto)
|
||||
}
|
||||
}
|
924
vendor/github.com/denisenkom/go-mssqldb/queries_go19_test.go
generated
vendored
Normal file
924
vendor/github.com/denisenkom/go-mssqldb/queries_go19_test.go
generated
vendored
Normal file
|
@ -0,0 +1,924 @@
|
|||
// +build go1.9
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOutputParam(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()
|
||||
|
||||
t.Run("sp with rows", func(t *testing.T) {
|
||||
sqltextcreate := `
|
||||
CREATE PROCEDURE spwithrows
|
||||
@intparam INT = NULL OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
-- return 2 rows
|
||||
SELECT @intparam
|
||||
union
|
||||
SELECT 20
|
||||
|
||||
-- set output parameter value
|
||||
SELECT @intparam = 10
|
||||
END;
|
||||
`
|
||||
sqltextdrop := `DROP PROCEDURE spwithrows;`
|
||||
sqltextrun := `spwithrows`
|
||||
|
||||
db.ExecContext(ctx, sqltextdrop)
|
||||
_, err = db.ExecContext(ctx, sqltextcreate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.ExecContext(ctx, sqltextdrop)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var intparam int = 5
|
||||
rows, err := db.QueryContext(ctx, sqltextrun,
|
||||
sql.Named("intparam", sql.Out{Dest: &intparam}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// reading first row
|
||||
if !rows.Next() {
|
||||
t.Error("Next returned false")
|
||||
}
|
||||
var rowval int
|
||||
err = rows.Scan(&rowval)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if rowval != 5 {
|
||||
t.Errorf("expected 5, got %d", rowval)
|
||||
}
|
||||
|
||||
// if uncommented would trigger race condition warning
|
||||
//if intparam != 10 {
|
||||
// t.Log("output parameter value is not yet 10, it is ", intparam)
|
||||
//}
|
||||
|
||||
// reading second row
|
||||
if !rows.Next() {
|
||||
t.Error("Next returned false")
|
||||
}
|
||||
err = rows.Scan(&rowval)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if rowval != 20 {
|
||||
t.Errorf("expected 20, got %d", rowval)
|
||||
}
|
||||
|
||||
if rows.Next() {
|
||||
t.Error("Next returned true but should return false after last row was returned")
|
||||
}
|
||||
|
||||
if intparam != 10 {
|
||||
t.Errorf("expected 10, got %d", intparam)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sp with no rows", func(t *testing.T) {
|
||||
sqltextcreate := `
|
||||
CREATE PROCEDURE abassign
|
||||
@aid INT = 5,
|
||||
@bid INT = NULL OUTPUT,
|
||||
@cstr NVARCHAR(2000) = NULL OUTPUT,
|
||||
@datetime datetime = NULL OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
SELECT @bid = @aid, @cstr = 'OK', @datetime = '2010-01-01T00:00:00';
|
||||
END;
|
||||
`
|
||||
sqltextdrop := `DROP PROCEDURE abassign;`
|
||||
sqltextrun := `abassign`
|
||||
|
||||
db.ExecContext(ctx, sqltextdrop)
|
||||
_, err = db.ExecContext(ctx, sqltextcreate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.ExecContext(ctx, sqltextdrop)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
t.Run("should work", func(t *testing.T) {
|
||||
var bout int64
|
||||
var cout string
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("aid", 5),
|
||||
sql.Named("bid", sql.Out{Dest: &bout}),
|
||||
sql.Named("cstr", sql.Out{Dest: &cout}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if bout != 5 {
|
||||
t.Errorf("expected 5, got %d", bout)
|
||||
}
|
||||
|
||||
if cout != "OK" {
|
||||
t.Errorf("expected OK, got %s", cout)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should work if aid is not passed", func(t *testing.T) {
|
||||
var bout int64
|
||||
var cout string
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: &bout}),
|
||||
sql.Named("cstr", sql.Out{Dest: &cout}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if bout != 5 {
|
||||
t.Errorf("expected 5, got %d", bout)
|
||||
}
|
||||
|
||||
if cout != "OK" {
|
||||
t.Errorf("expected OK, got %s", cout)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should work for DateTime1 parameter", func(t *testing.T) {
|
||||
tin, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05-07:00")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected, err := time.Parse(time.RFC3339, "2010-01-01T00:00:00-00:00")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var datetime_param DateTime1
|
||||
datetime_param = DateTime1(tin)
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("datetime", sql.Out{Dest: &datetime_param}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if time.Time(datetime_param).UTC() != expected.UTC() {
|
||||
t.Errorf("Datetime returned '%v' does not match expected value '%v'",
|
||||
time.Time(datetime_param).UTC(), expected.UTC())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("destination is not a pointer", func(t *testing.T) {
|
||||
var int_out int64
|
||||
var str_out string
|
||||
// test when destination is not a pointer
|
||||
_, actual := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: int_out}),
|
||||
sql.Named("cstr", sql.Out{Dest: &str_out}),
|
||||
)
|
||||
pattern := ".*destination not a pointer.*"
|
||||
match, err := regexp.MatchString(pattern, actual.Error())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should convert int64 to int", func(t *testing.T) {
|
||||
var bout int
|
||||
var cout string
|
||||
_, err := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: &bout}),
|
||||
sql.Named("cstr", sql.Out{Dest: &cout}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if bout != 5 {
|
||||
t.Errorf("expected 5, got %d", bout)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should fail if destination has invalid type", func(t *testing.T) {
|
||||
// Error type should not be supported
|
||||
var err_out Error
|
||||
_, err := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: &err_out}),
|
||||
)
|
||||
if err == nil {
|
||||
t.Error("Expected to fail but it didn't")
|
||||
}
|
||||
|
||||
// double inderection should not work
|
||||
var out_out = sql.Out{Dest: &err_out}
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: out_out}),
|
||||
)
|
||||
if err == nil {
|
||||
t.Error("Expected to fail but it didn't")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should fail if parameter has invalid type", func(t *testing.T) {
|
||||
// passing invalid parameter type
|
||||
var err_val Error
|
||||
_, err = db.ExecContext(ctx, sqltextrun, err_val)
|
||||
if err == nil {
|
||||
t.Error("Expected to fail but it didn't")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("destination is a nil pointer", func(t *testing.T) {
|
||||
var str_out string
|
||||
// test when destination is nil pointer
|
||||
_, actual := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: nil}),
|
||||
sql.Named("cstr", sql.Out{Dest: &str_out}),
|
||||
)
|
||||
pattern := ".*destination is a nil pointer.*"
|
||||
match, err := regexp.MatchString(pattern, actual.Error())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("destination is a nil pointer 2", func(t *testing.T) {
|
||||
var int_ptr *int
|
||||
_, actual := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: int_ptr}),
|
||||
)
|
||||
pattern := ".*destination is a nil pointer.*"
|
||||
match, err := regexp.MatchString(pattern, actual.Error())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pointer to a pointer", func(t *testing.T) {
|
||||
var str_out *string
|
||||
_, actual := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("cstr", sql.Out{Dest: &str_out}),
|
||||
)
|
||||
pattern := ".*destination is a pointer to a pointer.*"
|
||||
match, err := regexp.MatchString(pattern, actual.Error())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestOutputINOUTParam(t *testing.T) {
|
||||
sqltextcreate := `
|
||||
CREATE PROCEDURE abinout
|
||||
@aid INT = 1,
|
||||
@bid INT = 2 OUTPUT,
|
||||
@cstr NVARCHAR(2000) = NULL OUTPUT,
|
||||
@vout VARCHAR(2000) = NULL OUTPUT,
|
||||
@nullint INT = NULL OUTPUT,
|
||||
@nullfloat FLOAT = NULL OUTPUT,
|
||||
@nullstr NVARCHAR(10) = NULL OUTPUT,
|
||||
@nullbit BIT = NULL OUTPUT,
|
||||
@varbin VARBINARY(10) = NULL OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
SELECT
|
||||
@bid = @aid + @bid,
|
||||
@cstr = 'OK',
|
||||
@Vout = 'DREAM'
|
||||
;
|
||||
END;
|
||||
`
|
||||
sqltextdrop := `DROP PROCEDURE abinout;`
|
||||
sqltextrun := `abinout`
|
||||
|
||||
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()
|
||||
|
||||
db.ExecContext(ctx, sqltextdrop)
|
||||
_, err = db.ExecContext(ctx, sqltextcreate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.ExecContext(ctx, sqltextdrop)
|
||||
|
||||
t.Run("original test", func(t *testing.T) {
|
||||
var bout int64 = 3
|
||||
var cout string
|
||||
var vout VarChar
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("aid", 5),
|
||||
sql.Named("bid", sql.Out{Dest: &bout}),
|
||||
sql.Named("cstr", sql.Out{Dest: &cout}),
|
||||
sql.Named("vout", sql.Out{Dest: &vout}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if bout != 8 {
|
||||
t.Errorf("expected 8, got %d", bout)
|
||||
}
|
||||
|
||||
if cout != "OK" {
|
||||
t.Errorf("expected OK, got %s", cout)
|
||||
}
|
||||
if string(vout) != "DREAM" {
|
||||
t.Errorf("expected DREAM, got %s", vout)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test null values returned into nullable", func(t *testing.T) {
|
||||
var nullint sql.NullInt64
|
||||
var nullfloat sql.NullFloat64
|
||||
var nullstr sql.NullString
|
||||
var nullbit sql.NullBool
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("nullint", sql.Out{Dest: &nullint}),
|
||||
sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
|
||||
sql.Named("nullstr", sql.Out{Dest: &nullstr}),
|
||||
sql.Named("nullbit", sql.Out{Dest: &nullbit}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if nullint.Valid {
|
||||
t.Errorf("expected NULL, got %v", nullint)
|
||||
}
|
||||
if nullfloat.Valid {
|
||||
t.Errorf("expected NULL, got %v", nullfloat)
|
||||
}
|
||||
if nullstr.Valid {
|
||||
t.Errorf("expected NULL, got %v", nullstr)
|
||||
}
|
||||
if nullbit.Valid {
|
||||
t.Errorf("expected NULL, got %v", nullbit)
|
||||
}
|
||||
})
|
||||
|
||||
// Not yet supported
|
||||
//t.Run("test null values returned into pointers", func(t *testing.T) {
|
||||
// var nullint *int64
|
||||
// var nullfloat *float64
|
||||
// var nullstr *string
|
||||
// var nullbit *bool
|
||||
// _, err = db.ExecContext(ctx, sqltextrun,
|
||||
// sql.Named("nullint", sql.Out{Dest: &nullint}),
|
||||
// sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
|
||||
// sql.Named("nullstr", sql.Out{Dest: &nullstr}),
|
||||
// sql.Named("nullbit", sql.Out{Dest: &nullbit}),
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
|
||||
// if nullint != nil {
|
||||
// t.Errorf("expected NULL, got %v", nullint)
|
||||
// }
|
||||
// if nullfloat != nil {
|
||||
// t.Errorf("expected NULL, got %v", nullfloat)
|
||||
// }
|
||||
// if nullstr != nil {
|
||||
// t.Errorf("expected NULL, got %v", nullstr)
|
||||
// }
|
||||
// if nullbit != nil {
|
||||
// t.Errorf("expected NULL, got %v", nullbit)
|
||||
// }
|
||||
//})
|
||||
|
||||
t.Run("test non null values into nullable", func(t *testing.T) {
|
||||
nullint := sql.NullInt64{10, true}
|
||||
nullfloat := sql.NullFloat64{1.5, true}
|
||||
nullstr := sql.NullString{"hello", true}
|
||||
nullbit := sql.NullBool{true, true}
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("nullint", sql.Out{Dest: &nullint}),
|
||||
sql.Named("nullfloat", sql.Out{Dest: &nullfloat}),
|
||||
sql.Named("nullstr", sql.Out{Dest: &nullstr}),
|
||||
sql.Named("nullbit", sql.Out{Dest: &nullbit}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !nullint.Valid {
|
||||
t.Error("expected non null value, but got null")
|
||||
}
|
||||
if nullint.Int64 != 10 {
|
||||
t.Errorf("expected 10, got %d", nullint.Int64)
|
||||
}
|
||||
if !nullfloat.Valid {
|
||||
t.Error("expected non null value, but got null")
|
||||
}
|
||||
if nullfloat.Float64 != 1.5 {
|
||||
t.Errorf("expected 1.5, got %v", nullfloat.Float64)
|
||||
}
|
||||
if !nullstr.Valid {
|
||||
t.Error("expected non null value, but got null")
|
||||
}
|
||||
if nullstr.String != "hello" {
|
||||
t.Errorf("expected hello, got %s", nullstr.String)
|
||||
}
|
||||
})
|
||||
t.Run("test return into byte[]", func(t *testing.T) {
|
||||
cstr := []byte{1, 2, 3}
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("varbin", sql.Out{Dest: &cstr}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := []byte{1, 2, 3}
|
||||
if bytes.Compare(cstr, expected) != 0 {
|
||||
t.Errorf("expected [1,2,3], got %v", cstr)
|
||||
}
|
||||
})
|
||||
t.Run("test int into string", func(t *testing.T) {
|
||||
var str string
|
||||
_, err = db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: &str}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if str != "1" {
|
||||
t.Errorf("expected '1', got %v", str)
|
||||
}
|
||||
})
|
||||
t.Run("typeless null for output parameter should return error", func(t *testing.T) {
|
||||
var val interface{}
|
||||
_, actual := db.ExecContext(ctx, sqltextrun,
|
||||
sql.Named("bid", sql.Out{Dest: &val}),
|
||||
)
|
||||
if actual == nil {
|
||||
t.Error("Expected to fail but didn't")
|
||||
}
|
||||
pattern := ".*MSSQL does not allow NULL value without type for OUTPUT parameters.*"
|
||||
match, err := regexp.MatchString(pattern, actual.Error())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("Error '%v', does not match pattern '%v'.", actual, pattern)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestOutputParamWithRows tests reading output parameter before and after
|
||||
// retrieving rows from the result set of a stored procedure. SQL Server sends output
|
||||
// parameters after all the rows are returned. Therefore, if the output parameter
|
||||
// is read before all the rows are retrieved, the value will be incorrect.
|
||||
//
|
||||
// Issue https://github.com/denisenkom/go-mssqldb/issues/378
|
||||
func TestOutputParamWithRows(t *testing.T) {
|
||||
sqltextcreate := `
|
||||
CREATE PROCEDURE spwithoutputandrows
|
||||
@bitparam BIT OUTPUT
|
||||
AS BEGIN
|
||||
SET @bitparam = 1
|
||||
SELECT 'Row 1'
|
||||
END
|
||||
`
|
||||
sqltextdrop := `DROP PROCEDURE spwithoutputandrows;`
|
||||
sqltextrun := `spwithoutputandrows`
|
||||
|
||||
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()
|
||||
|
||||
db.ExecContext(ctx, sqltextdrop)
|
||||
_, err = db.ExecContext(ctx, sqltextcreate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.ExecContext(ctx, sqltextdrop)
|
||||
|
||||
t.Run("Retrieve output after reading rows", func(t *testing.T) {
|
||||
var bitout int64 = 5
|
||||
rows, err := db.QueryContext(ctx, sqltextrun, sql.Named("bitparam", sql.Out{Dest: &bitout}))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
defer rows.Close()
|
||||
var strrow string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&strrow)
|
||||
}
|
||||
if bitout != 1 {
|
||||
t.Errorf("expected 1, got %d", bitout)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Retrieve output before reading rows", func(t *testing.T) {
|
||||
var bitout int64 = 5
|
||||
rows, err := db.QueryContext(ctx, sqltextrun, sql.Named("bitparam", sql.Out{Dest: &bitout}))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
defer rows.Close()
|
||||
if bitout != 5 {
|
||||
t.Errorf("expected 5, got %d", bitout)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestTLSServerReadClose tests writing to an encrypted database connection.
|
||||
// Currently the database server will close the connection while the server is
|
||||
// reading the TDS packets and before any of the data has been parsed.
|
||||
//
|
||||
// When two queries are sent in reverse order, they PASS, but if we send only
|
||||
// a single ping (SELECT 1;) first, then the long query the query fails.
|
||||
//
|
||||
// The long query text is never parsed. In fact, you can comment out, return
|
||||
// early, or have malformed sql in the long query text. Just the length matters.
|
||||
// The error happens when sending the TDS Batch packet to SQL Server the server
|
||||
// closes the connection..
|
||||
//
|
||||
// It appears the driver sends valid TDS packets. In fact, if prefixed with 4
|
||||
// "SELECT 1;" TDS Batch queries then the long query works, but if zero or one
|
||||
// "SELECT 1;" TDS Batch queries are send prior the long query fails to send.
|
||||
//
|
||||
// Lastly, this only manafests itself with an encrypted connection. This has been
|
||||
// observed with SQL Server Azure, SQL Server 13.0.1742 on Windows, and SQL Server
|
||||
// 14.0.900.75 on Linux. It also fails when using the "dev.boringcrypto" (a C based
|
||||
// TLS crypto). I haven't found any knobs on SQL Server to expose the error message
|
||||
// nor have I found a good way to decrypt the TDS stream. KeyLogWriter in the TLS
|
||||
// config may help with that, but wireshark wasn't decrypting TDS based TLS streams
|
||||
// even when using that.
|
||||
//
|
||||
// Issue https://github.com/denisenkom/go-mssqldb/issues/166
|
||||
func TestTLSServerReadClose(t *testing.T) {
|
||||
query := `
|
||||
with
|
||||
config_cte (config) as (
|
||||
select *
|
||||
from ( values
|
||||
('_partition:{\"Fill\":{\"PatternType\":\"solid\",\"FgColor\":\"99ff99\"}}')
|
||||
, ('_separation:{\"Fill\":{\"PatternType\":\"solid\",\"FgColor\":\"99ffff\"}}')
|
||||
, ('Monthly Earnings:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Weekly Earnings:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Total Earnings:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Average Earnings:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Last Month Earning:#,##0.00 ;(#,##0.00)')
|
||||
, ('Award:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Amount:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Grand Total:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Total:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Price Each:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Hyperwallet:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Credit/Debit:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Earning:#,##0.00 ;(#,##0.00)')
|
||||
, ('Change Earning:#,##0.00 ;(#,##0.00)')
|
||||
, ('CheckAmount:#,##0.00 ;(#,##0.00)')
|
||||
, ('Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Prev Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Team Bonuses:#,##0.00 ;(#,##0.00)')
|
||||
, ('Change:#,##0.00 ;(#,##0.00)')
|
||||
, ('Shipping Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('SubTotal:\$#,##0.00 ;(\$#,##0.00)')
|
||||
, ('Total Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('SubTotal Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Return Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Return SubTotal:#,##0.00 ;(#,##0.00)')
|
||||
, ('Return Total Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Return SubTotal Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Cancel Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Cancel SubTotal:#,##0.00 ;(#,##0.00)')
|
||||
, ('Cancel Total Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Cancel SubTotal Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Replacement Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Replacement SubTotal:#,##0.00 ;(#,##0.00)')
|
||||
, ('Replacement Total Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Replacement SubTotal Diff:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('January Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('February Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('March Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('April Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('June Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('July Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('August Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('September Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('October Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('November Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('December Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('January Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('February Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('March Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('April Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('June Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('July Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('August Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('September Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('October Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('November Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('December Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('January Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('February Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('March Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('April Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('June Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('July Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('August Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('September Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('October Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('November Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('December Adj:#,##0.00 ;(#,##0.00)')
|
||||
, ('2016- 2015 YTD Dif:#,##0.00 ;(#,##0.00)')
|
||||
, ('2017- 2016 YTD Dif:#,##0.00 ;(#,##0.00)')
|
||||
, ('2018- 2017 YTD Dif:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec to Jan Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan to Feb Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb to Mar Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar to Apr Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr to May Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('May to Jun Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun to Jul Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul to Aug Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug to Sep Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep to Oct Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct to Nov Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov to Dec Dif Residual:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec to Jan Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan to Feb Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb to Mar Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar to Apr Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr to May Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('May to Jun Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun to Jul Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul to Aug Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug to Sep Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep to Oct Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct to Nov Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov to Dec Dif Bonus:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec to Jan Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan to Feb Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb to Mar Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar to Apr Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr to May Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('May to Jun Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun to Jul Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul to Aug Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug to Sep Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep to Oct Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct to Nov Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov to Dec Dif Total:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Feb Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Mar Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Apr Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('May Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jun Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jul Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Aug Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Sep Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Oct Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Nov Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Dec Refund Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jan Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Feb Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Mar Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Apr Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('May Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jun Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jul Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Aug Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Sep Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Oct Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Nov Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Dec Purchase Cnt:#,##0 ;(#,##0)')
|
||||
, ('Jan Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec Refund Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jan Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Feb Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Mar Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Apr Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('May Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jun Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Jul Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Aug Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Sep Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Oct Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Nov Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
, ('Dec Purchase Amt:#,##0.00 ;(#,##0.00)')
|
||||
) X(a))
|
||||
select * from config_cte
|
||||
`
|
||||
t.Logf("query len (utf16 bytes)=%d, len/4096=%f\n", len(query)*2, float64(len(query)*2)/4096)
|
||||
|
||||
db := open(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
type run struct {
|
||||
name string
|
||||
pings []int
|
||||
pass bool
|
||||
|
||||
conn *sql.Conn
|
||||
}
|
||||
|
||||
// Use separate Conns from the connection pool to ensure separation.
|
||||
runs := []*run{
|
||||
{name: "rev", pings: []int{4, 1}, pass: true},
|
||||
{name: "forward", pings: []int{1}, pass: true},
|
||||
}
|
||||
for _, r := range runs {
|
||||
var err error
|
||||
r.conn, err = db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer r.conn.Close()
|
||||
}
|
||||
|
||||
for _, r := range runs {
|
||||
for _, ping := range r.pings {
|
||||
t.Run(fmt.Sprintf("%s-ping-%d", r.name, ping), func(t *testing.T) {
|
||||
for i := 0; i < ping; i++ {
|
||||
if err := r.conn.PingContext(ctx); err != nil {
|
||||
if r.pass {
|
||||
t.Error("failed to ping server", err)
|
||||
} else {
|
||||
t.Log("failed to ping server", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := r.conn.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
if r.pass {
|
||||
t.Errorf("QueryContext: %+v", err)
|
||||
} else {
|
||||
t.Logf("QueryContext: %+v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
for rows.Next() {
|
||||
// Nothing.
|
||||
}
|
||||
rows.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateTimeParam19(t *testing.T) {
|
||||
conn := open(t)
|
||||
defer conn.Close()
|
||||
|
||||
// testing DateTime1, only supported on go 1.9
|
||||
var emptydate time.Time
|
||||
mindate1 := time.Date(1753, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
maxdate1 := time.Date(9999, 12, 31, 23, 59, 59, 997000000, time.UTC)
|
||||
testdates1 := []DateTime1{
|
||||
DateTime1(mindate1),
|
||||
DateTime1(maxdate1),
|
||||
DateTime1(time.Date(1752, 12, 31, 23, 59, 59, 997000000, time.UTC)), // just a little below minimum date
|
||||
DateTime1(time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC)), // just a little over maximum date
|
||||
DateTime1(emptydate),
|
||||
}
|
||||
|
||||
for _, test := range testdates1 {
|
||||
t.Run(fmt.Sprintf("Test datetime for %v", test), func(t *testing.T) {
|
||||
var res time.Time
|
||||
expected := time.Time(test)
|
||||
queryParamRoundTrip(conn, test, &res)
|
||||
// clip value
|
||||
if expected.Before(mindate1) {
|
||||
expected = mindate1
|
||||
}
|
||||
if expected.After(maxdate1) {
|
||||
expected = maxdate1
|
||||
}
|
||||
if expected.Sub(res) != 0 {
|
||||
t.Errorf("expected: '%s', got: '%s' delta: %d", expected, res, expected.Sub(res))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReturnStatus(t *testing.T) {
|
||||
conn := open(t)
|
||||
defer conn.Close()
|
||||
|
||||
_, err := conn.Exec("if object_id('retstatus') is not null drop proc retstatus;")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = conn.Exec("create proc retstatus as return 2;")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var rs ReturnStatus
|
||||
_, err = conn.Exec("retstatus", &rs)
|
||||
conn.Exec("drop proc retstatus;")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if rs != 2 {
|
||||
t.Errorf("expected status=2, got %d", rs)
|
||||
}
|
||||
}
|
2186
vendor/github.com/denisenkom/go-mssqldb/queries_test.go
generated
vendored
Normal file
2186
vendor/github.com/denisenkom/go-mssqldb/queries_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
522
vendor/github.com/denisenkom/go-mssqldb/tds_test.go
generated
vendored
Normal file
522
vendor/github.com/denisenkom/go-mssqldb/tds_test.go
generated
vendored
Normal file
|
@ -0,0 +1,522 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MockTransport struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (t *MockTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSendLogin(t *testing.T) {
|
||||
memBuf := new(MockTransport)
|
||||
buf := newTdsBuffer(1024, memBuf)
|
||||
login := login{
|
||||
TDSVersion: verTDS73,
|
||||
PacketSize: 0x1000,
|
||||
ClientProgVer: 0x01060100,
|
||||
ClientPID: 100,
|
||||
ClientTimeZone: -4 * 60,
|
||||
ClientID: [6]byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab},
|
||||
OptionFlags1: 0xe0,
|
||||
OptionFlags3: 8,
|
||||
HostName: "subdev1",
|
||||
UserName: "test",
|
||||
Password: "testpwd",
|
||||
AppName: "appname",
|
||||
ServerName: "servername",
|
||||
CtlIntName: "library",
|
||||
Language: "en",
|
||||
Database: "database",
|
||||
ClientLCID: 0x204,
|
||||
AtchDBFile: "filepath",
|
||||
}
|
||||
err := sendLogin(buf, login)
|
||||
if err != nil {
|
||||
t.Error("sendLogin should succeed")
|
||||
}
|
||||
ref := []byte{
|
||||
16, 1, 0, 222, 0, 0, 1, 0, 198 + 16, 0, 0, 0, 3, 0, 10, 115, 0, 16, 0, 0, 0, 1,
|
||||
6, 1, 100, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 8, 16, 255, 255, 255, 4, 2, 0,
|
||||
0, 94, 0, 7, 0, 108, 0, 4, 0, 116, 0, 7, 0, 130, 0, 7, 0, 144, 0, 10, 0, 0,
|
||||
0, 0, 0, 164, 0, 7, 0, 178, 0, 2, 0, 182, 0, 8, 0, 18, 52, 86, 120, 144, 171,
|
||||
198, 0, 0, 0, 198, 0, 8, 0, 214, 0, 0, 0, 0, 0, 0, 0, 115, 0, 117, 0, 98,
|
||||
0, 100, 0, 101, 0, 118, 0, 49, 0, 116, 0, 101, 0, 115, 0, 116, 0, 226, 165,
|
||||
243, 165, 146, 165, 226, 165, 162, 165, 210, 165, 227, 165, 97, 0, 112,
|
||||
0, 112, 0, 110, 0, 97, 0, 109, 0, 101, 0, 115, 0, 101, 0, 114, 0, 118, 0,
|
||||
101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 108, 0, 105, 0, 98, 0, 114,
|
||||
0, 97, 0, 114, 0, 121, 0, 101, 0, 110, 0, 100, 0, 97, 0, 116, 0, 97, 0, 98,
|
||||
0, 97, 0, 115, 0, 101, 0, 102, 0, 105, 0, 108, 0, 101, 0, 112, 0, 97, 0,
|
||||
116, 0, 104, 0}
|
||||
out := memBuf.Bytes()
|
||||
if !bytes.Equal(ref, out) {
|
||||
fmt.Println("Expected:")
|
||||
fmt.Print(hex.Dump(ref))
|
||||
fmt.Println("Returned:")
|
||||
fmt.Print(hex.Dump(out))
|
||||
t.Error("input output don't match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendSqlBatch(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
p, err := parseConnectParams(makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Error("parseConnectParams failed:", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := connect(context.Background(), nil, optionalLogger{testLogger{t}}, p)
|
||||
if err != nil {
|
||||
t.Error("Open connection failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.buf.transport.Close()
|
||||
|
||||
headers := []headerStruct{
|
||||
{hdrtype: dataStmHdrTransDescr,
|
||||
data: transDescrHdr{0, 1}.pack()},
|
||||
}
|
||||
err = sendSqlBatch72(conn.buf, "select 1", headers, true)
|
||||
if err != nil {
|
||||
t.Error("Sending sql batch failed", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ch := make(chan tokenStruct, 5)
|
||||
go processResponse(context.Background(), conn, ch, nil)
|
||||
|
||||
var lastRow []interface{}
|
||||
loop:
|
||||
for tok := range ch {
|
||||
switch token := tok.(type) {
|
||||
case doneStruct:
|
||||
break loop
|
||||
case []columnStruct:
|
||||
conn.columns = token
|
||||
case []interface{}:
|
||||
lastRow = token
|
||||
default:
|
||||
fmt.Println("unknown token", tok)
|
||||
}
|
||||
}
|
||||
|
||||
if len(lastRow) == 0 {
|
||||
t.Fatal("expected row but no row set")
|
||||
}
|
||||
|
||||
switch value := lastRow[0].(type) {
|
||||
case int32:
|
||||
if value != 1 {
|
||||
t.Error("Invalid value returned, should be 1", value)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkConnStr(t *testing.T) {
|
||||
if len(os.Getenv("SQLSERVER_DSN")) > 0 {
|
||||
return
|
||||
}
|
||||
if len(os.Getenv("HOST")) > 0 && len(os.Getenv("DATABASE")) > 0 {
|
||||
return
|
||||
}
|
||||
t.Skip("no database connection string")
|
||||
}
|
||||
|
||||
// makeConnStr returns a URL struct so it may be modified by various
|
||||
// tests before used as a DSN.
|
||||
func makeConnStr(t *testing.T) *url.URL {
|
||||
dsn := os.Getenv("SQLSERVER_DSN")
|
||||
if len(dsn) > 0 {
|
||||
parsed, err := url.Parse(dsn)
|
||||
if err != nil {
|
||||
t.Fatal("unable to parse SQLSERVER_DSN as URL", err)
|
||||
}
|
||||
values := parsed.Query()
|
||||
if values.Get("log") == "" {
|
||||
values.Set("log", "127")
|
||||
}
|
||||
parsed.RawQuery = values.Encode()
|
||||
return parsed
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Set("log", "127")
|
||||
values.Set("database", os.Getenv("DATABASE"))
|
||||
return &url.URL{
|
||||
Scheme: "sqlserver",
|
||||
Host: os.Getenv("HOST"),
|
||||
Path: os.Getenv("INSTANCE"),
|
||||
User: url.UserPassword(os.Getenv("SQLUSER"), os.Getenv("SQLPASSWORD")),
|
||||
RawQuery: values.Encode(),
|
||||
}
|
||||
}
|
||||
|
||||
type testLogger struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (l testLogger) Printf(format string, v ...interface{}) {
|
||||
l.t.Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l testLogger) Println(v ...interface{}) {
|
||||
l.t.Log(v...)
|
||||
}
|
||||
|
||||
func open(t *testing.T) *sql.DB {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
conn, err := sql.Open("mssql", makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Error("Open connection failed:", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func TestConnect(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
conn, err := sql.Open("mssql", makeConnStr(t).String())
|
||||
if err != nil {
|
||||
t.Error("Open connection failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
}
|
||||
|
||||
func simpleQuery(conn *sql.DB, t *testing.T) (stmt *sql.Stmt) {
|
||||
stmt, err := conn.Prepare("select 1 as a")
|
||||
if err != nil {
|
||||
t.Error("Prepare failed:", err.Error())
|
||||
return nil
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
func checkSimpleQuery(rows *sql.Rows, t *testing.T) {
|
||||
numrows := 0
|
||||
for rows.Next() {
|
||||
var val int
|
||||
err := rows.Scan(&val)
|
||||
if err != nil {
|
||||
t.Error("Scan failed:", err.Error())
|
||||
}
|
||||
if val != 1 {
|
||||
t.Error("query should return 1")
|
||||
}
|
||||
numrows++
|
||||
}
|
||||
if numrows != 1 {
|
||||
t.Error("query should return 1 row, returned", numrows)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
conn := open(t)
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
stmt := simpleQuery(conn, t)
|
||||
if stmt == nil {
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
if err != nil {
|
||||
t.Error("Query failed:", err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
t.Error("getting columns failed", err.Error())
|
||||
}
|
||||
if len(columns) != 1 && columns[0] != "a" {
|
||||
t.Error("returned incorrect columns (expected ['a']):", columns)
|
||||
}
|
||||
|
||||
checkSimpleQuery(rows, t)
|
||||
}
|
||||
|
||||
func TestMultipleQueriesSequentialy(t *testing.T) {
|
||||
|
||||
conn := open(t)
|
||||
defer conn.Close()
|
||||
|
||||
stmt, err := conn.Prepare("select 1 as a")
|
||||
if err != nil {
|
||||
t.Error("Prepare failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
if err != nil {
|
||||
t.Error("Query failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
checkSimpleQuery(rows, t)
|
||||
|
||||
rows, err = stmt.Query()
|
||||
if err != nil {
|
||||
t.Error("Query failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
checkSimpleQuery(rows, t)
|
||||
}
|
||||
|
||||
func TestMultipleQueryClose(t *testing.T) {
|
||||
conn := open(t)
|
||||
defer conn.Close()
|
||||
|
||||
stmt, err := conn.Prepare("select 1 as a")
|
||||
if err != nil {
|
||||
t.Error("Prepare failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query()
|
||||
if err != nil {
|
||||
t.Error("Query failed:", err.Error())
|
||||
return
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
rows, err = stmt.Query()
|
||||
if err != nil {
|
||||
t.Error("Query failed:", err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
checkSimpleQuery(rows, t)
|
||||
}
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
conn := open(t)
|
||||
defer conn.Close()
|
||||
conn.Ping()
|
||||
}
|
||||
|
||||
func TestSecureWithInvalidHostName(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
|
||||
dsn := makeConnStr(t)
|
||||
dsnParams := dsn.Query()
|
||||
dsnParams.Set("encrypt", "true")
|
||||
dsnParams.Set("TrustServerCertificate", "false")
|
||||
dsnParams.Set("hostNameInCertificate", "foo.bar")
|
||||
dsn.RawQuery = dsnParams.Encode()
|
||||
|
||||
conn, err := sql.Open("mssql", dsn.String())
|
||||
if err != nil {
|
||||
t.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.Ping()
|
||||
if err == nil {
|
||||
t.Fatal("Connected to fake foo.bar server")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureConnection(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
|
||||
dsn := makeConnStr(t)
|
||||
dsnParams := dsn.Query()
|
||||
dsnParams.Set("encrypt", "true")
|
||||
dsnParams.Set("TrustServerCertificate", "true")
|
||||
dsn.RawQuery = dsnParams.Encode()
|
||||
|
||||
conn, err := sql.Open("mssql", dsn.String())
|
||||
if err != nil {
|
||||
t.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
var msg string
|
||||
err = conn.QueryRow("select 'secret'").Scan(&msg)
|
||||
if err != nil {
|
||||
t.Fatal("cannot scan value", err)
|
||||
}
|
||||
if msg != "secret" {
|
||||
t.Fatal("expected secret, got: ", msg)
|
||||
}
|
||||
var secure bool
|
||||
err = conn.QueryRow("select encrypt_option from sys.dm_exec_connections where session_id=@@SPID").Scan(&secure)
|
||||
if err != nil {
|
||||
t.Fatal("cannot scan value", err)
|
||||
}
|
||||
if !secure {
|
||||
t.Fatal("connection is not encrypted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidConnectionString(t *testing.T) {
|
||||
connStrings := []string{
|
||||
"log=invalid",
|
||||
"port=invalid",
|
||||
"packet size=invalid",
|
||||
"connection timeout=invalid",
|
||||
"dial timeout=invalid",
|
||||
"keepalive=invalid",
|
||||
"encrypt=invalid",
|
||||
"trustservercertificate=invalid",
|
||||
"failoverport=invalid",
|
||||
|
||||
// ODBC mode
|
||||
"odbc:password={",
|
||||
"odbc:password={somepass",
|
||||
"odbc:password={somepass}}",
|
||||
"odbc:password={some}pass",
|
||||
}
|
||||
for _, connStr := range connStrings {
|
||||
_, err := parseConnectParams(connStr)
|
||||
if err == nil {
|
||||
t.Errorf("Connection expected to fail for connection string %s but it didn't", connStr)
|
||||
continue
|
||||
} else {
|
||||
t.Logf("Connection failed for %s as expected with error %v", connStr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidConnectionString(t *testing.T) {
|
||||
type testStruct struct {
|
||||
connStr string
|
||||
check func(connectParams) bool
|
||||
}
|
||||
connStrings := []testStruct{
|
||||
{"server=server\\instance;database=testdb;user id=tester;password=pwd", func(p connectParams) bool {
|
||||
return p.host == "server" && p.instance == "instance" && p.user == "tester" && p.password == "pwd"
|
||||
}},
|
||||
{"server=.", func(p connectParams) bool { return p.host == "localhost" }},
|
||||
{"server=(local)", func(p connectParams) bool { return p.host == "localhost" }},
|
||||
{"ServerSPN=serverspn;Workstation ID=workstid", func(p connectParams) bool { return p.serverSPN == "serverspn" && p.workstation == "workstid" }},
|
||||
{"failoverpartner=fopartner;failoverport=2000", func(p connectParams) bool { return p.failOverPartner == "fopartner" && p.failOverPort == 2000 }},
|
||||
{"app name=appname;applicationintent=ReadOnly", func(p connectParams) bool { return p.appname == "appname" && (p.typeFlags&fReadOnlyIntent != 0) }},
|
||||
{"encrypt=disable", func(p connectParams) bool { return p.disableEncryption }},
|
||||
{"encrypt=true", func(p connectParams) bool { return p.encrypt && !p.disableEncryption }},
|
||||
{"encrypt=false", func(p connectParams) bool { return !p.encrypt && !p.disableEncryption }},
|
||||
{"trustservercertificate=true", func(p connectParams) bool { return p.trustServerCertificate }},
|
||||
{"trustservercertificate=false", func(p connectParams) bool { return !p.trustServerCertificate }},
|
||||
{"certificate=abc", func(p connectParams) bool { return p.certificate == "abc" }},
|
||||
{"hostnameincertificate=abc", func(p connectParams) bool { return p.hostInCertificate == "abc" }},
|
||||
{"connection timeout=3;dial timeout=4;keepalive=5", func(p connectParams) bool {
|
||||
return p.conn_timeout == 3*time.Second && p.dial_timeout == 4*time.Second && p.keepAlive == 5*time.Second
|
||||
}},
|
||||
{"log=63", func(p connectParams) bool { return p.logFlags == 63 && p.port == 1433 }},
|
||||
{"log=63;port=1000", func(p connectParams) bool { return p.logFlags == 63 && p.port == 1000 }},
|
||||
{"log=64", func(p connectParams) bool { return p.logFlags == 64 && p.packetSize == 4096 }},
|
||||
{"log=64;packet size=0", func(p connectParams) bool { return p.logFlags == 64 && p.packetSize == 512 }},
|
||||
{"log=64;packet size=300", func(p connectParams) bool { return p.logFlags == 64 && p.packetSize == 512 }},
|
||||
{"log=64;packet size=8192", func(p connectParams) bool { return p.logFlags == 64 && p.packetSize == 8192 }},
|
||||
{"log=64;packet size=48000", func(p connectParams) bool { return p.logFlags == 64 && p.packetSize == 32767 }},
|
||||
|
||||
// those are supported currently, but maybe should not be
|
||||
{"someparam", func(p connectParams) bool { return true }},
|
||||
{";;=;", func(p connectParams) bool { return true }},
|
||||
|
||||
// ODBC mode
|
||||
{"odbc:server=somehost;user id=someuser;password=somepass", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "somepass"
|
||||
}},
|
||||
{"odbc:server=somehost;user id=someuser;password=some{pass", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some{pass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={somepass}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "somepass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={some=pass}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some=pass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={some;pass}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some;pass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={some{pass}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some{pass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={some}}pass}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some}pass"
|
||||
}},
|
||||
{"odbc:server={somehost};user id={someuser};password={some{}}p=a;ss}", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some{}p=a;ss"
|
||||
}},
|
||||
{"odbc: server = somehost; user id = someuser ; password = {some pass } ", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.user == "someuser" && p.password == "some pass "
|
||||
}},
|
||||
|
||||
// URL mode
|
||||
{"sqlserver://somehost?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1433 && p.instance == "" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
{"sqlserver://someuser@somehost?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1433 && p.instance == "" && p.user == "someuser" && p.password == "" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
{"sqlserver://someuser:@somehost?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1433 && p.instance == "" && p.user == "someuser" && p.password == "" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
{"sqlserver://someuser:foo%3A%2F%5C%21~%40;bar@somehost?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1433 && p.instance == "" && p.user == "someuser" && p.password == "foo:/\\!~@;bar" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
{"sqlserver://someuser:foo%3A%2F%5C%21~%40;bar@somehost:1434?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1434 && p.instance == "" && p.user == "someuser" && p.password == "foo:/\\!~@;bar" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
{"sqlserver://someuser:foo%3A%2F%5C%21~%40;bar@somehost:1434/someinstance?connection+timeout=30", func(p connectParams) bool {
|
||||
return p.host == "somehost" && p.port == 1434 && p.instance == "someinstance" && p.user == "someuser" && p.password == "foo:/\\!~@;bar" && p.conn_timeout == 30*time.Second
|
||||
}},
|
||||
}
|
||||
for _, ts := range connStrings {
|
||||
p, err := parseConnectParams(ts.connStr)
|
||||
if err == nil {
|
||||
t.Logf("Connection string was parsed successfully %s", ts.connStr)
|
||||
} else {
|
||||
t.Errorf("Connection string %s failed to parse with error %s", ts.connStr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !ts.check(p) {
|
||||
t.Errorf("Check failed on conn str %s", ts.connStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadConnect(t *testing.T) {
|
||||
checkConnStr(t)
|
||||
SetLogger(testLogger{t})
|
||||
connURL := makeConnStr(t)
|
||||
connURL.User = url.UserPassword("baduser", "badpwd")
|
||||
badDSN := connURL.String()
|
||||
|
||||
conn, err := sql.Open("mssql", badDSN)
|
||||
if err != nil {
|
||||
t.Error("Open connection failed:", err.Error())
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Ping()
|
||||
if err == nil {
|
||||
t.Error("Ping should fail for connection: ", badDSN)
|
||||
}
|
||||
}
|
114
vendor/github.com/denisenkom/go-mssqldb/tvp_example_test.go
generated
vendored
Normal file
114
vendor/github.com/denisenkom/go-mssqldb/tvp_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
mssql "github.com/denisenkom/go-mssqldb"
|
||||
)
|
||||
|
||||
// This example shows how to use tvp type
|
||||
func ExampleTVP() {
|
||||
const (
|
||||
createTable = "CREATE TABLE Location (Name VARCHAR(50), CostRate INT, Availability BIT, ModifiedDate DATETIME2)"
|
||||
|
||||
dropTable = "IF OBJECT_ID('Location', 'U') IS NOT NULL DROP TABLE Location"
|
||||
|
||||
createTVP = `CREATE TYPE LocationTableType AS TABLE
|
||||
(LocationName VARCHAR(50),
|
||||
CostRate INT)`
|
||||
|
||||
dropTVP = "IF type_id('LocationTableType') IS NOT NULL DROP TYPE LocationTableType"
|
||||
|
||||
createProc = `CREATE PROCEDURE dbo.usp_InsertProductionLocation
|
||||
@TVP LocationTableType READONLY
|
||||
AS
|
||||
SET NOCOUNT ON
|
||||
INSERT INTO Location
|
||||
(
|
||||
Name,
|
||||
CostRate,
|
||||
Availability,
|
||||
ModifiedDate)
|
||||
SELECT *, 0,GETDATE()
|
||||
FROM @TVP`
|
||||
|
||||
dropProc = "IF OBJECT_ID('dbo.usp_InsertProductionLocation', 'P') IS NOT NULL DROP PROCEDURE dbo.usp_InsertProductionLocation"
|
||||
|
||||
execTvp = "exec dbo.usp_InsertProductionLocation @TVP;"
|
||||
)
|
||||
type LocationTableTvp struct {
|
||||
LocationName string
|
||||
LocationCountry string `tvp:"-"`
|
||||
CostRate int64
|
||||
Currency string `json:"-"`
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" password:%s\n", *password)
|
||||
fmt.Printf(" port:%d\n", *port)
|
||||
fmt.Printf(" server:%s\n", *server)
|
||||
fmt.Printf(" user:%s\n", *user)
|
||||
}
|
||||
|
||||
connString := makeConnURL().String()
|
||||
if *debug {
|
||||
fmt.Printf(" connString:%s\n", connString)
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlserver", connString)
|
||||
if err != nil {
|
||||
log.Fatal("Open connection failed:", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(createTable)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = db.Exec(createTVP)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Exec(dropTVP)
|
||||
_, err = db.Exec(createProc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Exec(dropProc)
|
||||
|
||||
locationTableTypeData := []LocationTableTvp{
|
||||
{
|
||||
LocationName: "Alberta",
|
||||
LocationCountry: "Canada",
|
||||
CostRate: 0,
|
||||
Currency: "CAD",
|
||||
},
|
||||
{
|
||||
LocationName: "British Columbia",
|
||||
LocationCountry: "Canada",
|
||||
CostRate: 1,
|
||||
Currency: "CAD",
|
||||
},
|
||||
}
|
||||
|
||||
tvpType := mssql.TVP{
|
||||
TypeName: "LocationTableType",
|
||||
Value: locationTableTypeData,
|
||||
}
|
||||
|
||||
_, err = db.Exec(execTvp, sql.Named("TVP", tvpType))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
for _, locationData := range locationTableTypeData {
|
||||
fmt.Printf("Data for location %s, %s has been inserted.\n", locationData.LocationName, locationData.LocationCountry)
|
||||
}
|
||||
}
|
||||
}
|
150
vendor/github.com/denisenkom/go-mssqldb/tvp_go19.go
generated
vendored
150
vendor/github.com/denisenkom/go-mssqldb/tvp_go19.go
generated
vendored
|
@ -8,56 +8,70 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorEmptyTVPName = errors.New("TVPTypeName must not be empty")
|
||||
ErrorTVPTypeSlice = errors.New("TVPType must be slice type")
|
||||
ErrorTVPTypeSliceIsEmpty = errors.New("TVPType mustn't be null value")
|
||||
const (
|
||||
jsonTag = "json"
|
||||
tvpTag = "tvp"
|
||||
skipTagValue = "-"
|
||||
sqlSeparator = "."
|
||||
)
|
||||
|
||||
//TVPType is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server
|
||||
type TVPType struct {
|
||||
//TVP param name, mustn't be default value
|
||||
TVPTypeName string
|
||||
//TVP scheme name
|
||||
TVPScheme string
|
||||
//TVP Value. Param must be the slice, mustn't be nil
|
||||
TVPValue interface{}
|
||||
var (
|
||||
ErrorEmptyTVPTypeName = errors.New("TypeName must not be empty")
|
||||
ErrorTypeSlice = errors.New("TVP must be slice type")
|
||||
ErrorTypeSliceIsEmpty = errors.New("TVP mustn't be null value")
|
||||
ErrorSkip = errors.New("all fields mustn't skip")
|
||||
ErrorObjectName = errors.New("wrong tvp name")
|
||||
ErrorWrongTyping = errors.New("the number of elements in columnStr and tvpFieldIndexes do not align")
|
||||
)
|
||||
|
||||
//TVP is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server
|
||||
type TVP struct {
|
||||
//TypeName mustn't be default value
|
||||
TypeName string
|
||||
//Value must be the slice, mustn't be nil
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (tvp TVPType) check() error {
|
||||
if len(tvp.TVPTypeName) == 0 {
|
||||
return ErrorEmptyTVPName
|
||||
func (tvp TVP) check() error {
|
||||
if len(tvp.TypeName) == 0 {
|
||||
return ErrorEmptyTVPTypeName
|
||||
}
|
||||
valueOf := reflect.ValueOf(tvp.TVPValue)
|
||||
if !isProc(tvp.TypeName) {
|
||||
return ErrorEmptyTVPTypeName
|
||||
}
|
||||
if sepCount := getCountSQLSeparators(tvp.TypeName); sepCount > 1 {
|
||||
return ErrorObjectName
|
||||
}
|
||||
valueOf := reflect.ValueOf(tvp.Value)
|
||||
if valueOf.Kind() != reflect.Slice {
|
||||
return ErrorTVPTypeSlice
|
||||
return ErrorTypeSlice
|
||||
}
|
||||
if valueOf.IsNil() {
|
||||
return ErrorTVPTypeSliceIsEmpty
|
||||
return ErrorTypeSliceIsEmpty
|
||||
}
|
||||
if reflect.TypeOf(tvp.TVPValue).Elem().Kind() != reflect.Struct {
|
||||
return ErrorTVPTypeSlice
|
||||
if reflect.TypeOf(tvp.Value).Elem().Kind() != reflect.Struct {
|
||||
return ErrorTypeSlice
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tvp TVPType) encode() ([]byte, error) {
|
||||
columnStr, err := tvp.columnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (tvp TVP) encode(schema, name string, columnStr []columnStruct, tvpFieldIndexes []int) ([]byte, error) {
|
||||
if len(columnStr) != len(tvpFieldIndexes) {
|
||||
return nil, ErrorWrongTyping
|
||||
}
|
||||
preparedBuffer := make([]byte, 0, 20+(10*len(columnStr)))
|
||||
buf := bytes.NewBuffer(preparedBuffer)
|
||||
err = writeBVarChar(buf, "")
|
||||
err := writeBVarChar(buf, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeBVarChar(buf, tvp.TVPScheme)
|
||||
writeBVarChar(buf, tvp.TVPTypeName)
|
||||
|
||||
writeBVarChar(buf, schema)
|
||||
writeBVarChar(buf, name)
|
||||
binary.Write(buf, binary.LittleEndian, uint16(len(columnStr)))
|
||||
|
||||
for i, column := range columnStr {
|
||||
|
@ -66,7 +80,9 @@ func (tvp TVPType) encode() ([]byte, error) {
|
|||
writeTypeInfo(buf, &columnStr[i].ti)
|
||||
writeBVarChar(buf, "")
|
||||
}
|
||||
// The returned error is always nil
|
||||
buf.WriteByte(_TVP_END_TOKEN)
|
||||
|
||||
conn := new(Conn)
|
||||
conn.sess = new(tdsSession)
|
||||
conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73}
|
||||
|
@ -74,18 +90,18 @@ func (tvp TVPType) encode() ([]byte, error) {
|
|||
c: conn,
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(tvp.TVPValue)
|
||||
val := reflect.ValueOf(tvp.Value)
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
refStr := reflect.ValueOf(val.Index(i).Interface())
|
||||
buf.WriteByte(_TVP_ROW_TOKEN)
|
||||
for j := 0; j < refStr.NumField(); j++ {
|
||||
field := refStr.Field(j)
|
||||
for columnStrIdx, fieldIdx := range tvpFieldIndexes {
|
||||
field := refStr.Field(fieldIdx)
|
||||
tvpVal := field.Interface()
|
||||
valOf := reflect.ValueOf(tvpVal)
|
||||
elemKind := field.Kind()
|
||||
if elemKind == reflect.Ptr && valOf.IsNil() {
|
||||
switch tvpVal.(type) {
|
||||
case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64:
|
||||
case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64, *int:
|
||||
binary.Write(buf, binary.LittleEndian, uint8(0))
|
||||
continue
|
||||
default:
|
||||
|
@ -106,34 +122,44 @@ func (tvp TVPType) encode() ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err)
|
||||
}
|
||||
columnStr[j].ti.Writer(buf, param.ti, param.buffer)
|
||||
columnStr[columnStrIdx].ti.Writer(buf, param.ti, param.buffer)
|
||||
}
|
||||
}
|
||||
buf.WriteByte(_TVP_END_TOKEN)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (tvp TVPType) columnTypes() ([]columnStruct, error) {
|
||||
val := reflect.ValueOf(tvp.TVPValue)
|
||||
func (tvp TVP) columnTypes() ([]columnStruct, []int, error) {
|
||||
val := reflect.ValueOf(tvp.Value)
|
||||
var firstRow interface{}
|
||||
if val.Len() != 0 {
|
||||
firstRow = val.Index(0).Interface()
|
||||
} else {
|
||||
firstRow = reflect.New(reflect.TypeOf(tvp.TVPValue).Elem()).Elem().Interface()
|
||||
firstRow = reflect.New(reflect.TypeOf(tvp.Value).Elem()).Elem().Interface()
|
||||
}
|
||||
|
||||
tvpRow := reflect.TypeOf(firstRow)
|
||||
columnCount := tvpRow.NumField()
|
||||
defaultValues := make([]interface{}, 0, columnCount)
|
||||
|
||||
tvpFieldIndexes := make([]int, 0, columnCount)
|
||||
for i := 0; i < columnCount; i++ {
|
||||
typeField := tvpRow.Field(i).Type
|
||||
if typeField.Kind() == reflect.Ptr {
|
||||
v := reflect.New(typeField.Elem())
|
||||
field := tvpRow.Field(i)
|
||||
tvpTagValue, isTvpTag := field.Tag.Lookup(tvpTag)
|
||||
jsonTagValue, isJsonTag := field.Tag.Lookup(jsonTag)
|
||||
if IsSkipField(tvpTagValue, isTvpTag, jsonTagValue, isJsonTag) {
|
||||
continue
|
||||
}
|
||||
tvpFieldIndexes = append(tvpFieldIndexes, i)
|
||||
if field.Type.Kind() == reflect.Ptr {
|
||||
v := reflect.New(field.Type.Elem())
|
||||
defaultValues = append(defaultValues, v.Interface())
|
||||
continue
|
||||
}
|
||||
defaultValues = append(defaultValues, reflect.Zero(typeField).Interface())
|
||||
defaultValues = append(defaultValues, reflect.Zero(field.Type).Interface())
|
||||
}
|
||||
|
||||
if columnCount-len(tvpFieldIndexes) == columnCount {
|
||||
return nil, nil, ErrorSkip
|
||||
}
|
||||
|
||||
conn := new(Conn)
|
||||
|
@ -147,11 +173,11 @@ func (tvp TVPType) columnTypes() ([]columnStruct, error) {
|
|||
for index, val := range defaultValues {
|
||||
cval, err := convertInputParameter(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err)
|
||||
return nil, nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err)
|
||||
}
|
||||
param, err := stmt.makeParam(cval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
column := columnStruct{
|
||||
ti: param.ti,
|
||||
|
@ -163,5 +189,43 @@ func (tvp TVPType) columnTypes() ([]columnStruct, error) {
|
|||
columnConfiguration = append(columnConfiguration, column)
|
||||
}
|
||||
|
||||
return columnConfiguration, nil
|
||||
return columnConfiguration, tvpFieldIndexes, nil
|
||||
}
|
||||
|
||||
func IsSkipField(tvpTagValue string, isTvpValue bool, jsonTagValue string, isJsonTagValue bool) bool {
|
||||
if !isTvpValue && !isJsonTagValue {
|
||||
return false
|
||||
} else if isTvpValue && tvpTagValue != skipTagValue {
|
||||
return false
|
||||
} else if !isTvpValue && isJsonTagValue && jsonTagValue != skipTagValue {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getSchemeAndName(tvpName string) (string, string, error) {
|
||||
if len(tvpName) == 0 {
|
||||
return "", "", ErrorEmptyTVPTypeName
|
||||
}
|
||||
splitVal := strings.Split(tvpName, ".")
|
||||
if len(splitVal) > 2 {
|
||||
return "", "", errors.New("wrong tvp name")
|
||||
}
|
||||
if len(splitVal) == 2 {
|
||||
res := make([]string, 2)
|
||||
for key, value := range splitVal {
|
||||
tmp := strings.Replace(value, "[", "", -1)
|
||||
tmp = strings.Replace(tmp, "]", "", -1)
|
||||
res[key] = tmp
|
||||
}
|
||||
return res[0], res[1], nil
|
||||
}
|
||||
tmp := strings.Replace(splitVal[0], "[", "", -1)
|
||||
tmp = strings.Replace(tmp, "]", "", -1)
|
||||
|
||||
return "", tmp, nil
|
||||
}
|
||||
|
||||
func getCountSQLSeparators(str string) int {
|
||||
return strings.Count(str, sqlSeparator)
|
||||
}
|
||||
|
|
751
vendor/github.com/denisenkom/go-mssqldb/tvp_go19_db_test.go
generated
vendored
Normal file
751
vendor/github.com/denisenkom/go-mssqldb/tvp_go19_db_test.go
generated
vendored
Normal file
|
@ -0,0 +1,751 @@
|
|||
// +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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
578
vendor/github.com/denisenkom/go-mssqldb/tvp_go19_test.go
generated
vendored
Normal file
578
vendor/github.com/denisenkom/go-mssqldb/tvp_go19_test.go
generated
vendored
Normal file
|
@ -0,0 +1,578 @@
|
|||
// +build go1.9
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TestFields struct {
|
||||
PBinary []byte `tvp:"p_binary"`
|
||||
PVarchar string `json:"p_varchar"`
|
||||
PNvarchar *string `json:"p_nvarchar"`
|
||||
TimeValue time.Time `echo:"-"`
|
||||
TimeNullValue *time.Time
|
||||
}
|
||||
|
||||
type TestFieldError struct {
|
||||
ErrorValue []*byte
|
||||
}
|
||||
|
||||
type TestFieldsUnsupportedTypes struct {
|
||||
ErrorType TestFieldError
|
||||
}
|
||||
|
||||
func TestTVPType_columnTypes(t *testing.T) {
|
||||
type customTypeAllFieldsSkipOne struct {
|
||||
SkipTest int `tvp:"-"`
|
||||
}
|
||||
type customTypeAllFieldsSkipMoreOne struct {
|
||||
SkipTest int `tvp:"-"`
|
||||
SkipTest1 int `json:"-"`
|
||||
}
|
||||
type skipWrongField struct {
|
||||
SkipTest int
|
||||
SkipTest1 []*byte `json:"skip_test" tvp:"-"`
|
||||
}
|
||||
type structType struct {
|
||||
SkipTest int `json:"-" tvp:"test"`
|
||||
SkipTest1 []*skipWrongField `json:"any" tvp:"tvp"`
|
||||
}
|
||||
type skipWithAnotherTagValue struct {
|
||||
SkipTest int `json:"-" tvp:"test"`
|
||||
}
|
||||
|
||||
type fields struct {
|
||||
TVPName string
|
||||
TVPValue interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []columnStruct
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test Pass",
|
||||
fields: fields{
|
||||
TVPValue: []TestFields{TestFields{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Value has wrong field type",
|
||||
fields: fields{
|
||||
TVPValue: []TestFieldError{TestFieldError{}},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value has wrong type",
|
||||
fields: fields{
|
||||
TVPValue: []TestFieldsUnsupportedTypes{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value has wrong type",
|
||||
fields: fields{
|
||||
TVPValue: []structType{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "CustomTag all fields are skip, single field",
|
||||
fields: fields{
|
||||
TVPValue: []customTypeAllFieldsSkipOne{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "CustomTag all fields are skip, > 1 field",
|
||||
fields: fields{
|
||||
TVPValue: []customTypeAllFieldsSkipMoreOne{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "CustomTag all fields are skip wrong field type",
|
||||
fields: fields{
|
||||
TVPValue: []skipWrongField{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "CustomTag tag value is not -",
|
||||
fields: fields{
|
||||
TVPValue: []skipWithAnotherTagValue{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tvp := TVP{
|
||||
TypeName: tt.fields.TVPName,
|
||||
Value: tt.fields.TVPValue,
|
||||
}
|
||||
_, _, err := tvp.columnTypes()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TVP.columnTypes() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTVPType_check(t *testing.T) {
|
||||
type fields struct {
|
||||
TVPName string
|
||||
TVPValue interface{}
|
||||
}
|
||||
|
||||
var nullSlice []*string
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "TypeName is nil",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value is nil",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value is nil",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value isn't slice",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value isn't slice",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: 12345,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value isn't slice",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: nullSlice,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value isn't right",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: []*fields{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Value is right",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Value is right",
|
||||
fields: fields{
|
||||
TVPName: "Test",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Value is right",
|
||||
fields: fields{
|
||||
TVPName: "[Test]",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Value is right",
|
||||
fields: fields{
|
||||
TVPName: "[123].[Test]",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "TVP name is right",
|
||||
fields: fields{
|
||||
TVPName: "[123].Test",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "TVP name is right",
|
||||
fields: fields{
|
||||
TVPName: "123.[Test]",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "TVP name is wrong",
|
||||
fields: fields{
|
||||
TVPName: "123.[Test\n]",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "TVP name is wrong",
|
||||
fields: fields{
|
||||
TVPName: "123.[Test].456",
|
||||
TVPValue: []fields{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tvp := TVP{
|
||||
TypeName: tt.fields.TVPName,
|
||||
Value: tt.fields.TVPValue,
|
||||
}
|
||||
if err := tvp.check(); (err != nil) != tt.wantErr {
|
||||
t.Errorf("TVP.check() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTVPType_check(b *testing.B) {
|
||||
type val struct {
|
||||
Value string
|
||||
}
|
||||
tvp := TVP{
|
||||
TypeName: "Test",
|
||||
Value: []val{},
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := tvp.check()
|
||||
if err != nil {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkColumnTypes(b *testing.B) {
|
||||
type str struct {
|
||||
bytes byte
|
||||
bytesNull *byte
|
||||
bytesSlice []byte
|
||||
|
||||
int8s int8
|
||||
int8sNull *int8
|
||||
uint8s uint8
|
||||
uint8sNull *uint8
|
||||
|
||||
int16s int16
|
||||
int16sNull *int16
|
||||
uint16s uint16
|
||||
uint16sNull *uint16
|
||||
|
||||
int32s int32
|
||||
int32sNull *int32
|
||||
uint32s uint32
|
||||
uint32sNull *uint32
|
||||
|
||||
int64s int64
|
||||
int64sNull *int64
|
||||
uint64s uint64
|
||||
uint64sNull *uint64
|
||||
|
||||
stringVal string
|
||||
stringValNull *string
|
||||
|
||||
bools bool
|
||||
boolsNull *bool
|
||||
}
|
||||
wal := make([]str, 100)
|
||||
tvp := TVP{
|
||||
TypeName: "Test",
|
||||
Value: wal,
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, err := tvp.columnTypes()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSkipField(t *testing.T) {
|
||||
type args struct {
|
||||
tvpTagValue string
|
||||
isTvpValue bool
|
||||
jsonTagValue string
|
||||
isJsonTagValue bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Empty tags",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "tvp is skip",
|
||||
want: true,
|
||||
args: args{
|
||||
isTvpValue: true,
|
||||
tvpTagValue: skipTagValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tvp is any",
|
||||
want: false,
|
||||
args: args{
|
||||
isTvpValue: true,
|
||||
tvpTagValue: "tvp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is skip",
|
||||
want: true,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: skipTagValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is any",
|
||||
want: false,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: "any",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is skip tvp is skip",
|
||||
want: true,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: skipTagValue,
|
||||
isTvpValue: true,
|
||||
tvpTagValue: skipTagValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is skip tvp is any",
|
||||
want: false,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: skipTagValue,
|
||||
isTvpValue: true,
|
||||
tvpTagValue: "tvp",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is any tvp is skip",
|
||||
want: true,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: "json",
|
||||
isTvpValue: true,
|
||||
tvpTagValue: skipTagValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Json is any tvp is skip",
|
||||
want: false,
|
||||
args: args{
|
||||
isJsonTagValue: true,
|
||||
jsonTagValue: "json",
|
||||
isTvpValue: true,
|
||||
tvpTagValue: "tvp",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsSkipField(tt.args.tvpTagValue, tt.args.isTvpValue, tt.args.jsonTagValue, tt.args.isJsonTagValue); got != tt.want {
|
||||
t.Errorf("IsSkipField() = %v, schema %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getSchemeAndName(t *testing.T) {
|
||||
type args struct {
|
||||
tvpName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
schema string
|
||||
tvpName string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Empty object name",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Wrong object name",
|
||||
wantErr: true,
|
||||
args: args{
|
||||
tvpName: "1.2.3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Schema+name",
|
||||
wantErr: false,
|
||||
args: args{
|
||||
tvpName: "obj.tvp",
|
||||
},
|
||||
schema: "obj",
|
||||
tvpName: "tvp",
|
||||
},
|
||||
{
|
||||
name: "Schema+name",
|
||||
wantErr: false,
|
||||
args: args{
|
||||
tvpName: "[obj].[tvp]",
|
||||
},
|
||||
schema: "obj",
|
||||
tvpName: "tvp",
|
||||
},
|
||||
{
|
||||
name: "only name",
|
||||
wantErr: false,
|
||||
args: args{
|
||||
tvpName: "tvp",
|
||||
},
|
||||
schema: "",
|
||||
tvpName: "tvp",
|
||||
},
|
||||
{
|
||||
name: "only name",
|
||||
wantErr: false,
|
||||
args: args{
|
||||
tvpName: "[tvp]",
|
||||
},
|
||||
schema: "",
|
||||
tvpName: "tvp",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
schema, name, err := getSchemeAndName(tt.args.tvpName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getSchemeAndName() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if schema != tt.schema {
|
||||
t.Errorf("getSchemeAndName() schema = %v, schema %v", schema, tt.schema)
|
||||
}
|
||||
if name != tt.tvpName {
|
||||
t.Errorf("getSchemeAndName() name = %v, schema %v", name, tt.tvpName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTVP_encode(t *testing.T) {
|
||||
type fields struct {
|
||||
TypeName string
|
||||
Value interface{}
|
||||
}
|
||||
type args struct {
|
||||
schema string
|
||||
name string
|
||||
columnStr []columnStruct
|
||||
tvpFieldIndexes []int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want []byte
|
||||
wantErr bool
|
||||
wantPanic bool
|
||||
}{
|
||||
{
|
||||
name: "column and indexes are nil",
|
||||
wantErr: true,
|
||||
args: args{
|
||||
tvpFieldIndexes: []int{1, 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "column and indexes are nil",
|
||||
wantErr: true,
|
||||
args: args{
|
||||
tvpFieldIndexes: []int{1, 2},
|
||||
columnStr: []columnStruct{columnStruct{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "column and indexes are nil",
|
||||
wantErr: true,
|
||||
args: args{
|
||||
columnStr: []columnStruct{columnStruct{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "column and indexes are nil",
|
||||
wantErr: true,
|
||||
wantPanic: true,
|
||||
args: args{
|
||||
schema: string(make([]byte, 256)),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantPanic {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("Want panic")
|
||||
}
|
||||
}()
|
||||
}
|
||||
tvp := TVP{
|
||||
TypeName: tt.fields.TypeName,
|
||||
Value: tt.fields.Value,
|
||||
}
|
||||
got, err := tvp.encode(tt.args.schema, tt.args.name, tt.args.columnStr, tt.args.tvpFieldIndexes)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TVP.encode() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("TVP.encode() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
4
vendor/github.com/denisenkom/go-mssqldb/types.go
generated
vendored
4
vendor/github.com/denisenkom/go-mssqldb/types.go
generated
vendored
|
@ -73,14 +73,10 @@ const (
|
|||
const _PLP_NULL = 0xFFFFFFFFFFFFFFFF
|
||||
const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE
|
||||
const _PLP_TERMINATOR = 0x00000000
|
||||
const _TVP_NULL_TOKEN = 0xffff
|
||||
|
||||
// TVP COLUMN FLAGS
|
||||
const _TVP_COLUMN_DEFAULT_FLAG = 0x200
|
||||
const _TVP_END_TOKEN = 0x00
|
||||
const _TVP_ROW_TOKEN = 0x01
|
||||
const _TVP_ORDER_UNIQUE_TOKEN = 0x10
|
||||
const _TVP_COLUMN_ORDERING_TOKEN = 0x11
|
||||
|
||||
// TYPE_INFO rule
|
||||
// http://msdn.microsoft.com/en-us/library/dd358284.aspx
|
||||
|
|
123
vendor/github.com/denisenkom/go-mssqldb/types_test.go
generated
vendored
Normal file
123
vendor/github.com/denisenkom/go-mssqldb/types_test.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMakeGoLangScanType(t *testing.T) {
|
||||
if (reflect.TypeOf(int64(0)) != makeGoLangScanType(typeInfo{TypeId: typeInt8})) {
|
||||
t.Errorf("invalid type returned for typeDateTime")
|
||||
}
|
||||
if (reflect.TypeOf(float64(0)) != makeGoLangScanType(typeInfo{TypeId: typeFlt4})) {
|
||||
t.Errorf("invalid type returned for typeDateTime")
|
||||
}
|
||||
if (reflect.TypeOf(float64(0)) != makeGoLangScanType(typeInfo{TypeId: typeFlt8})) {
|
||||
t.Errorf("invalid type returned for typeDateTime")
|
||||
}
|
||||
if (reflect.TypeOf("") != makeGoLangScanType(typeInfo{TypeId: typeVarChar})) {
|
||||
t.Errorf("invalid type returned for typeDateTime")
|
||||
}
|
||||
if (reflect.TypeOf(time.Time{}) != makeGoLangScanType(typeInfo{TypeId: typeDateTime})) {
|
||||
t.Errorf("invalid type returned for typeDateTime")
|
||||
}
|
||||
if (reflect.TypeOf(time.Time{}) != makeGoLangScanType(typeInfo{TypeId: typeDateTim4})) {
|
||||
t.Errorf("invalid type returned for typeDateTim4")
|
||||
}
|
||||
if (reflect.TypeOf(int64(0)) != makeGoLangScanType(typeInfo{TypeId: typeInt1})) {
|
||||
t.Errorf("invalid type returned for typeInt1")
|
||||
}
|
||||
if (reflect.TypeOf(int64(0)) != makeGoLangScanType(typeInfo{TypeId: typeInt2})) {
|
||||
t.Errorf("invalid type returned for typeInt2")
|
||||
}
|
||||
if (reflect.TypeOf(int64(0)) != makeGoLangScanType(typeInfo{TypeId: typeInt4})) {
|
||||
t.Errorf("invalid type returned for typeInt4")
|
||||
}
|
||||
if (reflect.TypeOf(int64(0)) != makeGoLangScanType(typeInfo{TypeId: typeIntN, Size: 4})) {
|
||||
t.Errorf("invalid type returned for typeIntN")
|
||||
}
|
||||
if (reflect.TypeOf([]byte{}) != makeGoLangScanType(typeInfo{TypeId: typeMoney, Size: 8})) {
|
||||
t.Errorf("invalid type returned for typeIntN")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeGoLangTypeName(t *testing.T) {
|
||||
defer handlePanic(t)
|
||||
|
||||
tests := []struct {
|
||||
typeName string
|
||||
typeString string
|
||||
typeID uint8
|
||||
}{
|
||||
{"typeDateTime", "DATETIME", typeDateTime},
|
||||
{"typeDateTim4", "SMALLDATETIME", typeDateTim4},
|
||||
{"typeBigBinary", "BINARY", typeBigBinary},
|
||||
//TODO: Add other supported types
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if makeGoLangTypeName(typeInfo{TypeId: tt.typeID}) != tt.typeString {
|
||||
t.Errorf("invalid type name returned for %s", tt.typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeGoLangTypeLength(t *testing.T) {
|
||||
defer handlePanic(t)
|
||||
|
||||
tests := []struct {
|
||||
typeName string
|
||||
typeVarLen bool
|
||||
typeLen int64
|
||||
typeID uint8
|
||||
}{
|
||||
{"typeDateTime", false, 0, typeDateTime},
|
||||
{"typeDateTim4", false, 0, typeDateTim4},
|
||||
{"typeBigBinary", false, 0, typeBigBinary},
|
||||
//TODO: Add other supported types
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
n, v := makeGoLangTypeLength(typeInfo{TypeId: tt.typeID})
|
||||
if v != tt.typeVarLen {
|
||||
t.Errorf("invalid type length variability returned for %s", tt.typeName)
|
||||
}
|
||||
if n != tt.typeLen {
|
||||
t.Errorf("invalid type length returned for %s", tt.typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeGoLangTypePrecisionScale(t *testing.T) {
|
||||
defer handlePanic(t)
|
||||
|
||||
tests := []struct {
|
||||
typeName string
|
||||
typeID uint8
|
||||
typeVarLen bool
|
||||
typePrec int64
|
||||
typeScale int64
|
||||
}{
|
||||
{"typeDateTime", typeDateTime, false, 0, 0},
|
||||
{"typeDateTim4", typeDateTim4, false, 0, 0},
|
||||
{"typeBigBinary", typeBigBinary, false, 0, 0},
|
||||
//TODO: Add other supported types
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
prec, scale, varLen := makeGoLangTypePrecisionScale(typeInfo{TypeId: tt.typeID})
|
||||
if varLen != tt.typeVarLen {
|
||||
t.Errorf("invalid type length variability returned for %s", tt.typeName)
|
||||
}
|
||||
if prec != tt.typePrec || scale != tt.typeScale {
|
||||
t.Errorf("invalid type precision and/or scale returned for %s", tt.typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePanic(t *testing.T) {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("recovered panic")
|
||||
}
|
||||
}
|
70
vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier_test.go
generated
vendored
Normal file
70
vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier_test.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
package mssql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUniqueIdentifier(t *testing.T) {
|
||||
dbUUID := UniqueIdentifier{0x67, 0x45, 0x23, 0x01,
|
||||
0xAB, 0x89,
|
||||
0xEF, 0xCD,
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
|
||||
}
|
||||
|
||||
uuid := UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}
|
||||
|
||||
t.Run("Scan", func(t *testing.T) {
|
||||
t.Run("[]byte", func(t *testing.T) {
|
||||
var sut UniqueIdentifier
|
||||
if err := sut.Scan(dbUUID[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sut != uuid {
|
||||
t.Errorf("bytes not swapped correctly: got %q; want %q", sut, uuid)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("string", func(t *testing.T) {
|
||||
var sut UniqueIdentifier
|
||||
if err := sut.Scan(uuid.String()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sut != uuid {
|
||||
t.Errorf("string not scanned correctly: got %q; want %q", sut, uuid)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Value", func(t *testing.T) {
|
||||
sut := uuid
|
||||
v, err := sut.Value()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b, ok := v.([]byte)
|
||||
if !ok {
|
||||
t.Fatalf("(%T) is not []byte", v)
|
||||
}
|
||||
|
||||
if !bytes.Equal(b, dbUUID[:]) {
|
||||
t.Errorf("got %q; want %q", b, dbUUID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUniqueIdentifierString(t *testing.T) {
|
||||
sut := UniqueIdentifier{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}
|
||||
expected := "01234567-89AB-CDEF-0123-456789ABCDEF"
|
||||
if actual := sut.String(); actual != expected {
|
||||
t.Errorf("sut.String() = %s; want %s", sut, expected)
|
||||
}
|
||||
}
|
||||
|
||||
var _ fmt.Stringer = UniqueIdentifier{}
|
||||
var _ sql.Scanner = &UniqueIdentifier{}
|
||||
var _ driver.Valuer = UniqueIdentifier{}
|
Loading…
Add table
Add a link
Reference in a new issue