package mssql import ( "bytes" "encoding/binary" "fmt" "io" "math" "reflect" "strconv" "time" "github.com/denisenkom/go-mssqldb/internal/cp" ) // 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 typeTvp = 0xf3 // long length types typeText = 0x23 typeImage = 0x22 typeNText = 0x63 typeVariant = 0x62 ) const _PLP_NULL = 0xFFFFFFFFFFFFFFFF const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE const _PLP_TERMINATOR = 0x00000000 // TVP COLUMN FLAGS const _TVP_END_TOKEN = 0x00 const _TVP_ROW_TOKEN = 0x01 // 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 cp.Collation UdtInfo udtInfo XmlInfo xmlInfo Reader func(ti *typeInfo, r *tdsBuffer) (res interface{}) Writer func(w io.Writer, ti typeInfo, buf []byte) (err error) } // Common Language Runtime (CLR) Instances // http://msdn.microsoft.com/en-us/library/dd357962.aspx type udtInfo struct { //MaxByteSize uint32 DBName string SchemaName string TypeName string AssemblyQualifiedName string } // XML Values // http://msdn.microsoft.com/en-us/library/dd304764.aspx type xmlInfo struct { SchemaPresent uint8 DBName string OwningSchema string XmlSchemaCollection string } 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 } // https://msdn.microsoft.com/en-us/library/dd358284.aspx 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 // https://msdn.microsoft.com/en-us/library/dd341171.aspx ti.Writer = writeFixedType case typeTvp: ti.Writer = writeFixedType default: // all others are VARLENTYPE err = writeVarLen(w, ti) if err != nil { return } } return } func writeFixedType(w io.Writer, ti typeInfo, buf []byte) (err error) { _, err = w.Write(buf) return } // https://msdn.microsoft.com/en-us/library/dd358341.aspx func writeVarLen(w io.Writer, ti *typeInfo) (err error) { switch ti.TypeId { case typeDateN: ti.Writer = writeByteLenType case typeTimeN, typeDateTime2N, typeDateTimeOffsetN: if err = binary.Write(w, binary.LittleEndian, ti.Scale); err != nil { return } ti.Writer = writeByteLenType case 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 typeGuid: if !(ti.Size == 0x10 || ti.Size == 0x00) { panic("Invalid size for BYLELEN_TYPE") } if err = binary.Write(w, binary.LittleEndian, uint8(ti.Size)); 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: if err = binary.Write(w, binary.LittleEndian, ti.XmlInfo.SchemaPresent); err != nil { return } } case typeText, typeImage, typeNText, typeVariant: // LONGLEN_TYPE if err = binary.Write(w, binary.LittleEndian, uint32(ti.Size)); err != nil { return } if err = writeCollation(w, ti.Collation); err != nil { return } ti.Writer = writeLongLenType 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 encodeDateTim4(val time.Time) (buf []byte) { buf = make([]byte, 4) ref := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) dur := val.Sub(ref) days := dur / (24 * time.Hour) mins := val.Hour()*60 + val.Minute() if days < 0 { days = 0 mins = 0 } binary.LittleEndian.PutUint16(buf[:2], uint16(days)) binary.LittleEndian.PutUint16(buf[2:], uint16(mins)) return } // encodes datetime value // type identifier is typeDateTimeN func encodeDateTime(t time.Time) (res []byte) { // base date in days since Jan 1st 1900 basedays := gregorianDays(1900, 1) // days since Jan 1st 1900 (same TZ as t) days := gregorianDays(t.Year(), t.YearDay()) - basedays tm := 300*(t.Second()+t.Minute()*60+t.Hour()*60*60) + t.Nanosecond()*300/1e9 // minimum and maximum possible mindays := gregorianDays(1753, 1) - basedays maxdays := gregorianDays(9999, 365) - basedays if days < mindays { days = mindays tm = 0 } if days > maxdays { days = maxdays tm = (23*60*60+59*60+59)*300 + 299 } res = make([]byte, 8) binary.LittleEndian.PutUint32(res[0:4], uint32(days)) binary.LittleEndian.PutUint32(res[4:8], uint32(tm)) return } 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) 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 readByteLenType(ti *typeInfo, r *tdsBuffer) 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: %d", len(buf)) } 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 typeDateTim4: return decodeDateTim4(buf) case typeDateTime: return decodeDateTime(buf) 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(len(buf))) if err != nil { return } _, err = w.Write(buf) return } func readShortLenType(ti *typeInfo, r *tdsBuffer) 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) 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") } func writeLongLenType(w io.Writer, ti typeInfo, buf []byte) (err error) { //textptr err = binary.Write(w, binary.LittleEndian, byte(0x10)) if err != nil { return } err = binary.Write(w, binary.LittleEndian, uint64(0xFFFFFFFFFFFFFFFF)) if err != nil { return } err = binary.Write(w, binary.LittleEndian, uint64(0xFFFFFFFFFFFFFFFF)) if err != nil { return } //timestamp? err = binary.Write(w, binary.LittleEndian, uint64(0xFFFFFFFFFFFFFFFF)) if err != nil { return } err = binary.Write(w, binary.LittleEndian, uint32(ti.Size)) if err != nil { return } _, err = w.Write(buf) return } func readCollation(r *tdsBuffer) (res cp.Collation) { res.LcidAndFlags = r.uint32() res.SortId = r.byte() return } func writeCollation(w io.Writer, col cp.Collation) (err error) { if err = binary.Write(w, binary.LittleEndian, col.LcidAndFlags); err != nil { return } err = binary.Write(w, binary.LittleEndian, col.SortId) return } // reads variant value // http://msdn.microsoft.com/en-us/library/dd303302.aspx func readVariantType(ti *typeInfo, r *tdsBuffer) 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) interface{} { size := r.uint64() var buf *bytes.Buffer switch size { case _PLP_NULL: // null return nil case _UNKNOWN_PLP_LEN: // 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(_UNKNOWN_PLP_LEN)); err != nil { return } for { chunksize := uint32(len(buf)) if chunksize == 0 { err = binary.Write(w, binary.LittleEndian, uint32(_PLP_TERMINATOR)) return } if err = binary.Write(w, binary.LittleEndian, chunksize); err != nil { 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: ti.XmlInfo.SchemaPresent = r.byte() if ti.XmlInfo.SchemaPresent != 0 { // dbname ti.XmlInfo.DBName = r.BVarChar() // owning schema ti.XmlInfo.OwningSchema = r.BVarChar() // xml schema collection ti.XmlInfo.XmlSchemaCollection = r.UsVarChar() } ti.Reader = readPLPType case typeUdt: ti.Size = int(r.uint16()) ti.UdtInfo.DBName = r.BVarChar() ti.UdtInfo.SchemaName = r.BVarChar() ti.UdtInfo.TypeName = r.BVarChar() ti.UdtInfo.AssemblyQualifiedName = r.UsVarChar() ti.Buffer = make([]byte, ti.Size) ti.Reader = readPLPType case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar, typeNVarChar, typeNChar: // 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) { days = int(buf[0]) + int(buf[1])*256 + int(buf[2])*256*256 return } func decodeDate(buf []byte) time.Time { return time.Date(1, 1, 1+decodeDateInt(buf), 0, 0, 0, 0, time.UTC) } func encodeDate(val time.Time) (buf []byte) { days, _, _ := dateTime2(val) buf = make([]byte, 3) buf[0] = byte(days) buf[1] = byte(days >> 8) buf[2] = byte(days >> 16) return } 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 } // calculate size of time field in bytes func calcTimeSize(scale int) int { if scale <= 2 { return 3 } else if scale <= 4 { return 4 } else { return 5 } } // writes time value into a field buffer // buffer should be at least calcTimeSize long func encodeTimeInt(seconds, ns, scale int, buf []byte) { ns_total := int64(seconds)*1000*1000*1000 + int64(ns) t := ns_total / int64(math.Pow10(int(scale)*-1)*1e9) buf[0] = byte(t) buf[1] = byte(t >> 8) buf[2] = byte(t >> 16) buf[3] = byte(t >> 24) buf[4] = byte(t >> 32) } 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 encodeTime(hour, minute, second, ns, scale int) (buf []byte) { seconds := hour*3600 + minute*60 + second buf = make([]byte, calcTimeSize(scale)) encodeTimeInt(seconds, ns, scale, buf) return } 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 encodeDateTime2(val time.Time, scale int) (buf []byte) { days, seconds, ns := dateTime2(val) timesize := calcTimeSize(scale) buf = make([]byte, 3+timesize) encodeTimeInt(seconds, ns, scale, buf) buf[timesize] = byte(days) buf[timesize+1] = byte(days >> 8) buf[timesize+2] = byte(days >> 16) return } 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 encodeDateTimeOffset(val time.Time, scale int) (buf []byte) { timesize := calcTimeSize(scale) buf = make([]byte, timesize+2+3) days, seconds, ns := dateTime2(val.In(time.UTC)) encodeTimeInt(seconds, ns, scale, buf) buf[timesize] = byte(days) buf[timesize+1] = byte(days >> 8) buf[timesize+2] = byte(days >> 16) _, offset := val.Zone() offset /= 60 buf[timesize+3] = byte(offset) buf[timesize+4] = byte(offset >> 8) return } // returns days since Jan 1st 0001 in Gregorian calendar func gregorianDays(year, yearday int) int { year0 := year - 1 return year0*365 + year0/4 - year0/100 + year0/400 + yearday - 1 } func dateTime2(t time.Time) (days int, seconds int, ns int) { // days since Jan 1 1 (in same TZ as t) days = gregorianDays(t.Year(), t.YearDay()) seconds = t.Second() + t.Minute()*60 + t.Hour()*60*60 ns = t.Nanosecond() if days < 0 { days = 0 seconds = 0 ns = 0 } max := gregorianDays(9999, 365) if days > max { days = max seconds = 59 + 59*60 + 23*60*60 ns = 999999900 } return } func decodeChar(col cp.Collation, buf []byte) string { return cp.CharsetToUTF8(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) []byte { return buf } // makes go/sql type instance as described below // It should return // the value type that can be used to scan types into. For example, the database // column type "bigint" this should return "reflect.TypeOf(int64(0))". func makeGoLangScanType(ti typeInfo) reflect.Type { switch ti.TypeId { case typeInt1: return reflect.TypeOf(int64(0)) case typeInt2: return reflect.TypeOf(int64(0)) case typeInt4: return reflect.TypeOf(int64(0)) case typeInt8: return reflect.TypeOf(int64(0)) case typeFlt4: return reflect.TypeOf(float64(0)) case typeIntN: switch ti.Size { case 1: return reflect.TypeOf(int64(0)) case 2: return reflect.TypeOf(int64(0)) case 4: return reflect.TypeOf(int64(0)) case 8: return reflect.TypeOf(int64(0)) default: panic("invalid size of INTNTYPE") } case typeFlt8: return reflect.TypeOf(float64(0)) case typeFltN: switch ti.Size { case 4: return reflect.TypeOf(float64(0)) case 8: return reflect.TypeOf(float64(0)) default: panic("invalid size of FLNNTYPE") } case typeBigVarBin: return reflect.TypeOf([]byte{}) case typeVarChar: return reflect.TypeOf("") case typeNVarChar: return reflect.TypeOf("") case typeBit, typeBitN: return reflect.TypeOf(true) case typeDecimalN, typeNumericN: return reflect.TypeOf([]byte{}) case typeMoney, typeMoney4, typeMoneyN: switch ti.Size { case 4: return reflect.TypeOf([]byte{}) case 8: return reflect.TypeOf([]byte{}) default: panic("invalid size of MONEYN") } case typeDateTim4: return reflect.TypeOf(time.Time{}) case typeDateTime: return reflect.TypeOf(time.Time{}) case typeDateTimeN: switch ti.Size { case 4: return reflect.TypeOf(time.Time{}) case 8: return reflect.TypeOf(time.Time{}) default: panic("invalid size of DATETIMEN") } case typeDateTime2N: return reflect.TypeOf(time.Time{}) case typeDateN: return reflect.TypeOf(time.Time{}) case typeTimeN: return reflect.TypeOf(time.Time{}) case typeDateTimeOffsetN: return reflect.TypeOf(time.Time{}) case typeBigVarChar: return reflect.TypeOf("") case typeBigChar: return reflect.TypeOf("") case typeNChar: return reflect.TypeOf("") case typeGuid: return reflect.TypeOf([]byte{}) case typeXml: return reflect.TypeOf("") case typeText: return reflect.TypeOf("") case typeNText: return reflect.TypeOf("") case typeImage: return reflect.TypeOf([]byte{}) case typeBigBinary: return reflect.TypeOf([]byte{}) case typeVariant: return reflect.TypeOf(nil) default: panic(fmt.Sprintf("not implemented makeGoLangScanType for type %d", ti.TypeId)) } } func makeDecl(ti typeInfo) string { switch ti.TypeId { case typeNull: // maybe we should use something else here // this is tested in TestNull return "nvarchar(1)" case typeInt1: return "tinyint" case typeBigBinary: return fmt.Sprintf("binary(%d)", ti.Size) case typeInt2: return "smallint" case typeInt4: return "int" 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 typeDecimal, typeDecimalN: return fmt.Sprintf("decimal(%d, %d)", ti.Prec, ti.Scale) case typeNumeric, typeNumericN: return fmt.Sprintf("numeric(%d, %d)", ti.Prec, ti.Scale) case typeMoney4: return "smallmoney" case typeMoney: return "money" case typeMoneyN: switch ti.Size { case 4: return "smallmoney" case 8: return "money" default: panic("invalid size of MONEYNTYPE") } case typeBigVarBin: if ti.Size > 8000 || ti.Size == 0 { return "varbinary(max)" } else { return fmt.Sprintf("varbinary(%d)", ti.Size) } case typeNChar: return fmt.Sprintf("nchar(%d)", ti.Size/2) case typeBigChar, typeChar: return fmt.Sprintf("char(%d)", ti.Size) case typeBigVarChar, typeVarChar: if ti.Size > 4000 || ti.Size == 0 { return fmt.Sprintf("varchar(max)") } else { return fmt.Sprintf("varchar(%d)", ti.Size) } case typeNVarChar: if ti.Size > 8000 || ti.Size == 0 { return "nvarchar(max)" } else { return fmt.Sprintf("nvarchar(%d)", ti.Size/2) } case typeBit, typeBitN: return "bit" case typeDateN: return "date" case typeDateTim4: return "smalldatetime" case typeDateTime: return "datetime" case typeDateTimeN: switch ti.Size { case 4: return "smalldatetime" case 8: return "datetime" default: panic("invalid size of DATETIMNTYPE") } case typeTimeN: return "time" case typeDateTime2N: return fmt.Sprintf("datetime2(%d)", ti.Scale) case typeDateTimeOffsetN: return fmt.Sprintf("datetimeoffset(%d)", ti.Scale) case typeText: return "text" case typeNText: return "ntext" case typeUdt: return ti.UdtInfo.TypeName case typeGuid: return "uniqueidentifier" case typeTvp: if ti.UdtInfo.SchemaName != "" { return fmt.Sprintf("%s.%s READONLY", ti.UdtInfo.SchemaName, ti.UdtInfo.TypeName) } return fmt.Sprintf("%s READONLY", ti.UdtInfo.TypeName) default: panic(fmt.Sprintf("not implemented makeDecl for type %#x", ti.TypeId)) } } // makes go/sql type name as described below // RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return the // database system type name without the length. Type names should be uppercase. // Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT", // "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", "JSONB", "XML", // "TIMESTAMP". func makeGoLangTypeName(ti typeInfo) string { switch ti.TypeId { case typeInt1: return "TINYINT" case typeInt2: return "SMALLINT" case typeInt4: return "INT" 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: return "VARBINARY" case typeVarChar: return "VARCHAR" case typeNVarChar: return "NVARCHAR" case typeBit, typeBitN: return "BIT" case typeDecimalN, typeNumericN: return "DECIMAL" case typeMoney, typeMoney4, typeMoneyN: switch ti.Size { case 4: return "SMALLMONEY" case 8: return "MONEY" default: panic("invalid size of MONEYN") } case typeDateTim4: return "SMALLDATETIME" case typeDateTime: return "DATETIME" case typeDateTimeN: switch ti.Size { case 4: return "SMALLDATETIME" case 8: return "DATETIME" default: panic("invalid size of DATETIMEN") } case typeDateTime2N: return "DATETIME2" case typeDateN: return "DATE" case typeTimeN: return "TIME" case typeDateTimeOffsetN: return "DATETIMEOFFSET" case typeBigVarChar: return "VARCHAR" case typeBigChar: return "CHAR" case typeNChar: return "NCHAR" case typeGuid: return "UNIQUEIDENTIFIER" case typeXml: return "XML" case typeText: return "TEXT" case typeNText: return "NTEXT" case typeImage: return "IMAGE" case typeVariant: return "SQL_VARIANT" case typeBigBinary: return "BINARY" default: panic(fmt.Sprintf("not implemented makeGoLangTypeName for type %d", ti.TypeId)) } } // makes go/sql type length as described below // It should return the length // of the column type if the column is a variable length type. If the column is // not a variable length type ok should return false. // If length is not limited other than system limits, it should return math.MaxInt64. // The following are examples of returned values for various types: // TEXT (math.MaxInt64, true) // varchar(10) (10, true) // nvarchar(10) (10, true) // decimal (0, false) // int (0, false) // bytea(30) (30, true) func makeGoLangTypeLength(ti typeInfo) (int64, bool) { switch ti.TypeId { case typeInt1: return 0, false case typeInt2: return 0, false case typeInt4: return 0, false case typeInt8: return 0, false case typeFlt4: return 0, false case typeIntN: switch ti.Size { case 1: return 0, false case 2: return 0, false case 4: return 0, false case 8: return 0, false default: panic("invalid size of INTNTYPE") } case typeFlt8: return 0, false case typeFltN: switch ti.Size { case 4: return 0, false case 8: return 0, false default: panic("invalid size of FLNNTYPE") } case typeBit, typeBitN: return 0, false case typeDecimalN, typeNumericN: return 0, false case typeMoney, typeMoney4, typeMoneyN: switch ti.Size { case 4: return 0, false case 8: return 0, false default: panic("invalid size of MONEYN") } case typeDateTim4, typeDateTime: return 0, false case typeDateTimeN: switch ti.Size { case 4: return 0, false case 8: return 0, false default: panic("invalid size of DATETIMEN") } case typeDateTime2N: return 0, false case typeDateN: return 0, false case typeTimeN: return 0, false case typeDateTimeOffsetN: return 0, false case typeBigVarBin: if ti.Size == 0xffff { return 2147483645, true } else { return int64(ti.Size), true } case typeVarChar: return int64(ti.Size), true case typeBigVarChar: if ti.Size == 0xffff { return 2147483645, true } else { return int64(ti.Size), true } case typeBigChar: return int64(ti.Size), true case typeNVarChar: if ti.Size == 0xffff { return 2147483645 / 2, true } else { return int64(ti.Size) / 2, true } case typeNChar: return int64(ti.Size) / 2, true case typeGuid: return 0, false case typeXml: return 1073741822, true case typeText: return 2147483647, true case typeNText: return 1073741823, true case typeImage: return 2147483647, true case typeVariant: return 0, false case typeBigBinary: return 0, false default: panic(fmt.Sprintf("not implemented makeGoLangTypeLength for type %d", ti.TypeId)) } } // makes go/sql type precision and scale as described below // It should return the length // of the column type if the column is a variable length type. If the column is // not a variable length type ok should return false. // If length is not limited other than system limits, it should return math.MaxInt64. // The following are examples of returned values for various types: // TEXT (math.MaxInt64, true) // varchar(10) (10, true) // nvarchar(10) (10, true) // decimal (0, false) // int (0, false) // bytea(30) (30, true) func makeGoLangTypePrecisionScale(ti typeInfo) (int64, int64, bool) { switch ti.TypeId { case typeInt1: return 0, 0, false case typeInt2: return 0, 0, false case typeInt4: return 0, 0, false case typeInt8: return 0, 0, false case typeFlt4: return 0, 0, false case typeIntN: switch ti.Size { case 1: return 0, 0, false case 2: return 0, 0, false case 4: return 0, 0, false case 8: return 0, 0, false default: panic("invalid size of INTNTYPE") } case typeFlt8: return 0, 0, false case typeFltN: switch ti.Size { case 4: return 0, 0, false case 8: return 0, 0, false default: panic("invalid size of FLNNTYPE") } case typeBit, typeBitN: return 0, 0, false case typeDecimalN, typeNumericN: return int64(ti.Prec), int64(ti.Scale), true case typeMoney, typeMoney4, typeMoneyN: switch ti.Size { case 4: return 0, 0, false case 8: return 0, 0, false default: panic("invalid size of MONEYN") } case typeDateTim4, typeDateTime: return 0, 0, false case typeDateTimeN: switch ti.Size { case 4: return 0, 0, false case 8: return 0, 0, false default: panic("invalid size of DATETIMEN") } case typeDateTime2N: return 0, 0, false case typeDateN: return 0, 0, false case typeTimeN: return 0, 0, false case typeDateTimeOffsetN: return 0, 0, false case typeBigVarBin: return 0, 0, false case typeVarChar: return 0, 0, false case typeBigVarChar: return 0, 0, false case typeBigChar: return 0, 0, false case typeNVarChar: return 0, 0, false case typeNChar: return 0, 0, false case typeGuid: return 0, 0, false case typeXml: return 0, 0, false case typeText: return 0, 0, false case typeNText: return 0, 0, false case typeImage: return 0, 0, false case typeVariant: return 0, 0, false case typeBigBinary: return 0, 0, false default: panic(fmt.Sprintf("not implemented makeGoLangTypePrecisionScale for type %d", ti.TypeId)) } }