Jeff Mitchell 43493f2767 Bump deps
2018-01-26 18:51:00 -05:00

1181 lines
25 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"
)
const (
realNullValue uint32 = ^uint32(0)
doubleNullValue uint64 = ^uint64(0)
)
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 field interface {
typeCode() typeCode
typeLength() (int64, bool)
typePrecisionScale() (int64, int64, bool)
nullable() bool
in() bool
out() bool
name(map[uint32]string) string
nameOffsets() []uint32
String() string
}
// FieldSet contains database field metadata.
type FieldSet struct {
fields []field
names map[uint32]string
}
func newFieldSet(size int) *FieldSet {
return &FieldSet{
fields: make([]field, size),
names: make(map[uint32]string),
}
}
// String implements the Stringer interface.
func (f *FieldSet) String() string {
a := make([]string, len(f.fields))
for i, f := range f.fields {
a[i] = f.String()
}
return fmt.Sprintf("%v", a)
}
func (f *FieldSet) nameOffsets() []uint32 {
for _, field := range f.fields {
for _, offset := range field.nameOffsets() {
if offset != 0xFFFFFFFF {
f.names[offset] = ""
}
}
}
// sort offsets (not sure if offsets are monotonically increasing in any case)
offsets := make([]uint32, len(f.names))
i := 0
for offset := range f.names {
offsets[i] = offset
i++
}
uint32Slice(offsets).sort()
return offsets
}
// NumInputField returns the number of input fields in a database statement.
func (f *FieldSet) NumInputField() int {
cnt := 0
for _, field := range f.fields {
if field.in() {
cnt++
}
}
return cnt
}
// NumOutputField returns the number of output fields of a query or stored procedure.
func (f *FieldSet) NumOutputField() int {
cnt := 0
for _, field := range f.fields {
if field.out() {
cnt++
}
}
return cnt
}
// DataType returns the datatype of the field at index idx.
func (f *FieldSet) DataType(idx int) DataType {
return f.fields[idx].typeCode().dataType()
}
// DatabaseTypeName returns the type name of the field at index idx.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName
func (f *FieldSet) DatabaseTypeName(idx int) string {
return f.fields[idx].typeCode().typeName()
}
// TypeLength returns the type length of the field at index idx.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength
func (f *FieldSet) TypeLength(idx int) (int64, bool) {
return f.fields[idx].typeLength()
}
// TypePrecisionScale returns the type precision and scale (decimal types) of the field at index idx.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale
func (f *FieldSet) TypePrecisionScale(idx int) (int64, int64, bool) {
return f.fields[idx].typePrecisionScale()
}
// TypeNullable returns true if the column at index idx may be null, false otherwise.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable
func (f *FieldSet) TypeNullable(idx int) bool {
return f.fields[idx].nullable()
}
// OutputNames fills the names parameter with field names of all output fields. The size of the names slice must be at least
// NumOutputField big.
func (f *FieldSet) OutputNames(names []string) error {
i := 0
for _, field := range f.fields {
if field.out() {
if i >= len(names) { // assert names size
return fmt.Errorf("names size too short %d - expected min %d", len(names), i)
}
names[i] = field.name(f.names)
i++
}
}
return nil
}
// FieldValues contains rows read from database.
type FieldValues struct {
s *Session
rows int
cols int
lobCols int
values []driver.Value
descrs []*LobReadDescr // Caution: store descriptor to guarantee valid addresses
writers []lobWriter
}
func newFieldValues(s *Session) *FieldValues {
return &FieldValues{s: s}
}
func (f *FieldValues) String() string {
return fmt.Sprintf("rows %d columns %d lob columns %d", f.rows, f.cols, f.lobCols)
}
func (f *FieldValues) read(rows int, fieldSet *FieldSet, rd *bufio.Reader) error {
f.rows = rows
f.descrs = make([]*LobReadDescr, 0)
f.cols, f.lobCols = 0, 0
for _, field := range fieldSet.fields {
if field.out() {
if field.typeCode().isLob() {
f.descrs = append(f.descrs, &LobReadDescr{col: f.cols})
f.lobCols++
}
f.cols++
}
}
f.values = make([]driver.Value, f.rows*f.cols)
f.writers = make([]lobWriter, f.lobCols)
for i := 0; i < f.rows; i++ {
j := 0
for _, field := range fieldSet.fields {
if !field.out() {
continue
}
var err error
if f.values[i*f.cols+j], err = readField(rd, field.typeCode()); err != nil {
return err
}
j++
}
}
return nil
}
// 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])
if f.lobCols == 0 {
return
}
for i, descr := range f.descrs {
col := descr.col
writer := dest[col].(lobWriter)
f.writers[i] = writer
descr.w = writer
dest[col] = lobReadDescrToPointer(descr)
}
// last descriptor triggers lob read
f.descrs[f.lobCols-1].fn = func() error {
return f.s.readLobStream(f.writers)
}
}
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, v driver.Value) (int, error) {
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 tcNlocator, tcBlob, tcClob, tcNclob:
return lobInputDescriptorSize, nil
}
outLogger.Fatalf("data type %s not implemented", tc)
return 0, nil
}
func readField(rd *bufio.Reader, tc typeCode) (interface{}, error) {
switch tc {
case tcTinyint, tcSmallint, tcInteger, tcBigint:
valid, err := rd.ReadBool()
if err != nil {
return nil, err
}
if !valid { //null value
return nil, nil
}
switch tc {
case tcTinyint:
if v, err := rd.ReadByte(); err == nil {
return int64(v), nil
}
return nil, err
case tcSmallint:
if v, err := rd.ReadInt16(); err == nil {
return int64(v), nil
}
return nil, err
case tcInteger:
if v, err := rd.ReadInt32(); err == nil {
return int64(v), nil
}
return nil, err
case tcBigint:
if v, err := rd.ReadInt64(); err == nil {
return v, nil
}
return nil, err
}
case tcReal:
v, err := rd.ReadUint32()
if err != nil {
return nil, err
}
if v == realNullValue {
return nil, nil
}
return float64(math.Float32frombits(v)), nil
case tcDouble:
v, err := rd.ReadUint64()
if err != nil {
return nil, err
}
if v == doubleNullValue {
return nil, nil
}
return math.Float64frombits(v), nil
case tcDate:
year, month, day, null, err := readDate(rd)
if err != nil {
return nil, err
}
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, err := readTime(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil
case tcTimestamp:
year, month, day, dateNull, err := readDate(rd)
if err != nil {
return nil, err
}
hour, minute, nanosecs, timeNull, err := readTime(rd)
if err != nil {
return nil, err
}
if dateNull || timeNull {
return nil, nil
}
return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), nil
case tcLongdate:
time, null, err := readLongdate(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return time, nil
case tcSeconddate:
time, null, err := readSeconddate(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return time, nil
case tcDaydate:
time, null, err := readDaydate(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return time, nil
case tcSecondtime:
time, null, err := readSecondtime(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return time, nil
case tcDecimal:
b, null, err := readDecimal(rd)
switch {
case err != nil:
return nil, err
case null:
return nil, nil
default:
return b, nil
}
case tcChar, tcVarchar:
value, null, err := readBytes(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return value, nil
case tcNchar, tcNvarchar:
value, null, err := readUtf8(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return value, nil
case tcBinary, tcVarbinary:
value, null, err := readBytes(rd)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return value, nil
case tcBlob, tcClob, tcNclob:
null, writer, err := readLob(rd, tc)
if err != nil {
return nil, err
}
if null {
return nil, nil
}
return writer, nil
}
outLogger.Fatalf("read field: type code %s not implemented", tc)
return nil, nil
}
func writeField(wr *bufio.Writer, tc typeCode, v driver.Value) error {
//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 {
if err := wr.WriteByte(byte(tc) | 0x80); err != nil { //set high bit
return err
}
return nil
}
// type code
if err := wr.WriteByte(byte(tc)); err != nil {
return err
}
switch 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:
return wr.WriteByte(byte(i64))
case tcSmallint:
return wr.WriteInt16(int16(i64))
case tcInteger:
return wr.WriteInt32(int32(i64))
case tcBigint:
return wr.WriteInt64(i64)
}
case tcReal:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return wr.WriteFloat32(float32(f64))
case tcDouble:
f64, ok := v.(float64)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return wr.WriteFloat64(f64)
case tcDate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return writeDate(wr, t)
case tcTime:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return writeTime(wr, t)
case tcTimestamp:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
if err := writeDate(wr, t); err != nil {
return err
}
return writeTime(wr, t)
case tcLongdate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return writeLongdate(wr, t)
case tcSeconddate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return writeSeconddate(wr, t)
case tcDaydate:
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return writeDaydate(wr, t)
case tcSecondtime:
// HDB bug: write null value explicite
if v == nil {
return wr.WriteInt32(86401)
}
t, ok := v.(time.Time)
if !ok {
return fmt.Errorf("invalid argument type %T", v)
}
return 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)
}
_, err := wr.Write(b)
return err
case tcChar, tcVarchar, tcString:
switch v := v.(type) {
case []byte:
return writeBytes(wr, v)
case string:
return writeString(wr, v)
default:
return fmt.Errorf("invalid argument type %T", v)
}
case tcNchar, tcNvarchar, tcNstring:
switch v := v.(type) {
case []byte:
return writeUtf8Bytes(wr, v)
case string:
return 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)
}
return writeBytes(wr, v)
case tcNlocator, tcBlob, tcClob, tcNclob:
return writeLob(wr)
}
outLogger.Fatalf("write field: type code %s not implemented", tc)
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, error) {
year, err := rd.ReadUint16()
if err != nil {
return 0, 0, 0, false, err
}
if (year & 0x8000) == 0 { //null value
if err := rd.Skip(2); err != nil {
return 0, 0, 0, false, err
}
return 0, 0, 0, true, nil
}
year &= 0x3fff
month, err := rd.ReadInt8()
if err != nil {
return 0, 0, 0, false, err
}
month++
day, err := rd.ReadInt8()
if err != nil {
return 0, 0, 0, false, err
}
return int(year), time.Month(month), int(day), false, nil
}
// year: set most sig bit
// month 0 based
func writeDate(wr *bufio.Writer, t time.Time) error {
//store in utc
utc := t.In(time.UTC)
year, month, day := utc.Date()
if err := wr.WriteUint16(uint16(year) | 0x8000); err != nil {
return err
}
if err := wr.WriteInt8(int8(month) - 1); err != nil {
return err
}
if err := wr.WriteInt8(int8(day)); err != nil {
return err
}
return nil
}
func readTime(rd *bufio.Reader) (int, int, int, bool, error) {
hour, err := rd.ReadByte()
if err != nil {
return 0, 0, 0, false, err
}
if (hour & 0x80) == 0 { //null value
if err := rd.Skip(3); err != nil {
return 0, 0, 0, false, err
}
return 0, 0, 0, true, nil
}
hour &= 0x7f
minute, err := rd.ReadInt8()
if err != nil {
return 0, 0, 0, false, err
}
millisecs, err := rd.ReadUint16()
if err != nil {
return 0, 0, 0, false, err
}
nanosecs := int(millisecs) * 1000000
return int(hour), int(minute), nanosecs, false, nil
}
func writeTime(wr *bufio.Writer, t time.Time) error {
//store in utc
utc := t.UTC()
if err := wr.WriteByte(byte(utc.Hour()) | 0x80); err != nil {
return err
}
if err := wr.WriteInt8(int8(utc.Minute())); err != nil {
return err
}
millisecs := utc.Second()*1000 + utc.Round(time.Millisecond).Nanosecond()/1000000
if err := wr.WriteUint16(uint16(millisecs)); err != nil {
return err
}
return nil
}
var zeroTime = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
func readLongdate(rd *bufio.Reader) (time.Time, bool, error) {
longdate, err := rd.ReadInt64()
if err != nil {
return zeroTime, false, err
}
if longdate == 3155380704000000001 { // null value
return zeroTime, true, nil
}
return convertLongdateToTime(longdate), false, nil
}
func writeLongdate(wr *bufio.Writer, t time.Time) error {
if err := wr.WriteInt64(convertTimeToLongdate(t)); err != nil {
return err
}
return nil
}
func readSeconddate(rd *bufio.Reader) (time.Time, bool, error) {
seconddate, err := rd.ReadInt64()
if err != nil {
return zeroTime, false, err
}
if seconddate == 315538070401 { // null value
return zeroTime, true, nil
}
return convertSeconddateToTime(seconddate), false, nil
}
func writeSeconddate(wr *bufio.Writer, t time.Time) error {
if err := wr.WriteInt64(convertTimeToSeconddate(t)); err != nil {
return err
}
return nil
}
func readDaydate(rd *bufio.Reader) (time.Time, bool, error) {
daydate, err := rd.ReadInt32()
if err != nil {
return zeroTime, false, err
}
if daydate == 3652062 { // null value
return zeroTime, true, nil
}
return convertDaydateToTime(int64(daydate)), false, nil
}
func writeDaydate(wr *bufio.Writer, t time.Time) error {
if err := wr.WriteInt32(int32(convertTimeToDayDate(t))); err != nil {
return err
}
return nil
}
func readSecondtime(rd *bufio.Reader) (time.Time, bool, error) {
secondtime, err := rd.ReadInt32()
if err != nil {
return zeroTime, false, err
}
if secondtime == 86401 { // null value
return zeroTime, true, nil
}
return convertSecondtimeToTime(int(secondtime)), false, nil
}
func writeSecondtime(wr *bufio.Writer, t time.Time) error {
if err := wr.WriteInt32(int32(convertTimeToSecondtime(t))); err != nil {
return err
}
return nil
}
// 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, error) {
b := make([]byte, 16)
if err := rd.ReadFull(b); err != nil {
return nil, false, err
}
if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set)
return nil, true, nil
}
return b, false, nil
}
// 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, error) {
ind, err := rd.ReadByte() //length indicator
if err != nil {
return 0, false, err
}
switch {
default:
return 0, false, fmt.Errorf("invalid length indicator %d", ind)
case ind == bytesLenIndNullValue:
return 0, true, nil
case ind <= bytesLenIndSmall:
return int(ind), false, nil
case ind == bytesLenIndMedium:
if size, err := rd.ReadInt16(); err == nil {
return int(size), false, nil
}
return 0, false, err
case ind == bytesLenIndBig:
if size, err := rd.ReadInt32(); err == nil {
return int(size), false, nil
}
return 0, false, err
}
}
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):
if err := wr.WriteByte(byte(size)); err != nil {
return err
}
case size <= math.MaxInt16:
if err := wr.WriteByte(bytesLenIndMedium); err != nil {
return err
}
if err := wr.WriteInt16(int16(size)); err != nil {
return err
}
case size <= math.MaxInt32:
if err := wr.WriteByte(bytesLenIndBig); err != nil {
return err
}
if err := wr.WriteInt32(int32(size)); err != nil {
return err
}
}
return nil
}
func readBytes(rd *bufio.Reader) ([]byte, bool, error) {
size, null, err := readBytesSize(rd)
if err != nil {
return nil, false, err
}
if null {
return nil, true, nil
}
b := make([]byte, size)
if err := rd.ReadFull(b); err != nil {
return nil, false, err
}
return b, false, nil
}
func readUtf8(rd *bufio.Reader) ([]byte, bool, error) {
size, null, err := readBytesSize(rd)
if err != nil {
return nil, false, err
}
if null {
return nil, true, nil
}
b, err := rd.ReadCesu8(size)
if err != nil {
return nil, false, err
}
return b, false, nil
}
// strings with one byte length
func readShortUtf8(rd *bufio.Reader) ([]byte, int, error) {
size, err := rd.ReadByte()
if err != nil {
return nil, 0, err
}
b, err := rd.ReadCesu8(int(size))
if err != nil {
return nil, 0, err
}
return b, int(size), nil
}
func writeBytes(wr *bufio.Writer, b []byte) error {
if err := writeBytesSize(wr, len(b)); err != nil {
return err
}
_, err := wr.Write(b)
return err
}
func writeString(wr *bufio.Writer, s string) error {
if err := writeBytesSize(wr, len(s)); err != nil {
return err
}
_, err := wr.WriteString(s)
return err
}
func writeUtf8Bytes(wr *bufio.Writer, b []byte) error {
size := cesu8.Size(b)
if err := writeBytesSize(wr, size); err != nil {
return err
}
_, err := wr.WriteCesu8(b)
return err
}
func writeUtf8String(wr *bufio.Writer, s string) error {
size := cesu8.StringSize(s)
if err := writeBytesSize(wr, size); err != nil {
return err
}
_, err := wr.WriteStringCesu8(s)
return err
}
func readLob(rd *bufio.Reader, tc typeCode) (bool, lobWriter, error) {
if _, err := rd.ReadInt8(); err != nil { // type code (is int here)
return false, nil, err
}
opt, err := rd.ReadInt8()
if err != nil {
return false, nil, err
}
if err := rd.Skip(2); err != nil {
return false, nil, err
}
charLen, err := rd.ReadInt64()
if err != nil {
return false, nil, err
}
byteLen, err := rd.ReadInt64()
if err != nil {
return false, nil, err
}
id, err := rd.ReadUint64()
if err != nil {
return false, nil, err
}
chunkLen, err := rd.ReadInt32()
if err != nil {
return false, nil, err
}
null := (lobOptions(opt) & loNullindicator) != 0
eof := (lobOptions(opt) & loLastdata) != 0
var writer lobWriter
if tc.isCharBased() {
writer = newCharLobWriter(locatorID(id), charLen, byteLen)
} else {
writer = newBinaryLobWriter(locatorID(id), charLen, byteLen)
}
if err := writer.write(rd, int(chunkLen), eof); err != nil {
return null, writer, err
}
return null, writer, nil
}
// TODO: first write: add content? - actually no data transferred
func writeLob(wr *bufio.Writer) error {
if err := wr.WriteByte(0); err != nil {
return err
}
if err := wr.WriteInt32(0); err != nil {
return err
}
if err := wr.WriteInt32(0); err != nil {
return err
}
return nil
}