mirror of
https://github.com/go-gitea/gitea.git
synced 2024-08-23 04:40:50 +02:00
* Enables mssql support Port of dlobs work in gogs. Enables options in index.js Enables MSSQL as a database option in go. Sets ID to 0 on initial migration. Required for MSSQL insert statements. Signed-off-by: Beau Trepp <beautrepp@gmail.com> * Vendors in denisenkom/go-mssqldb Includes golang.org/x/crypto/md4 as this is required by go-msssqldb Signed-off-by: Beau Trepp <beautrepp@gmail.com>
846 lines
20 KiB
Go
846 lines
20 KiB
Go
package mssql
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// fixed-length data types
|
|
// http://msdn.microsoft.com/en-us/library/dd341171.aspx
|
|
const (
|
|
typeNull = 0x1f
|
|
typeInt1 = 0x30
|
|
typeBit = 0x32
|
|
typeInt2 = 0x34
|
|
typeInt4 = 0x38
|
|
typeDateTim4 = 0x3a
|
|
typeFlt4 = 0x3b
|
|
typeMoney = 0x3c
|
|
typeDateTime = 0x3d
|
|
typeFlt8 = 0x3e
|
|
typeMoney4 = 0x7a
|
|
typeInt8 = 0x7f
|
|
)
|
|
|
|
// variable-length data types
|
|
// http://msdn.microsoft.com/en-us/library/dd358341.aspx
|
|
const (
|
|
// byte len types
|
|
typeGuid = 0x24
|
|
typeIntN = 0x26
|
|
typeDecimal = 0x37 // legacy
|
|
typeNumeric = 0x3f // legacy
|
|
typeBitN = 0x68
|
|
typeDecimalN = 0x6a
|
|
typeNumericN = 0x6c
|
|
typeFltN = 0x6d
|
|
typeMoneyN = 0x6e
|
|
typeDateTimeN = 0x6f
|
|
typeDateN = 0x28
|
|
typeTimeN = 0x29
|
|
typeDateTime2N = 0x2a
|
|
typeDateTimeOffsetN = 0x2b
|
|
typeChar = 0x2f // legacy
|
|
typeVarChar = 0x27 // legacy
|
|
typeBinary = 0x2d // legacy
|
|
typeVarBinary = 0x25 // legacy
|
|
|
|
// short length types
|
|
typeBigVarBin = 0xa5
|
|
typeBigVarChar = 0xa7
|
|
typeBigBinary = 0xad
|
|
typeBigChar = 0xaf
|
|
typeNVarChar = 0xe7
|
|
typeNChar = 0xef
|
|
typeXml = 0xf1
|
|
typeUdt = 0xf0
|
|
|
|
// long length types
|
|
typeText = 0x23
|
|
typeImage = 0x22
|
|
typeNText = 0x63
|
|
typeVariant = 0x62
|
|
)
|
|
|
|
// TYPE_INFO rule
|
|
// http://msdn.microsoft.com/en-us/library/dd358284.aspx
|
|
type typeInfo struct {
|
|
TypeId uint8
|
|
Size int
|
|
Scale uint8
|
|
Prec uint8
|
|
Buffer []byte
|
|
Collation collation
|
|
Reader func(ti *typeInfo, r *tdsBuffer) (res interface{})
|
|
Writer func(w io.Writer, ti typeInfo, buf []byte) (err error)
|
|
}
|
|
|
|
func readTypeInfo(r *tdsBuffer) (res typeInfo) {
|
|
res.TypeId = r.byte()
|
|
switch res.TypeId {
|
|
case typeNull, typeInt1, typeBit, typeInt2, typeInt4, typeDateTim4,
|
|
typeFlt4, typeMoney, typeDateTime, typeFlt8, typeMoney4, typeInt8:
|
|
// those are fixed length types
|
|
switch res.TypeId {
|
|
case typeNull:
|
|
res.Size = 0
|
|
case typeInt1, typeBit:
|
|
res.Size = 1
|
|
case typeInt2:
|
|
res.Size = 2
|
|
case typeInt4, typeDateTim4, typeFlt4, typeMoney4:
|
|
res.Size = 4
|
|
case typeMoney, typeDateTime, typeFlt8, typeInt8:
|
|
res.Size = 8
|
|
}
|
|
res.Reader = readFixedType
|
|
res.Buffer = make([]byte, res.Size)
|
|
default: // all others are VARLENTYPE
|
|
readVarLen(&res, r)
|
|
}
|
|
return
|
|
}
|
|
|
|
func writeTypeInfo(w io.Writer, ti *typeInfo) (err error) {
|
|
err = binary.Write(w, binary.LittleEndian, ti.TypeId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
switch ti.TypeId {
|
|
case typeNull, typeInt1, typeBit, typeInt2, typeInt4, typeDateTim4,
|
|
typeFlt4, typeMoney, typeDateTime, typeFlt8, typeMoney4, typeInt8:
|
|
// those are fixed length types
|
|
default: // all others are VARLENTYPE
|
|
err = writeVarLen(w, ti)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func writeVarLen(w io.Writer, ti *typeInfo) (err error) {
|
|
switch ti.TypeId {
|
|
case typeDateN:
|
|
|
|
case typeTimeN, typeDateTime2N, typeDateTimeOffsetN:
|
|
if err = binary.Write(w, binary.LittleEndian, ti.Scale); err != nil {
|
|
return
|
|
}
|
|
ti.Writer = writeByteLenType
|
|
case typeGuid, typeIntN, typeDecimal, typeNumeric,
|
|
typeBitN, typeDecimalN, typeNumericN, typeFltN,
|
|
typeMoneyN, typeDateTimeN, typeChar,
|
|
typeVarChar, typeBinary, typeVarBinary:
|
|
// byle len types
|
|
if ti.Size > 0xff {
|
|
panic("Invalid size for BYLELEN_TYPE")
|
|
}
|
|
if err = binary.Write(w, binary.LittleEndian, uint8(ti.Size)); err != nil {
|
|
return
|
|
}
|
|
switch ti.TypeId {
|
|
case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
|
|
err = binary.Write(w, binary.LittleEndian, ti.Prec)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = binary.Write(w, binary.LittleEndian, ti.Scale)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
ti.Writer = writeByteLenType
|
|
case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar,
|
|
typeNVarChar, typeNChar, typeXml, typeUdt:
|
|
// short len types
|
|
if ti.Size > 8000 || ti.Size == 0 {
|
|
if err = binary.Write(w, binary.LittleEndian, uint16(0xffff)); err != nil {
|
|
return
|
|
}
|
|
ti.Writer = writePLPType
|
|
} else {
|
|
if err = binary.Write(w, binary.LittleEndian, uint16(ti.Size)); err != nil {
|
|
return
|
|
}
|
|
ti.Writer = writeShortLenType
|
|
}
|
|
switch ti.TypeId {
|
|
case typeBigVarChar, typeBigChar, typeNVarChar, typeNChar:
|
|
if err = writeCollation(w, ti.Collation); err != nil {
|
|
return
|
|
}
|
|
case typeXml:
|
|
var schemapresent uint8 = 0
|
|
if err = binary.Write(w, binary.LittleEndian, schemapresent); err != nil {
|
|
return
|
|
}
|
|
}
|
|
case typeText, typeImage, typeNText, typeVariant:
|
|
// LONGLEN_TYPE
|
|
panic("LONGLEN_TYPE not implemented")
|
|
default:
|
|
panic("Invalid type")
|
|
}
|
|
return
|
|
}
|
|
|
|
// http://msdn.microsoft.com/en-us/library/ee780895.aspx
|
|
func decodeDateTim4(buf []byte) time.Time {
|
|
days := binary.LittleEndian.Uint16(buf)
|
|
mins := binary.LittleEndian.Uint16(buf[2:])
|
|
return time.Date(1900, 1, 1+int(days),
|
|
0, int(mins), 0, 0, time.UTC)
|
|
}
|
|
|
|
func decodeDateTime(buf []byte) time.Time {
|
|
days := int32(binary.LittleEndian.Uint32(buf))
|
|
tm := binary.LittleEndian.Uint32(buf[4:])
|
|
ns := int(math.Trunc(float64(tm%300)/0.3+0.5)) * 1000000
|
|
secs := int(tm / 300)
|
|
return time.Date(1900, 1, 1+int(days),
|
|
0, 0, secs, ns, time.UTC)
|
|
}
|
|
|
|
func readFixedType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
r.ReadFull(ti.Buffer)
|
|
buf := ti.Buffer
|
|
switch ti.TypeId {
|
|
case typeNull:
|
|
return nil
|
|
case typeInt1:
|
|
return int64(buf[0])
|
|
case typeBit:
|
|
return buf[0] != 0
|
|
case typeInt2:
|
|
return int64(int16(binary.LittleEndian.Uint16(buf)))
|
|
case typeInt4:
|
|
return int64(int32(binary.LittleEndian.Uint32(buf)))
|
|
case typeDateTim4:
|
|
return decodeDateTim4(buf)
|
|
case typeFlt4:
|
|
return math.Float32frombits(binary.LittleEndian.Uint32(buf))
|
|
case typeMoney4:
|
|
return decodeMoney4(buf)
|
|
case typeMoney:
|
|
return decodeMoney(buf)
|
|
case typeDateTime:
|
|
return decodeDateTime(buf)
|
|
case typeFlt8:
|
|
return math.Float64frombits(binary.LittleEndian.Uint64(buf))
|
|
case typeInt8:
|
|
return int64(binary.LittleEndian.Uint64(buf))
|
|
default:
|
|
badStreamPanicf("Invalid typeid")
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
func writeFixedType(w io.Writer, ti typeInfo, buf []byte) (err error) {
|
|
_, err = w.Write(buf)
|
|
return
|
|
}
|
|
|
|
func readByteLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
size := r.byte()
|
|
if size == 0 {
|
|
return nil
|
|
}
|
|
r.ReadFull(ti.Buffer[:size])
|
|
buf := ti.Buffer[:size]
|
|
switch ti.TypeId {
|
|
case typeDateN:
|
|
if len(buf) != 3 {
|
|
badStreamPanicf("Invalid size for DATENTYPE")
|
|
}
|
|
return decodeDate(buf)
|
|
case typeTimeN:
|
|
return decodeTime(ti.Scale, buf)
|
|
case typeDateTime2N:
|
|
return decodeDateTime2(ti.Scale, buf)
|
|
case typeDateTimeOffsetN:
|
|
return decodeDateTimeOffset(ti.Scale, buf)
|
|
case typeGuid:
|
|
return decodeGuid(buf)
|
|
case typeIntN:
|
|
switch len(buf) {
|
|
case 1:
|
|
return int64(buf[0])
|
|
case 2:
|
|
return int64(int16((binary.LittleEndian.Uint16(buf))))
|
|
case 4:
|
|
return int64(int32(binary.LittleEndian.Uint32(buf)))
|
|
case 8:
|
|
return int64(binary.LittleEndian.Uint64(buf))
|
|
default:
|
|
badStreamPanicf("Invalid size for INTNTYPE")
|
|
}
|
|
case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
|
|
return decodeDecimal(ti.Prec, ti.Scale, buf)
|
|
case typeBitN:
|
|
if len(buf) != 1 {
|
|
badStreamPanicf("Invalid size for BITNTYPE")
|
|
}
|
|
return buf[0] != 0
|
|
case typeFltN:
|
|
switch len(buf) {
|
|
case 4:
|
|
return float64(math.Float32frombits(binary.LittleEndian.Uint32(buf)))
|
|
case 8:
|
|
return math.Float64frombits(binary.LittleEndian.Uint64(buf))
|
|
default:
|
|
badStreamPanicf("Invalid size for FLTNTYPE")
|
|
}
|
|
case typeMoneyN:
|
|
switch len(buf) {
|
|
case 4:
|
|
return decodeMoney4(buf)
|
|
case 8:
|
|
return decodeMoney(buf)
|
|
default:
|
|
badStreamPanicf("Invalid size for MONEYNTYPE")
|
|
}
|
|
case typeDateTimeN:
|
|
switch len(buf) {
|
|
case 4:
|
|
return decodeDateTim4(buf)
|
|
case 8:
|
|
return decodeDateTime(buf)
|
|
default:
|
|
badStreamPanicf("Invalid size for DATETIMENTYPE")
|
|
}
|
|
case typeChar, typeVarChar:
|
|
return decodeChar(ti.Collation, buf)
|
|
case typeBinary, typeVarBinary:
|
|
// a copy, because the backing array for ti.Buffer is reused
|
|
// and can be overwritten by the next row while this row waits
|
|
// in a buffered chan
|
|
cpy := make([]byte, len(buf))
|
|
copy(cpy, buf)
|
|
return cpy
|
|
default:
|
|
badStreamPanicf("Invalid typeid")
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
func writeByteLenType(w io.Writer, ti typeInfo, buf []byte) (err error) {
|
|
if ti.Size > 0xff {
|
|
panic("Invalid size for BYTELEN_TYPE")
|
|
}
|
|
err = binary.Write(w, binary.LittleEndian, uint8(ti.Size))
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(buf)
|
|
return
|
|
}
|
|
|
|
func readShortLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
size := r.uint16()
|
|
if size == 0xffff {
|
|
return nil
|
|
}
|
|
r.ReadFull(ti.Buffer[:size])
|
|
buf := ti.Buffer[:size]
|
|
switch ti.TypeId {
|
|
case typeBigVarChar, typeBigChar:
|
|
return decodeChar(ti.Collation, buf)
|
|
case typeBigVarBin, typeBigBinary:
|
|
// a copy, because the backing array for ti.Buffer is reused
|
|
// and can be overwritten by the next row while this row waits
|
|
// in a buffered chan
|
|
cpy := make([]byte, len(buf))
|
|
copy(cpy, buf)
|
|
return cpy
|
|
case typeNVarChar, typeNChar:
|
|
return decodeNChar(buf)
|
|
case typeUdt:
|
|
return decodeUdt(*ti, buf)
|
|
default:
|
|
badStreamPanicf("Invalid typeid")
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
func writeShortLenType(w io.Writer, ti typeInfo, buf []byte) (err error) {
|
|
if buf == nil {
|
|
err = binary.Write(w, binary.LittleEndian, uint16(0xffff))
|
|
return
|
|
}
|
|
if ti.Size > 0xfffe {
|
|
panic("Invalid size for USHORTLEN_TYPE")
|
|
}
|
|
err = binary.Write(w, binary.LittleEndian, uint16(ti.Size))
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(buf)
|
|
return
|
|
}
|
|
|
|
func readLongLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
// information about this format can be found here:
|
|
// http://msdn.microsoft.com/en-us/library/dd304783.aspx
|
|
// and here:
|
|
// http://msdn.microsoft.com/en-us/library/dd357254.aspx
|
|
textptrsize := int(r.byte())
|
|
if textptrsize == 0 {
|
|
return nil
|
|
}
|
|
textptr := make([]byte, textptrsize)
|
|
r.ReadFull(textptr)
|
|
timestamp := r.uint64()
|
|
_ = timestamp // ignore timestamp
|
|
size := r.int32()
|
|
if size == -1 {
|
|
return nil
|
|
}
|
|
buf := make([]byte, size)
|
|
r.ReadFull(buf)
|
|
switch ti.TypeId {
|
|
case typeText:
|
|
return decodeChar(ti.Collation, buf)
|
|
case typeImage:
|
|
return buf
|
|
case typeNText:
|
|
return decodeNChar(buf)
|
|
default:
|
|
badStreamPanicf("Invalid typeid")
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
// reads variant value
|
|
// http://msdn.microsoft.com/en-us/library/dd303302.aspx
|
|
func readVariantType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
size := r.int32()
|
|
if size == 0 {
|
|
return nil
|
|
}
|
|
vartype := r.byte()
|
|
propbytes := int32(r.byte())
|
|
switch vartype {
|
|
case typeGuid:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return buf
|
|
case typeBit:
|
|
return r.byte() != 0
|
|
case typeInt1:
|
|
return int64(r.byte())
|
|
case typeInt2:
|
|
return int64(int16(r.uint16()))
|
|
case typeInt4:
|
|
return int64(r.int32())
|
|
case typeInt8:
|
|
return int64(r.uint64())
|
|
case typeDateTime:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDateTime(buf)
|
|
case typeDateTim4:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDateTim4(buf)
|
|
case typeFlt4:
|
|
return float64(math.Float32frombits(r.uint32()))
|
|
case typeFlt8:
|
|
return math.Float64frombits(r.uint64())
|
|
case typeMoney4:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeMoney4(buf)
|
|
case typeMoney:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeMoney(buf)
|
|
case typeDateN:
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDate(buf)
|
|
case typeTimeN:
|
|
scale := r.byte()
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeTime(scale, buf)
|
|
case typeDateTime2N:
|
|
scale := r.byte()
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDateTime2(scale, buf)
|
|
case typeDateTimeOffsetN:
|
|
scale := r.byte()
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDateTimeOffset(scale, buf)
|
|
case typeBigVarBin, typeBigBinary:
|
|
r.uint16() // max length, ignoring
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return buf
|
|
case typeDecimalN, typeNumericN:
|
|
prec := r.byte()
|
|
scale := r.byte()
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeDecimal(prec, scale, buf)
|
|
case typeBigVarChar, typeBigChar:
|
|
col := readCollation(r)
|
|
r.uint16() // max length, ignoring
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeChar(col, buf)
|
|
case typeNVarChar, typeNChar:
|
|
_ = readCollation(r)
|
|
r.uint16() // max length, ignoring
|
|
buf := make([]byte, size-2-propbytes)
|
|
r.ReadFull(buf)
|
|
return decodeNChar(buf)
|
|
default:
|
|
badStreamPanicf("Invalid variant typeid")
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
// partially length prefixed stream
|
|
// http://msdn.microsoft.com/en-us/library/dd340469.aspx
|
|
func readPLPType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
|
|
size := r.uint64()
|
|
var buf *bytes.Buffer
|
|
switch size {
|
|
case 0xffffffffffffffff:
|
|
// null
|
|
return nil
|
|
case 0xfffffffffffffffe:
|
|
// size unknown
|
|
buf = bytes.NewBuffer(make([]byte, 0, 1000))
|
|
default:
|
|
buf = bytes.NewBuffer(make([]byte, 0, size))
|
|
}
|
|
for true {
|
|
chunksize := r.uint32()
|
|
if chunksize == 0 {
|
|
break
|
|
}
|
|
if _, err := io.CopyN(buf, r, int64(chunksize)); err != nil {
|
|
badStreamPanicf("Reading PLP type failed: %s", err.Error())
|
|
}
|
|
}
|
|
switch ti.TypeId {
|
|
case typeXml:
|
|
return decodeXml(*ti, buf.Bytes())
|
|
case typeBigVarChar, typeBigChar, typeText:
|
|
return decodeChar(ti.Collation, buf.Bytes())
|
|
case typeBigVarBin, typeBigBinary, typeImage:
|
|
return buf.Bytes()
|
|
case typeNVarChar, typeNChar, typeNText:
|
|
return decodeNChar(buf.Bytes())
|
|
case typeUdt:
|
|
return decodeUdt(*ti, buf.Bytes())
|
|
}
|
|
panic("shoulnd't get here")
|
|
}
|
|
|
|
func writePLPType(w io.Writer, ti typeInfo, buf []byte) (err error) {
|
|
if err = binary.Write(w, binary.LittleEndian, uint64(len(buf))); err != nil {
|
|
return
|
|
}
|
|
for {
|
|
chunksize := uint32(len(buf))
|
|
if err = binary.Write(w, binary.LittleEndian, chunksize); err != nil {
|
|
return
|
|
}
|
|
if chunksize == 0 {
|
|
return
|
|
}
|
|
if _, err = w.Write(buf[:chunksize]); err != nil {
|
|
return
|
|
}
|
|
buf = buf[chunksize:]
|
|
}
|
|
}
|
|
|
|
func readVarLen(ti *typeInfo, r *tdsBuffer) {
|
|
switch ti.TypeId {
|
|
case typeDateN:
|
|
ti.Size = 3
|
|
ti.Reader = readByteLenType
|
|
ti.Buffer = make([]byte, ti.Size)
|
|
case typeTimeN, typeDateTime2N, typeDateTimeOffsetN:
|
|
ti.Scale = r.byte()
|
|
switch ti.Scale {
|
|
case 0, 1, 2:
|
|
ti.Size = 3
|
|
case 3, 4:
|
|
ti.Size = 4
|
|
case 5, 6, 7:
|
|
ti.Size = 5
|
|
default:
|
|
badStreamPanicf("Invalid scale for TIME/DATETIME2/DATETIMEOFFSET type")
|
|
}
|
|
switch ti.TypeId {
|
|
case typeDateTime2N:
|
|
ti.Size += 3
|
|
case typeDateTimeOffsetN:
|
|
ti.Size += 5
|
|
}
|
|
ti.Reader = readByteLenType
|
|
ti.Buffer = make([]byte, ti.Size)
|
|
case typeGuid, typeIntN, typeDecimal, typeNumeric,
|
|
typeBitN, typeDecimalN, typeNumericN, typeFltN,
|
|
typeMoneyN, typeDateTimeN, typeChar,
|
|
typeVarChar, typeBinary, typeVarBinary:
|
|
// byle len types
|
|
ti.Size = int(r.byte())
|
|
ti.Buffer = make([]byte, ti.Size)
|
|
switch ti.TypeId {
|
|
case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
|
|
ti.Prec = r.byte()
|
|
ti.Scale = r.byte()
|
|
}
|
|
ti.Reader = readByteLenType
|
|
case typeXml:
|
|
schemapresent := r.byte()
|
|
if schemapresent != 0 {
|
|
// just ignore this for now
|
|
// dbname
|
|
r.BVarChar()
|
|
// owning schema
|
|
r.BVarChar()
|
|
// xml schema collection
|
|
r.UsVarChar()
|
|
}
|
|
ti.Reader = readPLPType
|
|
case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar,
|
|
typeNVarChar, typeNChar, typeUdt:
|
|
// short len types
|
|
ti.Size = int(r.uint16())
|
|
switch ti.TypeId {
|
|
case typeBigVarChar, typeBigChar, typeNVarChar, typeNChar:
|
|
ti.Collation = readCollation(r)
|
|
}
|
|
if ti.Size == 0xffff {
|
|
ti.Reader = readPLPType
|
|
} else {
|
|
ti.Buffer = make([]byte, ti.Size)
|
|
ti.Reader = readShortLenType
|
|
}
|
|
case typeText, typeImage, typeNText, typeVariant:
|
|
// LONGLEN_TYPE
|
|
ti.Size = int(r.int32())
|
|
switch ti.TypeId {
|
|
case typeText, typeNText:
|
|
ti.Collation = readCollation(r)
|
|
// ignore tablenames
|
|
numparts := int(r.byte())
|
|
for i := 0; i < numparts; i++ {
|
|
r.UsVarChar()
|
|
}
|
|
ti.Reader = readLongLenType
|
|
case typeImage:
|
|
// ignore tablenames
|
|
numparts := int(r.byte())
|
|
for i := 0; i < numparts; i++ {
|
|
r.UsVarChar()
|
|
}
|
|
ti.Reader = readLongLenType
|
|
case typeVariant:
|
|
ti.Reader = readVariantType
|
|
}
|
|
default:
|
|
badStreamPanicf("Invalid type %d", ti.TypeId)
|
|
}
|
|
return
|
|
}
|
|
|
|
func decodeMoney(buf []byte) []byte {
|
|
money := int64(uint64(buf[4]) |
|
|
uint64(buf[5])<<8 |
|
|
uint64(buf[6])<<16 |
|
|
uint64(buf[7])<<24 |
|
|
uint64(buf[0])<<32 |
|
|
uint64(buf[1])<<40 |
|
|
uint64(buf[2])<<48 |
|
|
uint64(buf[3])<<56)
|
|
return scaleBytes(strconv.FormatInt(money, 10), 4)
|
|
}
|
|
|
|
func decodeMoney4(buf []byte) []byte {
|
|
money := int32(binary.LittleEndian.Uint32(buf[0:4]))
|
|
return scaleBytes(strconv.FormatInt(int64(money), 10), 4)
|
|
}
|
|
|
|
func decodeGuid(buf []byte) []byte {
|
|
res := make([]byte, 16)
|
|
copy(res, buf)
|
|
return res
|
|
}
|
|
|
|
func decodeDecimal(prec uint8, scale uint8, buf []byte) []byte {
|
|
var sign uint8
|
|
sign = buf[0]
|
|
dec := Decimal{
|
|
positive: sign != 0,
|
|
prec: prec,
|
|
scale: scale,
|
|
}
|
|
buf = buf[1:]
|
|
l := len(buf) / 4
|
|
for i := 0; i < l; i++ {
|
|
dec.integer[i] = binary.LittleEndian.Uint32(buf[0:4])
|
|
buf = buf[4:]
|
|
}
|
|
return dec.Bytes()
|
|
}
|
|
|
|
// http://msdn.microsoft.com/en-us/library/ee780895.aspx
|
|
func decodeDateInt(buf []byte) (days int) {
|
|
return int(buf[0]) + int(buf[1])*256 + int(buf[2])*256*256
|
|
}
|
|
|
|
func decodeDate(buf []byte) time.Time {
|
|
return time.Date(1, 1, 1+decodeDateInt(buf), 0, 0, 0, 0, time.UTC)
|
|
}
|
|
|
|
func decodeTimeInt(scale uint8, buf []byte) (sec int, ns int) {
|
|
var acc uint64 = 0
|
|
for i := len(buf) - 1; i >= 0; i-- {
|
|
acc <<= 8
|
|
acc |= uint64(buf[i])
|
|
}
|
|
for i := 0; i < 7-int(scale); i++ {
|
|
acc *= 10
|
|
}
|
|
nsbig := acc * 100
|
|
sec = int(nsbig / 1000000000)
|
|
ns = int(nsbig % 1000000000)
|
|
return
|
|
}
|
|
|
|
func decodeTime(scale uint8, buf []byte) time.Time {
|
|
sec, ns := decodeTimeInt(scale, buf)
|
|
return time.Date(1, 1, 1, 0, 0, sec, ns, time.UTC)
|
|
}
|
|
|
|
func decodeDateTime2(scale uint8, buf []byte) time.Time {
|
|
timesize := len(buf) - 3
|
|
sec, ns := decodeTimeInt(scale, buf[:timesize])
|
|
days := decodeDateInt(buf[timesize:])
|
|
return time.Date(1, 1, 1+days, 0, 0, sec, ns, time.UTC)
|
|
}
|
|
|
|
func decodeDateTimeOffset(scale uint8, buf []byte) time.Time {
|
|
timesize := len(buf) - 3 - 2
|
|
sec, ns := decodeTimeInt(scale, buf[:timesize])
|
|
buf = buf[timesize:]
|
|
days := decodeDateInt(buf[:3])
|
|
buf = buf[3:]
|
|
offset := int(int16(binary.LittleEndian.Uint16(buf))) // in mins
|
|
return time.Date(1, 1, 1+days, 0, 0, sec+offset*60, ns,
|
|
time.FixedZone("", offset*60))
|
|
}
|
|
|
|
func divFloor(x int64, y int64) int64 {
|
|
q := x / y
|
|
r := x % y
|
|
if r != 0 && ((r < 0) != (y < 0)) {
|
|
q--
|
|
}
|
|
return q
|
|
}
|
|
|
|
func dateTime2(t time.Time) (days int32, ns int64) {
|
|
// number of days since Jan 1 1970 UTC
|
|
days64 := divFloor(t.Unix(), 24*60*60)
|
|
// number of days since Jan 1 1 UTC
|
|
days = int32(days64) + 1969*365 + 1969/4 - 1969/100 + 1969/400
|
|
// number of seconds within day
|
|
secs := t.Unix() - days64*24*60*60
|
|
// number of nanoseconds within day
|
|
ns = secs*1e9 + int64(t.Nanosecond())
|
|
return
|
|
}
|
|
|
|
func decodeChar(col collation, buf []byte) string {
|
|
return charset2utf8(col, buf)
|
|
}
|
|
|
|
func decodeUcs2(buf []byte) string {
|
|
res, err := ucs22str(buf)
|
|
if err != nil {
|
|
badStreamPanicf("Invalid UCS2 encoding: %s", err.Error())
|
|
}
|
|
return res
|
|
}
|
|
|
|
func decodeNChar(buf []byte) string {
|
|
return decodeUcs2(buf)
|
|
}
|
|
|
|
func decodeXml(ti typeInfo, buf []byte) string {
|
|
return decodeUcs2(buf)
|
|
}
|
|
|
|
func decodeUdt(ti typeInfo, buf []byte) int {
|
|
panic("Not implemented")
|
|
}
|
|
|
|
func makeDecl(ti typeInfo) string {
|
|
switch ti.TypeId {
|
|
case typeInt8:
|
|
return "bigint"
|
|
case typeFlt4:
|
|
return "real"
|
|
case typeIntN:
|
|
switch ti.Size {
|
|
case 1:
|
|
return "tinyint"
|
|
case 2:
|
|
return "smallint"
|
|
case 4:
|
|
return "int"
|
|
case 8:
|
|
return "bigint"
|
|
default:
|
|
panic("invalid size of INTNTYPE")
|
|
}
|
|
case typeFlt8:
|
|
return "float"
|
|
case typeFltN:
|
|
switch ti.Size {
|
|
case 4:
|
|
return "real"
|
|
case 8:
|
|
return "float"
|
|
default:
|
|
panic("invalid size of FLNNTYPE")
|
|
}
|
|
case typeBigVarBin:
|
|
if ti.Size > 8000 || ti.Size == 0 {
|
|
return fmt.Sprintf("varbinary(max)")
|
|
} else {
|
|
return fmt.Sprintf("varbinary(%d)", ti.Size)
|
|
}
|
|
case typeNVarChar:
|
|
if ti.Size > 8000 || ti.Size == 0 {
|
|
return fmt.Sprintf("nvarchar(max)")
|
|
} else {
|
|
return fmt.Sprintf("nvarchar(%d)", ti.Size/2)
|
|
}
|
|
case typeBit, typeBitN:
|
|
return "bit"
|
|
case typeDateTimeN:
|
|
return "datetime"
|
|
case typeDateTimeOffsetN:
|
|
return fmt.Sprintf("datetimeoffset(%d)", ti.Scale)
|
|
default:
|
|
panic(fmt.Sprintf("not implemented makeDecl for type %d", ti.TypeId))
|
|
}
|
|
}
|