1// Code generated by erb. DO NOT EDIT.
2
3package pgtype
4
5import (
6	"database/sql/driver"
7	"encoding/binary"
8	"fmt"
9	"reflect"
10
11	"github.com/jackc/pgio"
12)
13
14type VarcharArray struct {
15	Elements   []Varchar
16	Dimensions []ArrayDimension
17	Status     Status
18}
19
20func (dst *VarcharArray) Set(src interface{}) error {
21	// untyped nil and typed nil interfaces are different
22	if src == nil {
23		*dst = VarcharArray{Status: Null}
24		return nil
25	}
26
27	if value, ok := src.(interface{ Get() interface{} }); ok {
28		value2 := value.Get()
29		if value2 != value {
30			return dst.Set(value2)
31		}
32	}
33
34	// Attempt to match to select common types:
35	switch value := src.(type) {
36
37	case []string:
38		if value == nil {
39			*dst = VarcharArray{Status: Null}
40		} else if len(value) == 0 {
41			*dst = VarcharArray{Status: Present}
42		} else {
43			elements := make([]Varchar, len(value))
44			for i := range value {
45				if err := elements[i].Set(value[i]); err != nil {
46					return err
47				}
48			}
49			*dst = VarcharArray{
50				Elements:   elements,
51				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
52				Status:     Present,
53			}
54		}
55
56	case []*string:
57		if value == nil {
58			*dst = VarcharArray{Status: Null}
59		} else if len(value) == 0 {
60			*dst = VarcharArray{Status: Present}
61		} else {
62			elements := make([]Varchar, len(value))
63			for i := range value {
64				if err := elements[i].Set(value[i]); err != nil {
65					return err
66				}
67			}
68			*dst = VarcharArray{
69				Elements:   elements,
70				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
71				Status:     Present,
72			}
73		}
74
75	case []Varchar:
76		if value == nil {
77			*dst = VarcharArray{Status: Null}
78		} else if len(value) == 0 {
79			*dst = VarcharArray{Status: Present}
80		} else {
81			*dst = VarcharArray{
82				Elements:   value,
83				Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
84				Status:     Present,
85			}
86		}
87	default:
88		// Fallback to reflection if an optimised match was not found.
89		// The reflection is necessary for arrays and multidimensional slices,
90		// but it comes with a 20-50% performance penalty for large arrays/slices
91		reflectedValue := reflect.ValueOf(src)
92		if !reflectedValue.IsValid() || reflectedValue.IsZero() {
93			*dst = VarcharArray{Status: Null}
94			return nil
95		}
96
97		dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
98		if !ok {
99			return fmt.Errorf("cannot find dimensions of %v for VarcharArray", src)
100		}
101		if elementsLength == 0 {
102			*dst = VarcharArray{Status: Present}
103			return nil
104		}
105		if len(dimensions) == 0 {
106			if originalSrc, ok := underlyingSliceType(src); ok {
107				return dst.Set(originalSrc)
108			}
109			return fmt.Errorf("cannot convert %v to VarcharArray", src)
110		}
111
112		*dst = VarcharArray{
113			Elements:   make([]Varchar, elementsLength),
114			Dimensions: dimensions,
115			Status:     Present,
116		}
117		elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
118		if err != nil {
119			// Maybe the target was one dimension too far, try again:
120			if len(dst.Dimensions) > 1 {
121				dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
122				elementsLength = 0
123				for _, dim := range dst.Dimensions {
124					if elementsLength == 0 {
125						elementsLength = int(dim.Length)
126					} else {
127						elementsLength *= int(dim.Length)
128					}
129				}
130				dst.Elements = make([]Varchar, elementsLength)
131				elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
132				if err != nil {
133					return err
134				}
135			} else {
136				return err
137			}
138		}
139		if elementCount != len(dst.Elements) {
140			return fmt.Errorf("cannot convert %v to VarcharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
141		}
142	}
143
144	return nil
145}
146
147func (dst *VarcharArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
148	switch value.Kind() {
149	case reflect.Array:
150		fallthrough
151	case reflect.Slice:
152		if len(dst.Dimensions) == dimension {
153			break
154		}
155
156		valueLen := value.Len()
157		if int32(valueLen) != dst.Dimensions[dimension].Length {
158			return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
159		}
160		for i := 0; i < valueLen; i++ {
161			var err error
162			index, err = dst.setRecursive(value.Index(i), index, dimension+1)
163			if err != nil {
164				return 0, err
165			}
166		}
167
168		return index, nil
169	}
170	if !value.CanInterface() {
171		return 0, fmt.Errorf("cannot convert all values to VarcharArray")
172	}
173	if err := dst.Elements[index].Set(value.Interface()); err != nil {
174		return 0, fmt.Errorf("%v in VarcharArray", err)
175	}
176	index++
177
178	return index, nil
179}
180
181func (dst VarcharArray) Get() interface{} {
182	switch dst.Status {
183	case Present:
184		return dst
185	case Null:
186		return nil
187	default:
188		return dst.Status
189	}
190}
191
192func (src *VarcharArray) AssignTo(dst interface{}) error {
193	switch src.Status {
194	case Present:
195		if len(src.Dimensions) <= 1 {
196			// Attempt to match to select common types:
197			switch v := dst.(type) {
198
199			case *[]string:
200				*v = make([]string, len(src.Elements))
201				for i := range src.Elements {
202					if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
203						return err
204					}
205				}
206				return nil
207
208			case *[]*string:
209				*v = make([]*string, len(src.Elements))
210				for i := range src.Elements {
211					if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
212						return err
213					}
214				}
215				return nil
216
217			}
218		}
219
220		// Try to convert to something AssignTo can use directly.
221		if nextDst, retry := GetAssignToDstType(dst); retry {
222			return src.AssignTo(nextDst)
223		}
224
225		// Fallback to reflection if an optimised match was not found.
226		// The reflection is necessary for arrays and multidimensional slices,
227		// but it comes with a 20-50% performance penalty for large arrays/slices
228		value := reflect.ValueOf(dst)
229		if value.Kind() == reflect.Ptr {
230			value = value.Elem()
231		}
232
233		switch value.Kind() {
234		case reflect.Array, reflect.Slice:
235		default:
236			return fmt.Errorf("cannot assign %T to %T", src, dst)
237		}
238
239		if len(src.Elements) == 0 {
240			if value.Kind() == reflect.Slice {
241				value.Set(reflect.MakeSlice(value.Type(), 0, 0))
242				return nil
243			}
244		}
245
246		elementCount, err := src.assignToRecursive(value, 0, 0)
247		if err != nil {
248			return err
249		}
250		if elementCount != len(src.Elements) {
251			return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
252		}
253
254		return nil
255	case Null:
256		return NullAssignTo(dst)
257	}
258
259	return fmt.Errorf("cannot decode %#v into %T", src, dst)
260}
261
262func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
263	switch kind := value.Kind(); kind {
264	case reflect.Array:
265		fallthrough
266	case reflect.Slice:
267		if len(src.Dimensions) == dimension {
268			break
269		}
270
271		length := int(src.Dimensions[dimension].Length)
272		if reflect.Array == kind {
273			typ := value.Type()
274			if typ.Len() != length {
275				return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
276			}
277			value.Set(reflect.New(typ).Elem())
278		} else {
279			value.Set(reflect.MakeSlice(value.Type(), length, length))
280		}
281
282		var err error
283		for i := 0; i < length; i++ {
284			index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
285			if err != nil {
286				return 0, err
287			}
288		}
289
290		return index, nil
291	}
292	if len(src.Dimensions) != dimension {
293		return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
294	}
295	if !value.CanAddr() {
296		return 0, fmt.Errorf("cannot assign all values from VarcharArray")
297	}
298	addr := value.Addr()
299	if !addr.CanInterface() {
300		return 0, fmt.Errorf("cannot assign all values from VarcharArray")
301	}
302	if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
303		return 0, err
304	}
305	index++
306	return index, nil
307}
308
309func (dst *VarcharArray) DecodeText(ci *ConnInfo, src []byte) error {
310	if src == nil {
311		*dst = VarcharArray{Status: Null}
312		return nil
313	}
314
315	uta, err := ParseUntypedTextArray(string(src))
316	if err != nil {
317		return err
318	}
319
320	var elements []Varchar
321
322	if len(uta.Elements) > 0 {
323		elements = make([]Varchar, len(uta.Elements))
324
325		for i, s := range uta.Elements {
326			var elem Varchar
327			var elemSrc []byte
328			if s != "NULL" || uta.Quoted[i] {
329				elemSrc = []byte(s)
330			}
331			err = elem.DecodeText(ci, elemSrc)
332			if err != nil {
333				return err
334			}
335
336			elements[i] = elem
337		}
338	}
339
340	*dst = VarcharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
341
342	return nil
343}
344
345func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error {
346	if src == nil {
347		*dst = VarcharArray{Status: Null}
348		return nil
349	}
350
351	var arrayHeader ArrayHeader
352	rp, err := arrayHeader.DecodeBinary(ci, src)
353	if err != nil {
354		return err
355	}
356
357	if len(arrayHeader.Dimensions) == 0 {
358		*dst = VarcharArray{Dimensions: arrayHeader.Dimensions, Status: Present}
359		return nil
360	}
361
362	elementCount := arrayHeader.Dimensions[0].Length
363	for _, d := range arrayHeader.Dimensions[1:] {
364		elementCount *= d.Length
365	}
366
367	elements := make([]Varchar, elementCount)
368
369	for i := range elements {
370		elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
371		rp += 4
372		var elemSrc []byte
373		if elemLen >= 0 {
374			elemSrc = src[rp : rp+elemLen]
375			rp += elemLen
376		}
377		err = elements[i].DecodeBinary(ci, elemSrc)
378		if err != nil {
379			return err
380		}
381	}
382
383	*dst = VarcharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
384	return nil
385}
386
387func (src VarcharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
388	switch src.Status {
389	case Null:
390		return nil, nil
391	case Undefined:
392		return nil, errUndefined
393	}
394
395	if len(src.Dimensions) == 0 {
396		return append(buf, '{', '}'), nil
397	}
398
399	buf = EncodeTextArrayDimensions(buf, src.Dimensions)
400
401	// dimElemCounts is the multiples of elements that each array lies on. For
402	// example, a single dimension array of length 4 would have a dimElemCounts of
403	// [4]. A multi-dimensional array of lengths [3,5,2] would have a
404	// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{'
405	// or '}'.
406	dimElemCounts := make([]int, len(src.Dimensions))
407	dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length)
408	for i := len(src.Dimensions) - 2; i > -1; i-- {
409		dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
410	}
411
412	inElemBuf := make([]byte, 0, 32)
413	for i, elem := range src.Elements {
414		if i > 0 {
415			buf = append(buf, ',')
416		}
417
418		for _, dec := range dimElemCounts {
419			if i%dec == 0 {
420				buf = append(buf, '{')
421			}
422		}
423
424		elemBuf, err := elem.EncodeText(ci, inElemBuf)
425		if err != nil {
426			return nil, err
427		}
428		if elemBuf == nil {
429			buf = append(buf, `NULL`...)
430		} else {
431			buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...)
432		}
433
434		for _, dec := range dimElemCounts {
435			if (i+1)%dec == 0 {
436				buf = append(buf, '}')
437			}
438		}
439	}
440
441	return buf, nil
442}
443
444func (src VarcharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
445	switch src.Status {
446	case Null:
447		return nil, nil
448	case Undefined:
449		return nil, errUndefined
450	}
451
452	arrayHeader := ArrayHeader{
453		Dimensions: src.Dimensions,
454	}
455
456	if dt, ok := ci.DataTypeForName("varchar"); ok {
457		arrayHeader.ElementOID = int32(dt.OID)
458	} else {
459		return nil, fmt.Errorf("unable to find oid for type name %v", "varchar")
460	}
461
462	for i := range src.Elements {
463		if src.Elements[i].Status == Null {
464			arrayHeader.ContainsNull = true
465			break
466		}
467	}
468
469	buf = arrayHeader.EncodeBinary(ci, buf)
470
471	for i := range src.Elements {
472		sp := len(buf)
473		buf = pgio.AppendInt32(buf, -1)
474
475		elemBuf, err := src.Elements[i].EncodeBinary(ci, buf)
476		if err != nil {
477			return nil, err
478		}
479		if elemBuf != nil {
480			buf = elemBuf
481			pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
482		}
483	}
484
485	return buf, nil
486}
487
488// Scan implements the database/sql Scanner interface.
489func (dst *VarcharArray) Scan(src interface{}) error {
490	if src == nil {
491		return dst.DecodeText(nil, nil)
492	}
493
494	switch src := src.(type) {
495	case string:
496		return dst.DecodeText(nil, []byte(src))
497	case []byte:
498		srcCopy := make([]byte, len(src))
499		copy(srcCopy, src)
500		return dst.DecodeText(nil, srcCopy)
501	}
502
503	return fmt.Errorf("cannot scan %T", src)
504}
505
506// Value implements the database/sql/driver Valuer interface.
507func (src VarcharArray) Value() (driver.Value, error) {
508	buf, err := src.EncodeText(nil, nil)
509	if err != nil {
510		return nil, err
511	}
512	if buf == nil {
513		return nil, nil
514	}
515
516	return string(buf), nil
517}
518