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

390 lines
8.8 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"
"io"
"github.com/SAP/go-hdb/internal/bufio"
)
type parameterOptions int8
const (
poMandatory parameterOptions = 0x01
poOptional parameterOptions = 0x02
poDefault parameterOptions = 0x04
)
var parameterOptionsText = map[parameterOptions]string{
poMandatory: "mandatory",
poOptional: "optional",
poDefault: "default",
}
func (k parameterOptions) String() string {
t := make([]string, 0, len(parameterOptionsText))
for option, text := range parameterOptionsText {
if (k & option) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
type parameterMode int8
const (
pmIn parameterMode = 0x01
pmInout parameterMode = 0x02
pmOut parameterMode = 0x04
)
var parameterModeText = map[parameterMode]string{
pmIn: "in",
pmInout: "inout",
pmOut: "out",
}
func (k parameterMode) String() string {
t := make([]string, 0, len(parameterModeText))
for mode, text := range parameterModeText {
if (k & mode) != 0 {
t = append(t, text)
}
}
return fmt.Sprintf("%v", t)
}
// ParameterFieldSet contains database field metadata for parameters.
type ParameterFieldSet struct {
fields []*ParameterField
_inputFields []*ParameterField
_outputFields []*ParameterField
names fieldNames
}
func newParameterFieldSet(size int) *ParameterFieldSet {
return &ParameterFieldSet{
fields: make([]*ParameterField, size),
_inputFields: make([]*ParameterField, 0, size),
_outputFields: make([]*ParameterField, 0, size),
names: newFieldNames(),
}
}
// String implements the Stringer interface.
func (f *ParameterFieldSet) 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 *ParameterFieldSet) read(rd *bufio.Reader) {
for i := 0; i < len(f.fields); i++ {
field := newParameterField(f.names)
field.read(rd)
f.fields[i] = field
if field.In() {
f._inputFields = append(f._inputFields, field)
}
if field.Out() {
f._outputFields = append(f._outputFields, field)
}
}
pos := uint32(0)
for _, offset := range f.names.sortOffsets() {
if diff := int(offset - pos); diff > 0 {
rd.Skip(diff)
}
b, size := readShortUtf8(rd)
f.names.setName(offset, string(b))
pos += uint32(1 + size)
}
}
func (f *ParameterFieldSet) inputFields() []*ParameterField {
return f._inputFields
}
func (f *ParameterFieldSet) outputFields() []*ParameterField {
return f._outputFields
}
// NumInputField returns the number of input fields in a database statement.
func (f *ParameterFieldSet) NumInputField() int {
return len(f._inputFields)
}
// NumOutputField returns the number of output fields of a query or stored procedure.
func (f *ParameterFieldSet) NumOutputField() int {
return len(f._outputFields)
}
// Field returns the field at index idx.
func (f *ParameterFieldSet) Field(idx int) *ParameterField {
return f.fields[idx]
}
// OutputField returns the output field at index idx.
func (f *ParameterFieldSet) OutputField(idx int) *ParameterField {
return f._outputFields[idx]
}
// ParameterField contains database field attributes for parameters.
type ParameterField struct {
fieldNames fieldNames
parameterOptions parameterOptions
tc TypeCode
mode parameterMode
fraction int16
length int16
offset uint32
chunkReader lobChunkReader
lobLocatorID locatorID
}
func newParameterField(fieldNames fieldNames) *ParameterField {
return &ParameterField{fieldNames: fieldNames}
}
// String implements the Stringer interface.
func (f *ParameterField) String() string {
return fmt.Sprintf("parameterOptions %s typeCode %s mode %s fraction %d length %d name %s",
f.parameterOptions,
f.tc,
f.mode,
f.fraction,
f.length,
f.Name(),
)
}
// TypeCode returns the type code of the field.
func (f *ParameterField) TypeCode() TypeCode {
return f.tc
}
// TypeLength returns the type length of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength
func (f *ParameterField) TypeLength() (int64, bool) {
if f.tc.isVariableLength() {
return int64(f.length), true
}
return 0, false
}
// TypePrecisionScale returns the type precision and scale (decimal types) of the field.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale
func (f *ParameterField) TypePrecisionScale() (int64, int64, bool) {
if f.tc.isDecimalType() {
return int64(f.length), int64(f.fraction), true
}
return 0, 0, false
}
// Nullable returns true if the field may be null, false otherwise.
// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable
func (f *ParameterField) Nullable() bool {
return f.parameterOptions == poOptional
}
// In returns true if the parameter field is an input field.
func (f *ParameterField) In() bool {
return f.mode == pmInout || f.mode == pmIn
}
// Out returns true if the parameter field is an output field.
func (f *ParameterField) Out() bool {
return f.mode == pmInout || f.mode == pmOut
}
// Name returns the parameter field name.
func (f *ParameterField) Name() string {
return f.fieldNames.name(f.offset)
}
// SetLobReader sets the io.Reader if a Lob parameter field.
func (f *ParameterField) SetLobReader(rd io.Reader) error {
f.chunkReader = newLobChunkReader(f.TypeCode().isCharBased(), rd)
return nil
}
//
func (f *ParameterField) read(rd *bufio.Reader) {
f.parameterOptions = parameterOptions(rd.ReadInt8())
f.tc = TypeCode(rd.ReadInt8())
f.mode = parameterMode(rd.ReadInt8())
rd.Skip(1) //filler
f.offset = rd.ReadUint32()
f.fieldNames.addOffset(f.offset)
f.length = rd.ReadInt16()
f.fraction = rd.ReadInt16()
rd.Skip(4) //filler
}
// parameter metadata
type parameterMetadata struct {
prmFieldSet *ParameterFieldSet
numArg int
}
func (m *parameterMetadata) String() string {
return fmt.Sprintf("parameter metadata: %s", m.prmFieldSet.fields)
}
func (m *parameterMetadata) kind() partKind {
return pkParameterMetadata
}
func (m *parameterMetadata) setNumArg(numArg int) {
m.numArg = numArg
}
func (m *parameterMetadata) read(rd *bufio.Reader) error {
m.prmFieldSet.read(rd)
if trace {
outLogger.Printf("read %s", m)
}
return rd.GetError()
}
// input parameters
type inputParameters struct {
inputFields []*ParameterField
args []driver.NamedValue
}
func newInputParameters(inputFields []*ParameterField, args []driver.NamedValue) *inputParameters {
return &inputParameters{inputFields: inputFields, args: args}
}
func (p *inputParameters) String() string {
return fmt.Sprintf("input parameters: %v", p.args)
}
func (p *inputParameters) kind() partKind {
return pkParameters
}
func (p *inputParameters) size() (int, error) {
size := len(p.args)
cnt := len(p.inputFields)
for i, arg := range p.args {
if arg.Value == nil { // null value
continue
}
// mass insert
field := p.inputFields[i%cnt]
fieldSize, err := fieldSize(field.TypeCode(), arg)
if err != nil {
return 0, err
}
size += fieldSize
}
return size, nil
}
func (p *inputParameters) numArg() int {
cnt := len(p.inputFields)
if cnt == 0 { // avoid divide-by-zero (e.g. prepare without parameters)
return 0
}
return len(p.args) / cnt
}
func (p *inputParameters) write(wr *bufio.Writer) error {
cnt := len(p.inputFields)
for i, arg := range p.args {
//mass insert
field := p.inputFields[i%cnt]
if err := writeField(wr, field.TypeCode(), arg); err != nil {
return err
}
}
if trace {
outLogger.Printf("input parameters: %s", p)
}
return nil
}
// output parameter
type outputParameters struct {
numArg int
s *Session
outputFields []*ParameterField
fieldValues *FieldValues
}
func (p *outputParameters) String() string {
return fmt.Sprintf("output parameters: %v", p.fieldValues)
}
func (p *outputParameters) kind() partKind {
return pkOutputParameters
}
func (p *outputParameters) setNumArg(numArg int) {
p.numArg = numArg // should always be 1
}
func (p *outputParameters) read(rd *bufio.Reader) error {
cols := len(p.outputFields)
p.fieldValues.resize(p.numArg, cols)
for i := 0; i < p.numArg; i++ {
for j, field := range p.outputFields {
var err error
if p.fieldValues.values[i*cols+j], err = readField(p.s, rd, field.TypeCode()); err != nil {
return err
}
}
}
if trace {
outLogger.Printf("read %s", p)
}
return rd.GetError()
}