update mssql drive to last working version 20180314172330-6a30f4e59a44 (#7306)
parent
aeb8f7aad8
commit
1e46eedce7
@ -0,0 +1,45 @@ |
|||||||
|
version: 1.0.{build} |
||||||
|
|
||||||
|
os: Windows Server 2012 R2 |
||||||
|
|
||||||
|
clone_folder: c:\gopath\src\github.com\denisenkom\go-mssqldb |
||||||
|
|
||||||
|
environment: |
||||||
|
GOPATH: c:\gopath |
||||||
|
HOST: localhost |
||||||
|
SQLUSER: sa |
||||||
|
SQLPASSWORD: Password12! |
||||||
|
DATABASE: test |
||||||
|
GOVERSION: 110 |
||||||
|
matrix: |
||||||
|
- GOVERSION: 18 |
||||||
|
SQLINSTANCE: SQL2016 |
||||||
|
- GOVERSION: 110 |
||||||
|
SQLINSTANCE: SQL2016 |
||||||
|
- SQLINSTANCE: SQL2014 |
||||||
|
- SQLINSTANCE: SQL2012SP1 |
||||||
|
- SQLINSTANCE: SQL2008R2SP2 |
||||||
|
|
||||||
|
install: |
||||||
|
- set GOROOT=c:\go%GOVERSION% |
||||||
|
- set PATH=%GOPATH%\bin;%GOROOT%\bin;%PATH% |
||||||
|
- go version |
||||||
|
- go env |
||||||
|
|
||||||
|
build_script: |
||||||
|
- go build |
||||||
|
|
||||||
|
before_test: |
||||||
|
# setup SQL Server |
||||||
|
- ps: | |
||||||
|
$instanceName = $env:SQLINSTANCE |
||||||
|
Start-Service "MSSQL`$$instanceName" |
||||||
|
Start-Service "SQLBrowser" |
||||||
|
- sqlcmd -S "(local)\%SQLINSTANCE%" -Q "Use [master]; CREATE DATABASE test;" |
||||||
|
- sqlcmd -S "(local)\%SQLINSTANCE%" -h -1 -Q "set nocount on; Select @@version" |
||||||
|
- pip install codecov |
||||||
|
|
||||||
|
|
||||||
|
test_script: |
||||||
|
- go test -race -coverprofile=coverage.txt -covermode=atomic |
||||||
|
- codecov -f coverage.txt |
@ -0,0 +1,616 @@ |
|||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"context" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"math" |
||||||
|
"reflect" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type Bulk struct { |
||||||
|
cn *Conn |
||||||
|
metadata []columnStruct |
||||||
|
bulkColumns []columnStruct |
||||||
|
columnsName []string |
||||||
|
tablename string |
||||||
|
numRows int |
||||||
|
|
||||||
|
headerSent bool |
||||||
|
Options BulkOptions |
||||||
|
Debug bool |
||||||
|
} |
||||||
|
type BulkOptions struct { |
||||||
|
CheckConstraints bool |
||||||
|
FireTriggers bool |
||||||
|
KeepNulls bool |
||||||
|
KilobytesPerBatch int |
||||||
|
RowsPerBatch int |
||||||
|
Order []string |
||||||
|
Tablock bool |
||||||
|
} |
||||||
|
|
||||||
|
type DataValue interface{} |
||||||
|
|
||||||
|
func (cn *Conn) CreateBulk(table string, columns []string) (_ *Bulk) { |
||||||
|
b := Bulk{cn: cn, tablename: table, headerSent: false, columnsName: columns} |
||||||
|
b.Debug = false |
||||||
|
return &b |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) sendBulkCommand() (err error) { |
||||||
|
//get table columns info
|
||||||
|
err = b.getMetadata() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
//match the columns
|
||||||
|
for _, colname := range b.columnsName { |
||||||
|
var bulkCol *columnStruct |
||||||
|
|
||||||
|
for _, m := range b.metadata { |
||||||
|
if m.ColName == colname { |
||||||
|
bulkCol = &m |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
if bulkCol != nil { |
||||||
|
|
||||||
|
if bulkCol.ti.TypeId == typeUdt { |
||||||
|
//send udt as binary
|
||||||
|
bulkCol.ti.TypeId = typeBigVarBin |
||||||
|
} |
||||||
|
b.bulkColumns = append(b.bulkColumns, *bulkCol) |
||||||
|
b.dlogf("Adding column %s %s %#x", colname, bulkCol.ColName, bulkCol.ti.TypeId) |
||||||
|
} else { |
||||||
|
return fmt.Errorf("Column %s does not exist in destination table %s", colname, b.tablename) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//create the bulk command
|
||||||
|
|
||||||
|
//columns definitions
|
||||||
|
var col_defs bytes.Buffer |
||||||
|
for i, col := range b.bulkColumns { |
||||||
|
if i != 0 { |
||||||
|
col_defs.WriteString(", ") |
||||||
|
} |
||||||
|
col_defs.WriteString("[" + col.ColName + "] " + makeDecl(col.ti)) |
||||||
|
} |
||||||
|
|
||||||
|
//options
|
||||||
|
var with_opts []string |
||||||
|
|
||||||
|
if b.Options.CheckConstraints { |
||||||
|
with_opts = append(with_opts, "CHECK_CONSTRAINTS") |
||||||
|
} |
||||||
|
if b.Options.FireTriggers { |
||||||
|
with_opts = append(with_opts, "FIRE_TRIGGERS") |
||||||
|
} |
||||||
|
if b.Options.KeepNulls { |
||||||
|
with_opts = append(with_opts, "KEEP_NULLS") |
||||||
|
} |
||||||
|
if b.Options.KilobytesPerBatch > 0 { |
||||||
|
with_opts = append(with_opts, fmt.Sprintf("KILOBYTES_PER_BATCH = %d", b.Options.KilobytesPerBatch)) |
||||||
|
} |
||||||
|
if b.Options.RowsPerBatch > 0 { |
||||||
|
with_opts = append(with_opts, fmt.Sprintf("ROWS_PER_BATCH = %d", b.Options.RowsPerBatch)) |
||||||
|
} |
||||||
|
if len(b.Options.Order) > 0 { |
||||||
|
with_opts = append(with_opts, fmt.Sprintf("ORDER(%s)", strings.Join(b.Options.Order, ","))) |
||||||
|
} |
||||||
|
if b.Options.Tablock { |
||||||
|
with_opts = append(with_opts, "TABLOCK") |
||||||
|
} |
||||||
|
var with_part string |
||||||
|
if len(with_opts) > 0 { |
||||||
|
with_part = fmt.Sprintf("WITH (%s)", strings.Join(with_opts, ",")) |
||||||
|
} |
||||||
|
|
||||||
|
query := fmt.Sprintf("INSERT BULK %s (%s) %s", b.tablename, col_defs.String(), with_part) |
||||||
|
|
||||||
|
stmt, err := b.cn.Prepare(query) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("Prepare failed: %s", err.Error()) |
||||||
|
} |
||||||
|
b.dlogf(query) |
||||||
|
|
||||||
|
_, err = stmt.Exec(nil) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
b.headerSent = true |
||||||
|
|
||||||
|
var buf = b.cn.sess.buf |
||||||
|
buf.BeginPacket(packBulkLoadBCP) |
||||||
|
|
||||||
|
// send the columns metadata
|
||||||
|
columnMetadata := b.createColMetadata() |
||||||
|
_, err = buf.Write(columnMetadata) |
||||||
|
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// AddRow immediately writes the row to the destination table.
|
||||||
|
// The arguments are the row values in the order they were specified.
|
||||||
|
func (b *Bulk) AddRow(row []interface{}) (err error) { |
||||||
|
if !b.headerSent { |
||||||
|
err = b.sendBulkCommand() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if len(row) != len(b.bulkColumns) { |
||||||
|
return fmt.Errorf("Row does not have the same number of columns than the destination table %d %d", |
||||||
|
len(row), len(b.bulkColumns)) |
||||||
|
} |
||||||
|
|
||||||
|
bytes, err := b.makeRowData(row) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
_, err = b.cn.sess.buf.Write(bytes) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
b.numRows = b.numRows + 1 |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) makeRowData(row []interface{}) ([]byte, error) { |
||||||
|
buf := new(bytes.Buffer) |
||||||
|
buf.WriteByte(byte(tokenRow)) |
||||||
|
|
||||||
|
var logcol bytes.Buffer |
||||||
|
for i, col := range b.bulkColumns { |
||||||
|
|
||||||
|
if b.Debug { |
||||||
|
logcol.WriteString(fmt.Sprintf(" col[%d]='%v' ", i, row[i])) |
||||||
|
} |
||||||
|
param, err := b.makeParam(row[i], col) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("bulkcopy: %s", err.Error()) |
||||||
|
} |
||||||
|
|
||||||
|
if col.ti.Writer == nil { |
||||||
|
return nil, fmt.Errorf("no writer for column: %s, TypeId: %#x", |
||||||
|
col.ColName, col.ti.TypeId) |
||||||
|
} |
||||||
|
err = col.ti.Writer(buf, param.ti, param.buffer) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("bulkcopy: %s", err.Error()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
b.dlogf("row[%d] %s\n", b.numRows, logcol.String()) |
||||||
|
|
||||||
|
return buf.Bytes(), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) Done() (rowcount int64, err error) { |
||||||
|
if b.headerSent == false { |
||||||
|
//no rows had been sent
|
||||||
|
return 0, nil |
||||||
|
} |
||||||
|
var buf = b.cn.sess.buf |
||||||
|
buf.WriteByte(byte(tokenDone)) |
||||||
|
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(doneFinal)) |
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(0)) // curcmd
|
||||||
|
|
||||||
|
if b.cn.sess.loginAck.TDSVersion >= verTDS72 { |
||||||
|
binary.Write(buf, binary.LittleEndian, uint64(0)) //rowcount 0
|
||||||
|
} else { |
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(0)) //rowcount 0
|
||||||
|
} |
||||||
|
|
||||||
|
buf.FinishPacket() |
||||||
|
|
||||||
|
tokchan := make(chan tokenStruct, 5) |
||||||
|
go processResponse(context.Background(), b.cn.sess, tokchan, nil) |
||||||
|
|
||||||
|
var rowCount int64 |
||||||
|
for token := range tokchan { |
||||||
|
switch token := token.(type) { |
||||||
|
case doneStruct: |
||||||
|
if token.Status&doneCount != 0 { |
||||||
|
rowCount = int64(token.RowCount) |
||||||
|
} |
||||||
|
if token.isError() { |
||||||
|
return 0, token.getError() |
||||||
|
} |
||||||
|
case error: |
||||||
|
return 0, b.cn.checkBadConn(token) |
||||||
|
} |
||||||
|
} |
||||||
|
return rowCount, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) createColMetadata() []byte { |
||||||
|
buf := new(bytes.Buffer) |
||||||
|
buf.WriteByte(byte(tokenColMetadata)) // token
|
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(len(b.bulkColumns))) // column count
|
||||||
|
|
||||||
|
for i, col := range b.bulkColumns { |
||||||
|
|
||||||
|
if b.cn.sess.loginAck.TDSVersion >= verTDS72 { |
||||||
|
binary.Write(buf, binary.LittleEndian, uint32(col.UserType)) // usertype, always 0?
|
||||||
|
} else { |
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(col.UserType)) |
||||||
|
} |
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(col.Flags)) |
||||||
|
|
||||||
|
writeTypeInfo(buf, &b.bulkColumns[i].ti) |
||||||
|
|
||||||
|
if col.ti.TypeId == typeNText || |
||||||
|
col.ti.TypeId == typeText || |
||||||
|
col.ti.TypeId == typeImage { |
||||||
|
|
||||||
|
tablename_ucs2 := str2ucs2(b.tablename) |
||||||
|
binary.Write(buf, binary.LittleEndian, uint16(len(tablename_ucs2)/2)) |
||||||
|
buf.Write(tablename_ucs2) |
||||||
|
} |
||||||
|
colname_ucs2 := str2ucs2(col.ColName) |
||||||
|
buf.WriteByte(uint8(len(colname_ucs2) / 2)) |
||||||
|
buf.Write(colname_ucs2) |
||||||
|
} |
||||||
|
|
||||||
|
return buf.Bytes() |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) getMetadata() (err error) { |
||||||
|
stmt, err := b.cn.Prepare("SET FMTONLY ON") |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
_, err = stmt.Exec(nil) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
//get columns info
|
||||||
|
stmt, err = b.cn.Prepare(fmt.Sprintf("select * from %s SET FMTONLY OFF", b.tablename)) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
stmt2 := stmt.(*Stmt) |
||||||
|
cols, err := stmt2.QueryMeta() |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("get columns info failed: %v", err.Error()) |
||||||
|
} |
||||||
|
b.metadata = cols |
||||||
|
|
||||||
|
if b.Debug { |
||||||
|
for _, col := range b.metadata { |
||||||
|
b.dlogf("col: %s typeId: %#x size: %d scale: %d prec: %d flags: %d lcid: %#x\n", |
||||||
|
col.ColName, col.ti.TypeId, col.ti.Size, col.ti.Scale, col.ti.Prec, |
||||||
|
col.Flags, col.ti.Collation.LcidAndFlags) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// QueryMeta is almost the same as mssql.Stmt.Query, but returns all the columns info.
|
||||||
|
func (s *Stmt) QueryMeta() (cols []columnStruct, err error) { |
||||||
|
if err = s.sendQuery(nil); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
tokchan := make(chan tokenStruct, 5) |
||||||
|
go processResponse(context.Background(), s.c.sess, tokchan, s.c.outs) |
||||||
|
s.c.clearOuts() |
||||||
|
loop: |
||||||
|
for tok := range tokchan { |
||||||
|
switch token := tok.(type) { |
||||||
|
case doneStruct: |
||||||
|
break loop |
||||||
|
case []columnStruct: |
||||||
|
cols = token |
||||||
|
break loop |
||||||
|
case error: |
||||||
|
return nil, s.c.checkBadConn(token) |
||||||
|
} |
||||||
|
} |
||||||
|
return cols, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) makeParam(val DataValue, col columnStruct) (res Param, err error) { |
||||||
|
res.ti.Size = col.ti.Size |
||||||
|
res.ti.TypeId = col.ti.TypeId |
||||||
|
|
||||||
|
if val == nil { |
||||||
|
res.ti.Size = 0 |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
switch col.ti.TypeId { |
||||||
|
|
||||||
|
case typeInt1, typeInt2, typeInt4, typeInt8, typeIntN: |
||||||
|
var intvalue int64 |
||||||
|
|
||||||
|
switch val := val.(type) { |
||||||
|
case int: |
||||||
|
intvalue = int64(val) |
||||||
|
case int32: |
||||||
|
intvalue = int64(val) |
||||||
|
case int64: |
||||||
|
intvalue = val |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for int column") |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
res.buffer = make([]byte, res.ti.Size) |
||||||
|
if col.ti.Size == 1 { |
||||||
|
res.buffer[0] = byte(intvalue) |
||||||
|
} else if col.ti.Size == 2 { |
||||||
|
binary.LittleEndian.PutUint16(res.buffer, uint16(intvalue)) |
||||||
|
} else if col.ti.Size == 4 { |
||||||
|
binary.LittleEndian.PutUint32(res.buffer, uint32(intvalue)) |
||||||
|
} else if col.ti.Size == 8 { |
||||||
|
binary.LittleEndian.PutUint64(res.buffer, uint64(intvalue)) |
||||||
|
} |
||||||
|
case typeFlt4, typeFlt8, typeFltN: |
||||||
|
var floatvalue float64 |
||||||
|
|
||||||
|
switch val := val.(type) { |
||||||
|
case float32: |
||||||
|
floatvalue = float64(val) |
||||||
|
case float64: |
||||||
|
floatvalue = val |
||||||
|
case int: |
||||||
|
floatvalue = float64(val) |
||||||
|
case int64: |
||||||
|
floatvalue = float64(val) |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for float column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if col.ti.Size == 4 { |
||||||
|
res.buffer = make([]byte, 4) |
||||||
|
binary.LittleEndian.PutUint32(res.buffer, math.Float32bits(float32(floatvalue))) |
||||||
|
} else if col.ti.Size == 8 { |
||||||
|
res.buffer = make([]byte, 8) |
||||||
|
binary.LittleEndian.PutUint64(res.buffer, math.Float64bits(floatvalue)) |
||||||
|
} |
||||||
|
case typeNVarChar, typeNText, typeNChar: |
||||||
|
|
||||||
|
switch val := val.(type) { |
||||||
|
case string: |
||||||
|
res.buffer = str2ucs2(val) |
||||||
|
case []byte: |
||||||
|
res.buffer = val |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for nvarchar column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
res.ti.Size = len(res.buffer) |
||||||
|
|
||||||
|
case typeVarChar, typeBigVarChar, typeText, typeChar, typeBigChar: |
||||||
|
switch val := val.(type) { |
||||||
|
case string: |
||||||
|
res.buffer = []byte(val) |
||||||
|
case []byte: |
||||||
|
res.buffer = val |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for varchar column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
res.ti.Size = len(res.buffer) |
||||||
|
|
||||||
|
case typeBit, typeBitN: |
||||||
|
if reflect.TypeOf(val).Kind() != reflect.Bool { |
||||||
|
err = fmt.Errorf("mssql: invalid type for bit column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
res.ti.TypeId = typeBitN |
||||||
|
res.ti.Size = 1 |
||||||
|
res.buffer = make([]byte, 1) |
||||||
|
if val.(bool) { |
||||||
|
res.buffer[0] = 1 |
||||||
|
} |
||||||
|
|
||||||
|
case typeDateTime2N, typeDateTimeOffsetN: |
||||||
|
switch val := val.(type) { |
||||||
|
case time.Time: |
||||||
|
days, ns := dateTime2(val) |
||||||
|
ns /= int64(math.Pow10(int(col.ti.Scale)*-1) * 1000000000) |
||||||
|
|
||||||
|
var data = make([]byte, 5) |
||||||
|
|
||||||
|
data[0] = byte(ns) |
||||||
|
data[1] = byte(ns >> 8) |
||||||
|
data[2] = byte(ns >> 16) |
||||||
|
data[3] = byte(ns >> 24) |
||||||
|
data[4] = byte(ns >> 32) |
||||||
|
|
||||||
|
if col.ti.Scale <= 2 { |
||||||
|
res.ti.Size = 6 |
||||||
|
} else if col.ti.Scale <= 4 { |
||||||
|
res.ti.Size = 7 |
||||||
|
} else { |
||||||
|
res.ti.Size = 8 |
||||||
|
} |
||||||
|
var buf []byte |
||||||
|
buf = make([]byte, res.ti.Size) |
||||||
|
copy(buf, data[0:res.ti.Size-3]) |
||||||
|
|
||||||
|
buf[res.ti.Size-3] = byte(days) |
||||||
|
buf[res.ti.Size-2] = byte(days >> 8) |
||||||
|
buf[res.ti.Size-1] = byte(days >> 16) |
||||||
|
|
||||||
|
if col.ti.TypeId == typeDateTimeOffsetN { |
||||||
|
_, offset := val.Zone() |
||||||
|
var offsetMinute = uint16(offset / 60) |
||||||
|
buf = append(buf, byte(offsetMinute)) |
||||||
|
buf = append(buf, byte(offsetMinute>>8)) |
||||||
|
res.ti.Size = res.ti.Size + 2 |
||||||
|
} |
||||||
|
|
||||||
|
res.buffer = buf |
||||||
|
|
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for datetime2 column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
case typeDateN: |
||||||
|
switch val := val.(type) { |
||||||
|
case time.Time: |
||||||
|
days, _ := dateTime2(val) |
||||||
|
|
||||||
|
res.ti.Size = 3 |
||||||
|
res.buffer = make([]byte, 3) |
||||||
|
res.buffer[0] = byte(days) |
||||||
|
res.buffer[1] = byte(days >> 8) |
||||||
|
res.buffer[2] = byte(days >> 16) |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for date column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
case typeDateTime, typeDateTimeN, typeDateTim4: |
||||||
|
switch val := val.(type) { |
||||||
|
case time.Time: |
||||||
|
if col.ti.Size == 4 { |
||||||
|
res.ti.Size = 4 |
||||||
|
res.buffer = make([]byte, 4) |
||||||
|
|
||||||
|
ref := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) |
||||||
|
dur := val.Sub(ref) |
||||||
|
days := dur / (24 * time.Hour) |
||||||
|
if days < 0 { |
||||||
|
err = fmt.Errorf("mssql: Date %s is out of range", val) |
||||||
|
return |
||||||
|
} |
||||||
|
mins := val.Hour()*60 + val.Minute() |
||||||
|
|
||||||
|
binary.LittleEndian.PutUint16(res.buffer[0:2], uint16(days)) |
||||||
|
binary.LittleEndian.PutUint16(res.buffer[2:4], uint16(mins)) |
||||||
|
} else if col.ti.Size == 8 { |
||||||
|
res.ti.Size = 8 |
||||||
|
res.buffer = make([]byte, 8) |
||||||
|
|
||||||
|
days := divFloor(val.Unix(), 24*60*60) |
||||||
|
//25567 - number of days since Jan 1 1900 UTC to Jan 1 1970
|
||||||
|
days = days + 25567 |
||||||
|
tm := (val.Hour()*60*60+val.Minute()*60+val.Second())*300 + int(val.Nanosecond()/10000000*3) |
||||||
|
|
||||||
|
binary.LittleEndian.PutUint32(res.buffer[0:4], uint32(days)) |
||||||
|
binary.LittleEndian.PutUint32(res.buffer[4:8], uint32(tm)) |
||||||
|
} else { |
||||||
|
err = fmt.Errorf("mssql: invalid size of column") |
||||||
|
} |
||||||
|
|
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for datetime column: %s", val) |
||||||
|
} |
||||||
|
|
||||||
|
// case typeMoney, typeMoney4, typeMoneyN:
|
||||||
|
case typeDecimal, typeDecimalN, typeNumeric, typeNumericN: |
||||||
|
var value float64 |
||||||
|
switch v := val.(type) { |
||||||
|
case int: |
||||||
|
value = float64(v) |
||||||
|
case int8: |
||||||
|
value = float64(v) |
||||||
|
case int16: |
||||||
|
value = float64(v) |
||||||
|
case int32: |
||||||
|
value = float64(v) |
||||||
|
case int64: |
||||||
|
value = float64(v) |
||||||
|
case float32: |
||||||
|
value = float64(v) |
||||||
|
case float64: |
||||||
|
value = v |
||||||
|
case string: |
||||||
|
if value, err = strconv.ParseFloat(v, 64); err != nil { |
||||||
|
return res, fmt.Errorf("bulk: unable to convert string to float: %v", err) |
||||||
|
} |
||||||
|
default: |
||||||
|
return res, fmt.Errorf("unknown value for decimal: %#v", v) |
||||||
|
} |
||||||
|
|
||||||
|
perc := col.ti.Prec |
||||||
|
scale := col.ti.Scale |
||||||
|
var dec Decimal |
||||||
|
dec, err = Float64ToDecimalScale(value, scale) |
||||||
|
if err != nil { |
||||||
|
return res, err |
||||||
|
} |
||||||
|
dec.prec = perc |
||||||
|
|
||||||
|
var length byte |
||||||
|
switch { |
||||||
|
case perc <= 9: |
||||||
|
length = 4 |
||||||
|
case perc <= 19: |
||||||
|
length = 8 |
||||||
|
case perc <= 28: |
||||||
|
length = 12 |
||||||
|
default: |
||||||
|
length = 16 |
||||||
|
} |
||||||
|
|
||||||
|
buf := make([]byte, length+1) |
||||||
|
// first byte length written by typeInfo.writer
|
||||||
|
res.ti.Size = int(length) + 1 |
||||||
|
// second byte sign
|
||||||
|
if value < 0 { |
||||||
|
buf[0] = 0 |
||||||
|
} else { |
||||||
|
buf[0] = 1 |
||||||
|
} |
||||||
|
|
||||||
|
ub := dec.UnscaledBytes() |
||||||
|
l := len(ub) |
||||||
|
if l > int(length) { |
||||||
|
err = fmt.Errorf("decimal out of range: %s", dec) |
||||||
|
return res, err |
||||||
|
} |
||||||
|
// reverse the bytes
|
||||||
|
for i, j := 1, l-1; j >= 0; i, j = i+1, j-1 { |
||||||
|
buf[i] = ub[j] |
||||||
|
} |
||||||
|
res.buffer = buf |
||||||
|
case typeBigVarBin: |
||||||
|
switch val := val.(type) { |
||||||
|
case []byte: |
||||||
|
res.ti.Size = len(val) |
||||||
|
res.buffer = val |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for Binary column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
case typeGuid: |
||||||
|
switch val := val.(type) { |
||||||
|
case []byte: |
||||||
|
res.ti.Size = len(val) |
||||||
|
res.buffer = val |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: invalid type for Guid column: %s", val) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: type %x not implemented", col.ti.TypeId) |
||||||
|
} |
||||||
|
return |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (b *Bulk) dlogf(format string, v ...interface{}) { |
||||||
|
if b.Debug { |
||||||
|
b.cn.sess.log.Printf(format, v...) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"database/sql/driver" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
) |
||||||
|
|
||||||
|
type copyin struct { |
||||||
|
cn *Conn |
||||||
|
bulkcopy *Bulk |
||||||
|
closed bool |
||||||
|
} |
||||||
|
|
||||||
|
type serializableBulkConfig struct { |
||||||
|
TableName string |
||||||
|
ColumnsName []string |
||||||
|
Options BulkOptions |
||||||
|
} |
||||||
|
|
||||||
|
func (d *Driver) OpenConnection(dsn string) (*Conn, error) { |
||||||
|
return d.open(context.Background(), dsn) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Conn) prepareCopyIn(query string) (_ driver.Stmt, err error) { |
||||||
|
config_json := query[11:] |
||||||
|
|
||||||
|
bulkconfig := serializableBulkConfig{} |
||||||
|
err = json.Unmarshal([]byte(config_json), &bulkconfig) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
bulkcopy := c.CreateBulk(bulkconfig.TableName, bulkconfig.ColumnsName) |
||||||
|
bulkcopy.Options = bulkconfig.Options |
||||||
|
|
||||||
|
ci := ©in{ |
||||||
|
cn: c, |
||||||
|
bulkcopy: bulkcopy, |
||||||
|
} |
||||||
|
|
||||||
|
return ci, nil |
||||||
|
} |
||||||
|
|
||||||
|
func CopyIn(table string, options BulkOptions, columns ...string) string { |
||||||
|
bulkconfig := &serializableBulkConfig{TableName: table, Options: options, ColumnsName: columns} |
||||||
|
|
||||||
|
config_json, err := json.Marshal(bulkconfig) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
|
||||||
|
stmt := "INSERTBULK " + string(config_json) |
||||||
|
|
||||||
|
return stmt |
||||||
|
} |
||||||
|
|
||||||
|
func (ci *copyin) NumInput() int { |
||||||
|
return -1 |
||||||
|
} |
||||||
|
|
||||||
|
func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { |
||||||
|
return nil, errors.New("ErrNotSupported") |
||||||
|
} |
||||||
|
|
||||||
|
func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { |
||||||
|
if ci.closed { |
||||||
|
return nil, errors.New("errCopyInClosed") |
||||||
|
} |
||||||
|
|
||||||
|
if len(v) == 0 { |
||||||
|
rowCount, err := ci.bulkcopy.Done() |
||||||
|
ci.closed = true |
||||||
|
return driver.RowsAffected(rowCount), err |
||||||
|
} |
||||||
|
|
||||||
|
t := make([]interface{}, len(v)) |
||||||
|
for i, val := range v { |
||||||
|
t[i] = val |
||||||
|
} |
||||||
|
|
||||||
|
err = ci.bulkcopy.AddRow(t) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
return driver.RowsAffected(0), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (ci *copyin) Close() (err error) { |
||||||
|
return nil |
||||||
|
} |
@ -1,39 +0,0 @@ |
|||||||
package mssql |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/binary" |
|
||||||
"io" |
|
||||||
) |
|
||||||
|
|
||||||
// http://msdn.microsoft.com/en-us/library/dd340437.aspx
|
|
||||||
|
|
||||||
type collation struct { |
|
||||||
lcidAndFlags uint32 |
|
||||||
sortId uint8 |
|
||||||
} |
|
||||||
|
|
||||||
func (c collation) getLcid() uint32 { |
|
||||||
return c.lcidAndFlags & 0x000fffff |
|
||||||
} |
|
||||||
|
|
||||||
func (c collation) getFlags() uint32 { |
|
||||||
return (c.lcidAndFlags & 0x0ff00000) >> 20 |
|
||||||
} |
|
||||||
|
|
||||||
func (c collation) getVersion() uint32 { |
|
||||||
return (c.lcidAndFlags & 0xf0000000) >> 28 |
|
||||||
} |
|
||||||
|
|
||||||
func readCollation(r *tdsBuffer) (res collation) { |
|
||||||
res.lcidAndFlags = r.uint32() |
|
||||||
res.sortId = r.byte() |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func writeCollation(w io.Writer, col collation) (err error) { |
|
||||||
if err = binary.Write(w, binary.LittleEndian, col.lcidAndFlags); err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
err = binary.Write(w, binary.LittleEndian, col.sortId) |
|
||||||
return |
|
||||||
} |
|
@ -0,0 +1,12 @@ |
|||||||
|
// package mssql implements the TDS protocol used to connect to MS SQL Server (sqlserver)
|
||||||
|
// database servers.
|
||||||
|
//
|
||||||
|
// This package registers two drivers:
|
||||||
|
// sqlserver: uses native "@" parameter placeholder names and does no pre-processing.
|
||||||
|
// mssql: expects identifiers to be prefixed with ":" and pre-processes queries.
|
||||||
|
//
|
||||||
|
// If the ordinal position is used for query parameters, identifiers will be named
|
||||||
|
// "@p1", "@p2", ... "@pN".
|
||||||
|
//
|
||||||
|
// Please refer to the README for the format of the DSN.
|
||||||
|
package mssql |
@ -0,0 +1,20 @@ |
|||||||
|
package cp |
||||||
|
|
||||||
|
// http://msdn.microsoft.com/en-us/library/dd340437.aspx
|
||||||
|
|
||||||
|
type Collation struct { |
||||||
|
LcidAndFlags uint32 |
||||||
|
SortId uint8 |
||||||
|
} |
||||||
|
|
||||||
|
func (c Collation) getLcid() uint32 { |
||||||
|
return c.LcidAndFlags & 0x000fffff |
||||||
|
} |
||||||
|
|
||||||
|
func (c Collation) getFlags() uint32 { |
||||||
|
return (c.LcidAndFlags & 0x0ff00000) >> 20 |
||||||
|
} |
||||||
|
|
||||||
|
func (c Collation) getVersion() uint32 { |
||||||
|
return (c.LcidAndFlags & 0xf0000000) >> 28 |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1250 *charsetMap = &charsetMap{ |
var cp1250 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1251 *charsetMap = &charsetMap{ |
var cp1251 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1252 *charsetMap = &charsetMap{ |
var cp1252 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1253 *charsetMap = &charsetMap{ |
var cp1253 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1254 *charsetMap = &charsetMap{ |
var cp1254 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1255 *charsetMap = &charsetMap{ |
var cp1255 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1256 *charsetMap = &charsetMap{ |
var cp1256 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1257 *charsetMap = &charsetMap{ |
var cp1257 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp1258 *charsetMap = &charsetMap{ |
var cp1258 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp437 *charsetMap = &charsetMap{ |
var cp437 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp850 *charsetMap = &charsetMap{ |
var cp850 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp874 *charsetMap = &charsetMap{ |
var cp874 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp932 *charsetMap = &charsetMap{ |
var cp932 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp936 *charsetMap = &charsetMap{ |
var cp936 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp949 *charsetMap = &charsetMap{ |
var cp949 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,4 +1,4 @@ |
|||||||
package mssql |
package cp |
||||||
|
|
||||||
var cp950 *charsetMap = &charsetMap{ |
var cp950 *charsetMap = &charsetMap{ |
||||||
sb: [256]rune{ |
sb: [256]rune{ |
@ -1,11 +0,0 @@ |
|||||||
// +build go1.3
|
|
||||||
|
|
||||||
package mssql |
|
||||||
|
|
||||||
import ( |
|
||||||
"net" |
|
||||||
) |
|
||||||
|
|
||||||
func createDialer(p connectParams) *net.Dialer { |
|
||||||
return &net.Dialer{Timeout: p.dial_timeout, KeepAlive: p.keepAlive} |
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
// +build !go1.3
|
|
||||||
|
|
||||||
package mssql |
|
||||||
|
|
||||||
import ( |
|
||||||
"net" |
|
||||||
) |
|
||||||
|
|
||||||
func createDialer(p *connectParams) *net.Dialer { |
|
||||||
return &net.Dialer{Timeout: p.dial_timeout} |
|
||||||
} |
|
@ -0,0 +1,91 @@ |
|||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"errors" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
var _ driver.Pinger = &Conn{} |
||||||
|
|
||||||
|
// Ping is used to check if the remote server is available and satisfies the Pinger interface.
|
||||||
|
func (c *Conn) Ping(ctx context.Context) error { |
||||||
|
if !c.connectionGood { |
||||||
|
return driver.ErrBadConn |
||||||
|
} |
||||||
|
stmt := &Stmt{c, `select 1;`, 0, nil} |
||||||
|
_, err := stmt.ExecContext(ctx, nil) |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
var _ driver.ConnBeginTx = &Conn{} |
||||||
|
|
||||||
|
// BeginTx satisfies ConnBeginTx.
|
||||||
|
func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { |
||||||
|
if !c.connectionGood { |
||||||
|
return nil, driver.ErrBadConn |
||||||
|
} |
||||||
|
if opts.ReadOnly { |
||||||
|
return nil, errors.New("Read-only transactions are not supported") |
||||||
|
} |
||||||
|
|
||||||
|
var tdsIsolation isoLevel |
||||||
|
switch sql.IsolationLevel(opts.Isolation) { |
||||||
|
case sql.LevelDefault: |
||||||
|
tdsIsolation = isolationUseCurrent |
||||||
|
case sql.LevelReadUncommitted: |
||||||
|
tdsIsolation = isolationReadUncommited |
||||||
|
case sql.LevelReadCommitted: |
||||||
|
tdsIsolation = isolationReadCommited |
||||||
|
case sql.LevelWriteCommitted: |
||||||
|
return nil, errors.New("LevelWriteCommitted isolation level is not supported") |
||||||
|
case sql.LevelRepeatableRead: |
||||||
|
tdsIsolation = isolationRepeatableRead |
||||||
|
case sql.LevelSnapshot: |
||||||
|
tdsIsolation = isolationSnapshot |
||||||
|
case sql.LevelSerializable: |
||||||
|
tdsIsolation = isolationSerializable |
||||||
|
case sql.LevelLinearizable: |
||||||
|
return nil, errors.New("LevelLinearizable isolation level is not supported") |
||||||
|
default: |
||||||
|
return nil, errors.New("Isolation level is not supported or unknown") |
||||||
|
} |
||||||
|
return c.begin(ctx, tdsIsolation) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { |
||||||
|
if !c.connectionGood { |
||||||
|
return nil, driver.ErrBadConn |
||||||
|
} |
||||||
|
if len(query) > 10 && strings.EqualFold(query[:10], "INSERTBULK") { |
||||||
|
return c.prepareCopyIn(query) |
||||||
|
} |
||||||
|
|
||||||
|
return c.prepareContext(ctx, query) |
||||||
|
} |
||||||
|
|
||||||
|
func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { |
||||||
|
if !s.c.connectionGood { |
||||||
|
return nil, driver.ErrBadConn |
||||||
|
} |
||||||
|
list := make([]namedValue, len(args)) |
||||||
|
for i, nv := range args { |
||||||
|
list[i] = namedValue(nv) |
||||||
|
} |
||||||
|
return s.queryContext(ctx, list) |
||||||
|
} |
||||||
|
|
||||||
|
func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { |
||||||
|
if !s.c.connectionGood { |
||||||
|
return nil, driver.ErrBadConn |
||||||
|
} |
||||||
|
list := make([]namedValue, len(args)) |
||||||
|
for i, nv := range args { |
||||||
|
list[i] = namedValue(nv) |
||||||
|
} |
||||||
|
return s.exec(ctx, list) |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"fmt" |
||||||
|
// "github.com/cockroachdb/apd"
|
||||||
|
) |
||||||
|
|
||||||
|
// Type alias provided for compibility.
|
||||||
|
//
|
||||||
|
// Deprecated: users should transition to the new names when possible.
|
||||||
|
type MssqlDriver = Driver |
||||||
|
type MssqlBulk = Bulk |
||||||
|
type MssqlBulkOptions = BulkOptions |
||||||
|
type MssqlConn = Conn |
||||||
|
type MssqlResult = Result |
||||||
|
type MssqlRows = Rows |
||||||
|
type MssqlStmt = Stmt |
||||||
|
|
||||||
|
var _ driver.NamedValueChecker = &Conn{} |
||||||
|
|
||||||
|
func (c *Conn) CheckNamedValue(nv *driver.NamedValue) error { |
||||||
|
switch v := nv.Value.(type) { |
||||||
|
case sql.Out: |
||||||
|
if c.outs == nil { |
||||||
|
c.outs = make(map[string]interface{}) |
||||||
|
} |
||||||
|
c.outs[nv.Name] = v.Dest |
||||||
|
|
||||||
|
// Unwrap the Out value and check the inner value.
|
||||||
|
lnv := *nv |
||||||
|
lnv.Value = v.Dest |
||||||
|
err := c.CheckNamedValue(&lnv) |
||||||
|
if err != nil { |
||||||
|
if err != driver.ErrSkip { |
||||||
|
return err |
||||||
|
} |
||||||
|
lnv.Value, err = driver.DefaultParameterConverter.ConvertValue(lnv.Value) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
nv.Value = sql.Out{Dest: lnv.Value} |
||||||
|
return nil |
||||||
|
// case *apd.Decimal:
|
||||||
|
// return nil
|
||||||
|
default: |
||||||
|
return driver.ErrSkip |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s *Stmt) makeParamExtra(val driver.Value) (res Param, err error) { |
||||||
|
switch val := val.(type) { |
||||||
|
case sql.Out: |
||||||
|
res, err = s.makeParam(val.Dest) |
||||||
|
res.Flags = fByRevValue |
||||||
|
default: |
||||||
|
err = fmt.Errorf("mssql: unknown type for %T", val) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"database/sql/driver" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
func (s *Stmt) makeParamExtra(val driver.Value) (Param, error) { |
||||||
|
return Param{}, fmt.Errorf("mssql: unknown type for %T", val) |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
// Code generated by "stringer -type token"; DO NOT EDIT
|
||||||
|
|
||||||
|
package mssql |
||||||
|
|
||||||
|
import "fmt" |
||||||
|
|
||||||
|
const ( |
||||||
|
_token_name_0 = "tokenReturnStatus" |
||||||
|
_token_name_1 = "tokenColMetadata" |
||||||
|
_token_name_2 = "tokenOrdertokenErrortokenInfo" |
||||||
|
_token_name_3 = "tokenLoginAck" |
||||||
|
_token_name_4 = "tokenRowtokenNbcRow" |
||||||
|
_token_name_5 = "tokenEnvChange" |
||||||
|
_token_name_6 = "tokenSSPI" |
||||||
|
_token_name_7 = "tokenDonetokenDoneProctokenDoneInProc" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
_token_index_0 = [...]uint8{0, 17} |
||||||
|
_token_index_1 = [...]uint8{0, 16} |
||||||
|
_token_index_2 = [...]uint8{0, 10, 20, 29} |
||||||
|
_token_index_3 = [...]uint8{0, 13} |
||||||
|
_token_index_4 = [...]uint8{0, 8, 19} |
||||||
|
_token_index_5 = [...]uint8{0, 14} |
||||||
|
_token_index_6 = [...]uint8{0, 9} |
||||||
|
_token_index_7 = [...]uint8{0, 9, 22, 37} |
||||||
|
) |
||||||
|
|
||||||
|
func (i token) String() string { |
||||||
|
switch { |
||||||
|
case i == 121: |
||||||
|
return _token_name_0 |
||||||
|
case i == 129: |
||||||
|
return _token_name_1 |
||||||
|
case 169 <= i && i <= 171: |
||||||
|
i -= 169 |
||||||
|
return _token_name_2[_token_index_2[i]:_token_index_2[i+1]] |
||||||
|
case i == 173: |
||||||
|
return _token_name_3 |
||||||
|
case 209 <= i && i <= 210: |
||||||
|
i -= 209 |
||||||
|
return _token_name_4[_token_index_4[i]:_token_index_4[i+1]] |
||||||
|
case i == 227: |
||||||
|
return _token_name_5 |
||||||
|
case i == 237: |
||||||
|
return _token_name_6 |
||||||
|
case 253 <= i && i <= 255: |
||||||
|
i -= 253 |
||||||
|
return _token_name_7[_token_index_7[i]:_token_index_7[i+1]] |
||||||
|
default: |
||||||
|
return fmt.Sprintf("token(%d)", i) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package mssql |
||||||
|
|
||||||
|
import ( |
||||||
|
"database/sql/driver" |
||||||
|
"encoding/hex" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type UniqueIdentifier [16]byte |
||||||
|
|
||||||
|
func (u *UniqueIdentifier) Scan(v interface{}) error { |
||||||
|
reverse := func(b []byte) { |
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { |
||||||
|
b[i], b[j] = b[j], b[i] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch vt := v.(type) { |
||||||
|
case []byte: |
||||||
|
if len(vt) != 16 { |
||||||
|
return errors.New("mssql: invalid UniqueIdentifier length") |
||||||
|
} |
||||||
|
|
||||||
|
var raw UniqueIdentifier |
||||||
|
|
||||||
|
copy(raw[:], vt) |
||||||
|
|
||||||
|
reverse(raw[0:4]) |
||||||
|
reverse(raw[4:6]) |
||||||
|
reverse(raw[6:8]) |
||||||
|
*u = raw |
||||||
|
|
||||||
|
return nil |
||||||
|
case string: |
||||||
|
if len(vt) != 36 { |
||||||
|
return errors.New("mssql: invalid UniqueIdentifier string length") |
||||||
|
} |
||||||
|
|
||||||
|
b := []byte(vt) |
||||||
|
for i, c := range b { |
||||||
|
switch c { |
||||||
|
case '-': |
||||||
|
b = append(b[:i], b[i+1:]...) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_, err := hex.Decode(u[:], []byte(b)) |
||||||
|
return err |
||||||
|
default: |
||||||
|
return fmt.Errorf("mssql: cannot convert %T to UniqueIdentifier", v) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (u UniqueIdentifier) Value() (driver.Value, error) { |
||||||
|
reverse := func(b []byte) { |
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { |
||||||
|
b[i], b[j] = b[j], b[i] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
raw := make([]byte, len(u)) |
||||||
|
copy(raw, u[:]) |
||||||
|
|
||||||
|
reverse(raw[0:4]) |
||||||
|
reverse(raw[4:6]) |
||||||
|
reverse(raw[6:8]) |
||||||
|
|
||||||
|
return raw, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (u UniqueIdentifier) String() string { |
||||||
|
return fmt.Sprintf("%X-%X-%X-%X-%X", u[0:4], u[4:6], u[6:8], u[8:10], u[10:]) |
||||||
|
} |
Loading…
Reference in new issue