1/* 2Copyright 2014 SAP SE 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package protocol 18 19import ( 20 "database/sql/driver" 21 "fmt" 22 "io" 23 24 "github.com/SAP/go-hdb/internal/bufio" 25) 26 27type parameterOptions int8 28 29const ( 30 poMandatory parameterOptions = 0x01 31 poOptional parameterOptions = 0x02 32 poDefault parameterOptions = 0x04 33) 34 35var parameterOptionsText = map[parameterOptions]string{ 36 poMandatory: "mandatory", 37 poOptional: "optional", 38 poDefault: "default", 39} 40 41func (k parameterOptions) String() string { 42 t := make([]string, 0, len(parameterOptionsText)) 43 44 for option, text := range parameterOptionsText { 45 if (k & option) != 0 { 46 t = append(t, text) 47 } 48 } 49 return fmt.Sprintf("%v", t) 50} 51 52type parameterMode int8 53 54const ( 55 pmIn parameterMode = 0x01 56 pmInout parameterMode = 0x02 57 pmOut parameterMode = 0x04 58) 59 60var parameterModeText = map[parameterMode]string{ 61 pmIn: "in", 62 pmInout: "inout", 63 pmOut: "out", 64} 65 66func (k parameterMode) String() string { 67 t := make([]string, 0, len(parameterModeText)) 68 69 for mode, text := range parameterModeText { 70 if (k & mode) != 0 { 71 t = append(t, text) 72 } 73 } 74 return fmt.Sprintf("%v", t) 75} 76 77// ParameterFieldSet contains database field metadata for parameters. 78type ParameterFieldSet struct { 79 fields []*ParameterField 80 _inputFields []*ParameterField 81 _outputFields []*ParameterField 82 names fieldNames 83} 84 85func newParameterFieldSet(size int) *ParameterFieldSet { 86 return &ParameterFieldSet{ 87 fields: make([]*ParameterField, size), 88 _inputFields: make([]*ParameterField, 0, size), 89 _outputFields: make([]*ParameterField, 0, size), 90 names: newFieldNames(), 91 } 92} 93 94// String implements the Stringer interface. 95func (f *ParameterFieldSet) String() string { 96 a := make([]string, len(f.fields)) 97 for i, f := range f.fields { 98 a[i] = f.String() 99 } 100 return fmt.Sprintf("%v", a) 101} 102 103func (f *ParameterFieldSet) read(rd *bufio.Reader) { 104 for i := 0; i < len(f.fields); i++ { 105 field := newParameterField(f.names) 106 field.read(rd) 107 f.fields[i] = field 108 if field.In() { 109 f._inputFields = append(f._inputFields, field) 110 } 111 if field.Out() { 112 f._outputFields = append(f._outputFields, field) 113 } 114 } 115 116 pos := uint32(0) 117 for _, offset := range f.names.sortOffsets() { 118 if diff := int(offset - pos); diff > 0 { 119 rd.Skip(diff) 120 } 121 b, size := readShortUtf8(rd) 122 f.names.setName(offset, string(b)) 123 pos += uint32(1 + size) 124 } 125} 126 127func (f *ParameterFieldSet) inputFields() []*ParameterField { 128 return f._inputFields 129} 130 131func (f *ParameterFieldSet) outputFields() []*ParameterField { 132 return f._outputFields 133} 134 135// NumInputField returns the number of input fields in a database statement. 136func (f *ParameterFieldSet) NumInputField() int { 137 return len(f._inputFields) 138} 139 140// NumOutputField returns the number of output fields of a query or stored procedure. 141func (f *ParameterFieldSet) NumOutputField() int { 142 return len(f._outputFields) 143} 144 145// Field returns the field at index idx. 146func (f *ParameterFieldSet) Field(idx int) *ParameterField { 147 return f.fields[idx] 148} 149 150// OutputField returns the output field at index idx. 151func (f *ParameterFieldSet) OutputField(idx int) *ParameterField { 152 return f._outputFields[idx] 153} 154 155// ParameterField contains database field attributes for parameters. 156type ParameterField struct { 157 fieldNames fieldNames 158 parameterOptions parameterOptions 159 tc TypeCode 160 mode parameterMode 161 fraction int16 162 length int16 163 offset uint32 164 chunkReader lobChunkReader 165 lobLocatorID locatorID 166} 167 168func newParameterField(fieldNames fieldNames) *ParameterField { 169 return &ParameterField{fieldNames: fieldNames} 170} 171 172// String implements the Stringer interface. 173func (f *ParameterField) String() string { 174 return fmt.Sprintf("parameterOptions %s typeCode %s mode %s fraction %d length %d name %s", 175 f.parameterOptions, 176 f.tc, 177 f.mode, 178 f.fraction, 179 f.length, 180 f.Name(), 181 ) 182} 183 184// TypeCode returns the type code of the field. 185func (f *ParameterField) TypeCode() TypeCode { 186 return f.tc 187} 188 189// TypeLength returns the type length of the field. 190// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength 191func (f *ParameterField) TypeLength() (int64, bool) { 192 if f.tc.isVariableLength() { 193 return int64(f.length), true 194 } 195 return 0, false 196} 197 198// TypePrecisionScale returns the type precision and scale (decimal types) of the field. 199// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale 200func (f *ParameterField) TypePrecisionScale() (int64, int64, bool) { 201 if f.tc.isDecimalType() { 202 return int64(f.length), int64(f.fraction), true 203 } 204 return 0, 0, false 205} 206 207// Nullable returns true if the field may be null, false otherwise. 208// see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable 209func (f *ParameterField) Nullable() bool { 210 return f.parameterOptions == poOptional 211} 212 213// In returns true if the parameter field is an input field. 214func (f *ParameterField) In() bool { 215 return f.mode == pmInout || f.mode == pmIn 216} 217 218// Out returns true if the parameter field is an output field. 219func (f *ParameterField) Out() bool { 220 return f.mode == pmInout || f.mode == pmOut 221} 222 223// Name returns the parameter field name. 224func (f *ParameterField) Name() string { 225 return f.fieldNames.name(f.offset) 226} 227 228// SetLobReader sets the io.Reader if a Lob parameter field. 229func (f *ParameterField) SetLobReader(rd io.Reader) error { 230 f.chunkReader = newLobChunkReader(f.TypeCode().isCharBased(), rd) 231 return nil 232} 233 234// 235 236func (f *ParameterField) read(rd *bufio.Reader) { 237 f.parameterOptions = parameterOptions(rd.ReadInt8()) 238 f.tc = TypeCode(rd.ReadInt8()) 239 f.mode = parameterMode(rd.ReadInt8()) 240 rd.Skip(1) //filler 241 f.offset = rd.ReadUint32() 242 f.fieldNames.addOffset(f.offset) 243 f.length = rd.ReadInt16() 244 f.fraction = rd.ReadInt16() 245 rd.Skip(4) //filler 246} 247 248// parameter metadata 249type parameterMetadata struct { 250 prmFieldSet *ParameterFieldSet 251 numArg int 252} 253 254func (m *parameterMetadata) String() string { 255 return fmt.Sprintf("parameter metadata: %s", m.prmFieldSet.fields) 256} 257 258func (m *parameterMetadata) kind() partKind { 259 return pkParameterMetadata 260} 261 262func (m *parameterMetadata) setNumArg(numArg int) { 263 m.numArg = numArg 264} 265 266func (m *parameterMetadata) read(rd *bufio.Reader) error { 267 268 m.prmFieldSet.read(rd) 269 270 if trace { 271 outLogger.Printf("read %s", m) 272 } 273 274 return rd.GetError() 275} 276 277// input parameters 278type inputParameters struct { 279 inputFields []*ParameterField 280 args []driver.NamedValue 281} 282 283func newInputParameters(inputFields []*ParameterField, args []driver.NamedValue) *inputParameters { 284 return &inputParameters{inputFields: inputFields, args: args} 285} 286 287func (p *inputParameters) String() string { 288 return fmt.Sprintf("input parameters: %v", p.args) 289} 290 291func (p *inputParameters) kind() partKind { 292 return pkParameters 293} 294 295func (p *inputParameters) size() (int, error) { 296 297 size := len(p.args) 298 cnt := len(p.inputFields) 299 300 for i, arg := range p.args { 301 302 if arg.Value == nil { // null value 303 continue 304 } 305 306 // mass insert 307 field := p.inputFields[i%cnt] 308 309 fieldSize, err := fieldSize(field.TypeCode(), arg) 310 if err != nil { 311 return 0, err 312 } 313 314 size += fieldSize 315 } 316 317 return size, nil 318} 319 320func (p *inputParameters) numArg() int { 321 cnt := len(p.inputFields) 322 323 if cnt == 0 { // avoid divide-by-zero (e.g. prepare without parameters) 324 return 0 325 } 326 327 return len(p.args) / cnt 328} 329 330func (p *inputParameters) write(wr *bufio.Writer) error { 331 332 cnt := len(p.inputFields) 333 334 for i, arg := range p.args { 335 336 //mass insert 337 field := p.inputFields[i%cnt] 338 339 if err := writeField(wr, field.TypeCode(), arg); err != nil { 340 return err 341 } 342 } 343 344 if trace { 345 outLogger.Printf("input parameters: %s", p) 346 } 347 348 return nil 349} 350 351// output parameter 352type outputParameters struct { 353 numArg int 354 s *Session 355 outputFields []*ParameterField 356 fieldValues *FieldValues 357} 358 359func (p *outputParameters) String() string { 360 return fmt.Sprintf("output parameters: %v", p.fieldValues) 361} 362 363func (p *outputParameters) kind() partKind { 364 return pkOutputParameters 365} 366 367func (p *outputParameters) setNumArg(numArg int) { 368 p.numArg = numArg // should always be 1 369} 370 371func (p *outputParameters) read(rd *bufio.Reader) error { 372 373 cols := len(p.outputFields) 374 p.fieldValues.resize(p.numArg, cols) 375 376 for i := 0; i < p.numArg; i++ { 377 for j, field := range p.outputFields { 378 var err error 379 if p.fieldValues.values[i*cols+j], err = readField(p.s, rd, field.TypeCode()); err != nil { 380 return err 381 } 382 } 383 } 384 385 if trace { 386 outLogger.Printf("read %s", p) 387 } 388 return rd.GetError() 389} 390