consul/vendor/github.com/SAP/go-hdb/internal/protocol/field.go

775 lines
17 KiB
Go

/*
Copyright 2014 SAP SE
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package protocol
import (
"database/sql/driver"
"fmt"
"math"
"sort"
"time"
"github.com/SAP/go-hdb/internal/bufio"
"github.com/SAP/go-hdb/internal/unicode/cesu8"
)
var test uint32
const (
realNullValue uint32 = ^uint32(0)
doubleNullValue uint64 = ^uint64(0)
)
const noFieldName uint32 = 0xFFFFFFFF
type uint32Slice []uint32
func (p uint32Slice) Len() int { return len(p) }
func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p uint32Slice) sort() { sort.Sort(p) }
type fieldNames map[uint32]string
func newFieldNames() fieldNames {
return make(map[uint32]string)
}
func (f fieldNames) addOffset(offset uint32) {
if offset != noFieldName {
f[offset] = ""
}
}
func (f fieldNames) name(offset uint32) string {
if name, ok := f[offset]; ok {
return name
}
return ""
}
func (f fieldNames) setName(offset uint32, name string) {
f[offset] = name
}
func (f fieldNames) sortOffsets() []uint32 {
offsets := make([]uint32, 0, len(f))
for k := range f {
offsets = append(offsets, k)
}
uint32Slice(offsets).sort()
return offsets
}
// FieldValues contains rows read from database.
type FieldValues struct {
rows int
cols int
values []driver.Value
}
func newFieldValues() *FieldValues {
return &FieldValues{}
}
func (f *FieldValues) String() string {
return fmt.Sprintf("rows %d columns %d", f.rows, f.cols)
}
func (f *FieldValues) resize(rows, cols int) {
f.rows, f.cols = rows, cols
f.values = make([]driver.Value, rows*cols)
}
// NumRow returns the number of rows available in FieldValues.
func (f *FieldValues) NumRow() int {
return f.rows
}
// Row fills the dest value slice with row data at index idx.
func (f *FieldValues) Row(idx int, dest []driver.Value) {
copy(dest, f.values[idx*f.cols:(idx+1)*f.cols])
}
const (
tinyintFieldSize = 1
smallintFieldSize = 2
intFieldSize = 4
bigintFieldSize = 8
realFieldSize = 4
doubleFieldSize = 8
dateFieldSize = 4
timeFieldSize = 4
timestampFieldSize = dateFieldSize + timeFieldSize
longdateFieldSize = 8
seconddateFieldSize = 8
daydateFieldSize = 4
secondtimeFieldSize = 4
decimalFieldSize = 16
lobInputDescriptorSize = 9
)
func fieldSize(tc TypeCode, arg driver.NamedValue) (int, error) {
v := arg.Value
if v == nil { //HDB bug: secondtime null value --> see writeField
return 0, nil
}
switch tc {
case tcTinyint:
return tinyintFieldSize, nil
case tcSmallint:
return smallintFieldSize, nil
case tcInteger:
return intFieldSize, nil
case tcBigint:
return bigintFieldSize, nil
case tcReal:
return realFieldSize, nil
case tcDouble:
return doubleFieldSize, nil
case tcDate:
return dateFieldSize, nil
case tcTime:
return timeFieldSize, nil
case tcTimestamp:
return timestampFieldSize, nil
case tcLongdate:
return longdateFieldSize, nil
case tcSeconddate:
return seconddateFieldSize, nil
case tcDaydate:
return daydateFieldSize, nil
case tcSecondtime:
return secondtimeFieldSize, nil
case tcDecimal:
return decimalFieldSize, nil
case tcChar, tcVarchar, tcString:
switch v := v.(type) {
case []byte:
return bytesSize(len(v))
case string:
return bytesSize(len(v))
default:
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
case tcNchar, tcNvarchar, tcNstring:
switch v := v.(type) {
case []byte:
return bytesSize(cesu8.Size(v))
case string:
return bytesSize(cesu8.StringSize(v))
default:
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
case tcBinary, tcVarbinary:
v, ok := v.([]byte)
if !ok {
outLogger.Fatalf("data type %s mismatch %T", tc, v)
}
return bytesSize(len(v))
case tcBlob, tcClob, tcNclob:
return lobInputDescriptorSize, nil
}
outLogger.Fatalf("data type %s not implemented", tc)
return 0, nil
}
func readField(session *Session, rd *bufio.Reader, tc TypeCode) (interface{}, error) {
switch tc {
case tcTinyint, tcSmallint, tcInteger, tcBigint:
if !rd.ReadBool() { //null value
return nil, nil
}
switch tc {
case tcTinyint:
return int64(rd.ReadB()), nil
case tcSmallint:
return int64(rd.ReadInt16()), nil
case tcInteger:
return int64(rd.ReadInt32()), nil
case tcBigint:
return rd.ReadInt64(), nil
}
case tcReal:
v := rd.ReadUint32()
if v == realNullValue {
return nil, nil
}
return float64(math.Float32frombits(v)), nil
case tcDouble:
v := rd.ReadUint64()
if v == doubleNullValue {
return nil, nil
}
return math.Float64frombits(v), nil
case tcDate:
year, month, day, null := readDate(rd)
if null {
return nil, nil
}
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC), nil
// time read gives only seconds (cut), no milliseconds
case tcTime:
hour, minute, nanosecs, null := readTime(rd)
if null {
return nil, nil
}
return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil
case tcTimestamp:
year, month, day, dateNull := readDate(rd)
hour, minute, nanosecs, timeNull := readTime(rd)
if dateNull || timeNull {
return nil, nil
}
return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), nil
case tcLongdate:
time, null := readLongdate(rd)
if null {
return nil, nil
}
return time, nil
case tcSeconddate:
time, null := readSeconddate(rd)
if null {
return nil, nil
}
return time, nil
case tcDaydate:
time, null := readDaydate(rd)
if null {
return nil, nil
}
return time, nil
case tcSecondtime:
time, null := readSecondtime(rd)
if null {
return nil, nil
}
return time, nil
case tcDecimal:
b, null := readDecimal(rd)
if null {
return nil, nil
}
return b, nil
case tcChar, tcVarchar:
value, null := readBytes(rd)
if null {
return nil, nil
}
return value, nil
case tcNchar, tcNvarchar:
value, null := readUtf8(rd)
if null {
return nil, nil
}
return value, nil
case tcBinary, tcVarbinary:
value, null := readBytes(rd)
if null {
return nil, nil
}
return value, nil
case tcBlob, tcClob, tcNclob:
null, writer, err := readLob(session, rd, tc)
if null {
return nil, nil
}
return writer, err
}
outLogger.Fatalf("read field: type code %s not implemented", tc)
return nil, nil
}
func writeField(wr *bufio.Writer, tc TypeCode, arg driver.NamedValue) error {
v := arg.Value
//HDB bug: secondtime null value cannot be set by setting high byte
// trying so, gives
// SQL HdbError 1033 - error while parsing protocol: no such data type: type_code=192, index=2
// null value
//if v == nil && tc != tcSecondtime
if v == nil {
wr.WriteB(byte(tc) | 0x80) //set high bit
return nil
}
// type code
wr.WriteB(byte(tc))
switch tc {
default:
outLogger.Fatalf("write field: type code %s not implemented", tc)
case tcTinyint, tcSmallint, tcInteger, tcBigint:
var i64 int64
switch v := v.(type) {
default:
return fmt.Errorf("invalid argument type %T", v)
case bool:
if v {
i64 = 1
} else {
i64 = 0
}
case int64:
i64 = v
}
switch tc {
case tcTinyint:
wr.WriteB(byte(i64))
case tcSmallint:
wr.WriteInt16(int16(i64))
case tcInteger:
wr.WriteInt32(int32(i64))
case tcBigint:
wr.WriteInt64(i64)
}
case tcReal:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
wr.WriteFloat32(float32(f64))
case tcDouble:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
wr.WriteFloat64(f64)
case tcDate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDate(wr, t)
case tcTime:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeTime(wr, t)
case tcTimestamp:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDate(wr, t)
writeTime(wr, t)
case tcLongdate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeLongdate(wr, t)
case tcSeconddate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeSeconddate(wr, t)
case tcDaydate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeDaydate(wr, t)
case tcSecondtime:
// HDB bug: write null value explicite
if v == nil {
wr.WriteInt32(86401)
return nil
}
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeSecondtime(wr, t)
case tcDecimal:
b, ok := v.([]byte)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
if len(b) != 16 {
return fmt.Errorf("invalid argument length %d of type %T - expected %d", len(b), v, 16)
}
wr.Write(b)
case tcChar, tcVarchar, tcString:
switch v := v.(type) {
case []byte:
writeBytes(wr, v)
case string:
writeString(wr, v)
default:
return fmt.Errorf("invalid argument type %T", v)
}
case tcNchar, tcNvarchar, tcNstring:
switch v := v.(type) {
case []byte:
writeUtf8Bytes(wr, v)
case string:
writeUtf8String(wr, v)
default:
return fmt.Errorf("invalid argument type %T", v)
}
case tcBinary, tcVarbinary:
v, ok := v.([]byte)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
writeBytes(wr, v)
case tcBlob, tcClob, tcNclob:
writeLob(wr)
}
return nil
}
// null values: most sig bit unset
// year: unset second most sig bit (subtract 2^15)
// --> read year as unsigned
// month is 0-based
// day is 1 byte
func readDate(rd *bufio.Reader) (int, time.Month, int, bool) {
year := rd.ReadUint16()
null := ((year & 0x8000) == 0) //null value
year &= 0x3fff
month := rd.ReadInt8()
month++
day := rd.ReadInt8()
return int(year), time.Month(month), int(day), null
}
// year: set most sig bit
// month 0 based
func writeDate(wr *bufio.Writer, t time.Time) {
//store in utc
utc := t.In(time.UTC)
year, month, day := utc.Date()
wr.WriteUint16(uint16(year) | 0x8000)
wr.WriteInt8(int8(month) - 1)
wr.WriteInt8(int8(day))
}
func readTime(rd *bufio.Reader) (int, int, int, bool) {
hour := rd.ReadB()
null := (hour & 0x80) == 0 //null value
hour &= 0x7f
minute := rd.ReadInt8()
millisecs := rd.ReadUint16()
nanosecs := int(millisecs) * 1000000
return int(hour), int(minute), nanosecs, null
}
func writeTime(wr *bufio.Writer, t time.Time) {
//store in utc
utc := t.UTC()
wr.WriteB(byte(utc.Hour()) | 0x80)
wr.WriteInt8(int8(utc.Minute()))
millisecs := utc.Second()*1000 + utc.Round(time.Millisecond).Nanosecond()/1000000
wr.WriteUint16(uint16(millisecs))
}
var zeroTime = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
func readLongdate(rd *bufio.Reader) (time.Time, bool) {
longdate := rd.ReadInt64()
if longdate == 3155380704000000001 { // null value
return zeroTime, true
}
return convertLongdateToTime(longdate), false
}
func writeLongdate(wr *bufio.Writer, t time.Time) {
wr.WriteInt64(convertTimeToLongdate(t))
}
func readSeconddate(rd *bufio.Reader) (time.Time, bool) {
seconddate := rd.ReadInt64()
if seconddate == 315538070401 { // null value
return zeroTime, true
}
return convertSeconddateToTime(seconddate), false
}
func writeSeconddate(wr *bufio.Writer, t time.Time) {
wr.WriteInt64(convertTimeToSeconddate(t))
}
func readDaydate(rd *bufio.Reader) (time.Time, bool) {
daydate := rd.ReadInt32()
if daydate == 3652062 { // null value
return zeroTime, true
}
return convertDaydateToTime(int64(daydate)), false
}
func writeDaydate(wr *bufio.Writer, t time.Time) {
wr.WriteInt32(int32(convertTimeToDayDate(t)))
}
func readSecondtime(rd *bufio.Reader) (time.Time, bool) {
secondtime := rd.ReadInt32()
if secondtime == 86401 { // null value
return zeroTime, true
}
return convertSecondtimeToTime(int(secondtime)), false
}
func writeSecondtime(wr *bufio.Writer, t time.Time) {
wr.WriteInt32(int32(convertTimeToSecondtime(t)))
}
// nanosecond: HDB - 7 digits precision (not 9 digits)
func convertTimeToLongdate(t time.Time) int64 {
t = t.UTC()
return (((((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60)+int64(t.Second()))*10000000 + int64(t.Nanosecond()/100) + 1
}
func convertLongdateToTime(longdate int64) time.Time {
const dayfactor = 10000000 * 24 * 60 * 60
longdate--
d := (longdate % dayfactor) * 100
t := convertDaydateToTime((longdate / dayfactor) + 1)
return t.Add(time.Duration(d))
}
func convertTimeToSeconddate(t time.Time) int64 {
t = t.UTC()
return (((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60 + int64(t.Second()) + 1
}
func convertSeconddateToTime(seconddate int64) time.Time {
const dayfactor = 24 * 60 * 60
seconddate--
d := (seconddate % dayfactor) * 1000000000
t := convertDaydateToTime((seconddate / dayfactor) + 1)
return t.Add(time.Duration(d))
}
const julianHdb = 1721423 // 1 January 0001 00:00:00 (1721424) - 1
func convertTimeToDayDate(t time.Time) int64 {
return int64(timeToJulianDay(t) - julianHdb)
}
func convertDaydateToTime(daydate int64) time.Time {
return julianDayToTime(int(daydate) + julianHdb)
}
func convertTimeToSecondtime(t time.Time) int {
t = t.UTC()
return (t.Hour()*60+t.Minute())*60 + t.Second() + 1
}
func convertSecondtimeToTime(secondtime int) time.Time {
return time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(int64(secondtime-1) * 1000000000))
}
func readDecimal(rd *bufio.Reader) ([]byte, bool) {
b := make([]byte, 16)
rd.ReadFull(b)
if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set)
return nil, true
}
return b, false
}
// string / binary length indicators
const (
bytesLenIndNullValue byte = 255
bytesLenIndSmall byte = 245
bytesLenIndMedium byte = 246
bytesLenIndBig byte = 247
)
func bytesSize(size int) (int, error) { //size + length indicator
switch {
default:
return 0, fmt.Errorf("max string length %d exceeded %d", math.MaxInt32, size)
case size <= int(bytesLenIndSmall):
return size + 1, nil
case size <= math.MaxInt16:
return size + 3, nil
case size <= math.MaxInt32:
return size + 5, nil
}
}
func readBytesSize(rd *bufio.Reader) (int, bool) {
ind := rd.ReadB() //length indicator
switch {
default:
return 0, false
case ind == bytesLenIndNullValue:
return 0, true
case ind <= bytesLenIndSmall:
return int(ind), false
case ind == bytesLenIndMedium:
return int(rd.ReadInt16()), false
case ind == bytesLenIndBig:
return int(rd.ReadInt32()), false
}
}
func writeBytesSize(wr *bufio.Writer, size int) error {
switch {
default:
return fmt.Errorf("max argument length %d of string exceeded", size)
case size <= int(bytesLenIndSmall):
wr.WriteB(byte(size))
case size <= math.MaxInt16:
wr.WriteB(bytesLenIndMedium)
wr.WriteInt16(int16(size))
case size <= math.MaxInt32:
wr.WriteB(bytesLenIndBig)
wr.WriteInt32(int32(size))
}
return nil
}
func readBytes(rd *bufio.Reader) ([]byte, bool) {
size, null := readBytesSize(rd)
if null {
return nil, true
}
b := make([]byte, size)
rd.ReadFull(b)
return b, false
}
func readUtf8(rd *bufio.Reader) ([]byte, bool) {
size, null := readBytesSize(rd)
if null {
return nil, true
}
b := rd.ReadCesu8(size)
return b, false
}
// strings with one byte length
func readShortUtf8(rd *bufio.Reader) ([]byte, int) {
size := rd.ReadB()
b := rd.ReadCesu8(int(size))
return b, int(size)
}
func writeBytes(wr *bufio.Writer, b []byte) {
writeBytesSize(wr, len(b))
wr.Write(b)
}
func writeString(wr *bufio.Writer, s string) {
writeBytesSize(wr, len(s))
wr.WriteString(s)
}
func writeUtf8Bytes(wr *bufio.Writer, b []byte) {
size := cesu8.Size(b)
writeBytesSize(wr, size)
wr.WriteCesu8(b)
}
func writeUtf8String(wr *bufio.Writer, s string) {
size := cesu8.StringSize(s)
writeBytesSize(wr, size)
wr.WriteStringCesu8(s)
}
func readLob(s *Session, rd *bufio.Reader, tc TypeCode) (bool, lobChunkWriter, error) {
rd.ReadInt8() // type code (is int here)
opt := rd.ReadInt8()
null := (lobOptions(opt) & loNullindicator) != 0
if null {
return true, nil, nil
}
eof := (lobOptions(opt) & loLastdata) != 0
rd.Skip(2)
charLen := rd.ReadInt64()
byteLen := rd.ReadInt64()
id := rd.ReadUint64()
chunkLen := rd.ReadInt32()
lobChunkWriter := newLobChunkWriter(tc.isCharBased(), s, locatorID(id), charLen, byteLen)
if err := lobChunkWriter.write(rd, int(chunkLen), eof); err != nil {
return null, lobChunkWriter, err
}
return null, lobChunkWriter, nil
}
// TODO: first write: add content? - actually no data transferred
func writeLob(wr *bufio.Writer) {
wr.WriteB(0)
wr.WriteInt32(0)
wr.WriteInt32(0)
}