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