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