1package pgtype
2
3import (
4	errors "golang.org/x/xerrors"
5)
6
7// CompositeFields scans the fields of a composite type into the elements of the CompositeFields value. To scan a
8// nullable value use a *CompositeFields. It will be set to nil in case of null.
9//
10// CompositeFields implements EncodeBinary and EncodeText. However, functionality is limited due to CompositeFields not
11// knowing the PostgreSQL schema of the composite type. Prefer using a registered CompositeType.
12type CompositeFields []interface{}
13
14func (cf CompositeFields) DecodeBinary(ci *ConnInfo, src []byte) error {
15	if len(cf) == 0 {
16		return errors.Errorf("cannot decode into empty CompositeFields")
17	}
18
19	if src == nil {
20		return errors.Errorf("cannot decode unexpected null into CompositeFields")
21	}
22
23	scanner := NewCompositeBinaryScanner(ci, src)
24
25	for _, f := range cf {
26		scanner.ScanValue(f)
27	}
28
29	if scanner.Err() != nil {
30		return scanner.Err()
31	}
32
33	return nil
34}
35
36func (cf CompositeFields) DecodeText(ci *ConnInfo, src []byte) error {
37	if len(cf) == 0 {
38		return errors.Errorf("cannot decode into empty CompositeFields")
39	}
40
41	if src == nil {
42		return errors.Errorf("cannot decode unexpected null into CompositeFields")
43	}
44
45	scanner := NewCompositeTextScanner(ci, src)
46
47	for _, f := range cf {
48		scanner.ScanValue(f)
49	}
50
51	if scanner.Err() != nil {
52		return scanner.Err()
53	}
54
55	return nil
56}
57
58// EncodeText encodes composite fields into the text format. Prefer registering a CompositeType to using
59// CompositeFields to encode directly.
60func (cf CompositeFields) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
61	b := NewCompositeTextBuilder(ci, buf)
62
63	for _, f := range cf {
64		if textEncoder, ok := f.(TextEncoder); ok {
65			b.AppendEncoder(textEncoder)
66		} else {
67			b.AppendValue(f)
68		}
69	}
70
71	return b.Finish()
72}
73
74// EncodeBinary encodes composite fields into the binary format. Unlike CompositeType the schema of the destination is
75// unknown. Prefer registering a CompositeType to using CompositeFields to encode directly. Because the binary
76// composite format requires the OID of each field to be specified the only types that will work are those known to
77// ConnInfo.
78//
79// In particular:
80//
81// * Nil cannot be used because there is no way to determine what type it.
82// * Integer types must be exact matches. e.g. A Go int32 into a PostgreSQL bigint will fail.
83// * No dereferencing will be done. e.g. *Text must be used instead of Text.
84func (cf CompositeFields) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
85	b := NewCompositeBinaryBuilder(ci, buf)
86
87	for _, f := range cf {
88		dt, ok := ci.DataTypeForValue(f)
89		if !ok {
90			return nil, errors.Errorf("Unknown OID for %#v", f)
91		}
92
93		if binaryEncoder, ok := f.(BinaryEncoder); ok {
94			b.AppendEncoder(dt.OID, binaryEncoder)
95		} else {
96			err := dt.Value.Set(f)
97			if err != nil {
98				return nil, err
99			}
100			if binaryEncoder, ok := dt.Value.(BinaryEncoder); ok {
101				b.AppendEncoder(dt.OID, binaryEncoder)
102			} else {
103				return nil, errors.Errorf("Cannot encode binary format for %v", f)
104			}
105		}
106	}
107
108	return b.Finish()
109}
110