296 lines
5.6 KiB
Go
296 lines
5.6 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 (
|
|
"fmt"
|
|
|
|
"github.com/SAP/go-hdb/internal/bufio"
|
|
)
|
|
|
|
const (
|
|
resultsetIDSize = 8
|
|
)
|
|
|
|
type columnOptions int8
|
|
|
|
const (
|
|
coMandatory columnOptions = 0x01
|
|
coOptional columnOptions = 0x02
|
|
)
|
|
|
|
var columnOptionsText = map[columnOptions]string{
|
|
coMandatory: "mandatory",
|
|
coOptional: "optional",
|
|
}
|
|
|
|
func (k columnOptions) String() string {
|
|
t := make([]string, 0, len(columnOptionsText))
|
|
|
|
for option, text := range columnOptionsText {
|
|
if (k & option) != 0 {
|
|
t = append(t, text)
|
|
}
|
|
}
|
|
return fmt.Sprintf("%v", t)
|
|
}
|
|
|
|
//resultset id
|
|
type resultsetID struct {
|
|
id *uint64
|
|
}
|
|
|
|
func (id *resultsetID) kind() partKind {
|
|
return pkResultsetID
|
|
}
|
|
|
|
func (id *resultsetID) size() (int, error) {
|
|
return resultsetIDSize, nil
|
|
}
|
|
|
|
func (id *resultsetID) numArg() int {
|
|
return 1
|
|
}
|
|
|
|
func (id *resultsetID) setNumArg(int) {
|
|
//ignore - always 1
|
|
}
|
|
|
|
func (id *resultsetID) read(rd *bufio.Reader) error {
|
|
|
|
_id, err := rd.ReadUint64()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*id.id = _id
|
|
|
|
if trace {
|
|
outLogger.Printf("resultset id: %d", *id.id)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (id *resultsetID) write(wr *bufio.Writer) error {
|
|
|
|
if err := wr.WriteUint64(*id.id); err != nil {
|
|
return err
|
|
}
|
|
if trace {
|
|
outLogger.Printf("resultset id: %d", *id.id)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
resultTableName = iota // used as index: start with 0
|
|
resultSchemaName
|
|
resultColumnName
|
|
resultColumnDisplayName
|
|
maxResultNames
|
|
)
|
|
|
|
type resultField struct {
|
|
columnOptions columnOptions
|
|
tc typeCode
|
|
fraction int16
|
|
length int16
|
|
tablenameOffset uint32
|
|
schemanameOffset uint32
|
|
columnnameOffset uint32
|
|
columnDisplaynameOffset uint32
|
|
}
|
|
|
|
func newResultField() *resultField {
|
|
return &resultField{}
|
|
}
|
|
|
|
func (f *resultField) String() string {
|
|
return fmt.Sprintf("columnsOptions %s typeCode %s fraction %d length %d tablenameOffset %d schemanameOffset %d columnnameOffset %d columnDisplaynameOffset %d",
|
|
f.columnOptions,
|
|
f.tc,
|
|
f.fraction,
|
|
f.length,
|
|
f.tablenameOffset,
|
|
f.schemanameOffset,
|
|
f.columnnameOffset,
|
|
f.columnDisplaynameOffset,
|
|
)
|
|
}
|
|
|
|
// Field interface
|
|
func (f *resultField) typeCode() typeCode {
|
|
return f.tc
|
|
}
|
|
|
|
func (f *resultField) typeLength() (int64, bool) {
|
|
if f.tc.isVariableLength() {
|
|
return int64(f.length), true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (f *resultField) typePrecisionScale() (int64, int64, bool) {
|
|
if f.tc.isDecimalType() {
|
|
return int64(f.length), int64(f.fraction), true
|
|
}
|
|
return 0, 0, false
|
|
}
|
|
|
|
func (f *resultField) nullable() bool {
|
|
return f.columnOptions == coOptional
|
|
}
|
|
|
|
func (f *resultField) in() bool {
|
|
return false
|
|
}
|
|
|
|
func (f *resultField) out() bool {
|
|
return true
|
|
}
|
|
|
|
func (f *resultField) name(names map[uint32]string) string {
|
|
return names[f.columnDisplaynameOffset]
|
|
}
|
|
|
|
func (f *resultField) nameOffsets() []uint32 {
|
|
return []uint32{f.tablenameOffset, f.schemanameOffset, f.columnnameOffset, f.columnDisplaynameOffset}
|
|
}
|
|
|
|
//
|
|
|
|
func (f *resultField) read(rd *bufio.Reader) error {
|
|
var err error
|
|
|
|
if co, err := rd.ReadInt8(); err == nil {
|
|
f.columnOptions = columnOptions(co)
|
|
} else {
|
|
return err
|
|
}
|
|
if tc, err := rd.ReadInt8(); err == nil {
|
|
f.tc = typeCode(tc)
|
|
} else {
|
|
return err
|
|
}
|
|
if f.fraction, err = rd.ReadInt16(); err != nil {
|
|
return err
|
|
}
|
|
if f.length, err = rd.ReadInt16(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := rd.Skip(2); err != nil { //filler
|
|
return err
|
|
}
|
|
|
|
if f.tablenameOffset, err = rd.ReadUint32(); err != nil {
|
|
return err
|
|
}
|
|
if f.schemanameOffset, err = rd.ReadUint32(); err != nil {
|
|
return err
|
|
}
|
|
if f.columnnameOffset, err = rd.ReadUint32(); err != nil {
|
|
return err
|
|
}
|
|
if f.columnDisplaynameOffset, err = rd.ReadUint32(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//resultset metadata
|
|
type resultMetadata struct {
|
|
fieldSet *FieldSet
|
|
numArg int
|
|
}
|
|
|
|
func (r *resultMetadata) String() string {
|
|
return fmt.Sprintf("result metadata: %s", r.fieldSet.fields)
|
|
}
|
|
|
|
func (r *resultMetadata) kind() partKind {
|
|
return pkResultMetadata
|
|
}
|
|
|
|
func (r *resultMetadata) setNumArg(numArg int) {
|
|
r.numArg = numArg
|
|
}
|
|
|
|
func (r *resultMetadata) read(rd *bufio.Reader) error {
|
|
|
|
for i := 0; i < r.numArg; i++ {
|
|
field := newResultField()
|
|
if err := field.read(rd); err != nil {
|
|
return err
|
|
}
|
|
r.fieldSet.fields[i] = field
|
|
}
|
|
|
|
pos := uint32(0)
|
|
for _, offset := range r.fieldSet.nameOffsets() {
|
|
if diff := int(offset - pos); diff > 0 {
|
|
rd.Skip(diff)
|
|
}
|
|
|
|
b, size, err := readShortUtf8(rd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.fieldSet.names[offset] = string(b)
|
|
|
|
pos += uint32(1 + size)
|
|
}
|
|
|
|
if trace {
|
|
outLogger.Printf("read %s", r)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//resultset
|
|
type resultset struct {
|
|
numArg int
|
|
fieldSet *FieldSet
|
|
fieldValues *FieldValues
|
|
}
|
|
|
|
func (r *resultset) String() string {
|
|
return fmt.Sprintf("resultset: %s", r.fieldValues)
|
|
}
|
|
|
|
func (r *resultset) kind() partKind {
|
|
return pkResultset
|
|
}
|
|
|
|
func (r *resultset) setNumArg(numArg int) {
|
|
r.numArg = numArg
|
|
}
|
|
|
|
func (r *resultset) read(rd *bufio.Reader) error {
|
|
if err := r.fieldValues.read(r.numArg, r.fieldSet, rd); err != nil {
|
|
return err
|
|
}
|
|
if trace {
|
|
outLogger.Printf("read %s", r)
|
|
}
|
|
return nil
|
|
}
|