...
1 package pgproto3
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "encoding/json"
7 "errors"
8 "math"
9
10 "github.com/jackc/pgio"
11 )
12
13 const (
14 TextFormat = 0
15 BinaryFormat = 1
16 )
17
18 type FieldDescription struct {
19 Name []byte
20 TableOID uint32
21 TableAttributeNumber uint16
22 DataTypeOID uint32
23 DataTypeSize int16
24 TypeModifier int32
25 Format int16
26 }
27
28
29 func (fd FieldDescription) MarshalJSON() ([]byte, error) {
30 return json.Marshal(struct {
31 Name string
32 TableOID uint32
33 TableAttributeNumber uint16
34 DataTypeOID uint32
35 DataTypeSize int16
36 TypeModifier int32
37 Format int16
38 }{
39 Name: string(fd.Name),
40 TableOID: fd.TableOID,
41 TableAttributeNumber: fd.TableAttributeNumber,
42 DataTypeOID: fd.DataTypeOID,
43 DataTypeSize: fd.DataTypeSize,
44 TypeModifier: fd.TypeModifier,
45 Format: fd.Format,
46 })
47 }
48
49 type RowDescription struct {
50 Fields []FieldDescription
51 }
52
53
54 func (*RowDescription) Backend() {}
55
56
57
58 func (dst *RowDescription) Decode(src []byte) error {
59
60 if len(src) < 2 {
61 return &invalidMessageFormatErr{messageType: "RowDescription"}
62 }
63 fieldCount := int(binary.BigEndian.Uint16(src))
64 rp := 2
65
66 dst.Fields = dst.Fields[0:0]
67
68 for i := 0; i < fieldCount; i++ {
69 var fd FieldDescription
70
71 idx := bytes.IndexByte(src[rp:], 0)
72 if idx < 0 {
73 return &invalidMessageFormatErr{messageType: "RowDescription"}
74 }
75 fd.Name = src[rp : rp+idx]
76 rp += idx + 1
77
78
79
80 if len(src[rp:]) < 18 {
81 return &invalidMessageFormatErr{messageType: "RowDescription"}
82 }
83
84 fd.TableOID = binary.BigEndian.Uint32(src[rp:])
85 rp += 4
86 fd.TableAttributeNumber = binary.BigEndian.Uint16(src[rp:])
87 rp += 2
88 fd.DataTypeOID = binary.BigEndian.Uint32(src[rp:])
89 rp += 4
90 fd.DataTypeSize = int16(binary.BigEndian.Uint16(src[rp:]))
91 rp += 2
92 fd.TypeModifier = int32(binary.BigEndian.Uint32(src[rp:]))
93 rp += 4
94 fd.Format = int16(binary.BigEndian.Uint16(src[rp:]))
95 rp += 2
96
97 dst.Fields = append(dst.Fields, fd)
98 }
99
100 return nil
101 }
102
103
104 func (src *RowDescription) Encode(dst []byte) ([]byte, error) {
105 dst, sp := beginMessage(dst, 'T')
106
107 if len(src.Fields) > math.MaxUint16 {
108 return nil, errors.New("too many fields")
109 }
110 dst = pgio.AppendUint16(dst, uint16(len(src.Fields)))
111 for _, fd := range src.Fields {
112 dst = append(dst, fd.Name...)
113 dst = append(dst, 0)
114
115 dst = pgio.AppendUint32(dst, fd.TableOID)
116 dst = pgio.AppendUint16(dst, fd.TableAttributeNumber)
117 dst = pgio.AppendUint32(dst, fd.DataTypeOID)
118 dst = pgio.AppendInt16(dst, fd.DataTypeSize)
119 dst = pgio.AppendInt32(dst, fd.TypeModifier)
120 dst = pgio.AppendInt16(dst, fd.Format)
121 }
122
123 return finishMessage(dst, sp)
124 }
125
126
127 func (src RowDescription) MarshalJSON() ([]byte, error) {
128 return json.Marshal(struct {
129 Type string
130 Fields []FieldDescription
131 }{
132 Type: "RowDescription",
133 Fields: src.Fields,
134 })
135 }
136
137
138 func (dst *RowDescription) UnmarshalJSON(data []byte) error {
139 var msg struct {
140 Fields []struct {
141 Name string
142 TableOID uint32
143 TableAttributeNumber uint16
144 DataTypeOID uint32
145 DataTypeSize int16
146 TypeModifier int32
147 Format int16
148 }
149 }
150 if err := json.Unmarshal(data, &msg); err != nil {
151 return err
152 }
153 dst.Fields = make([]FieldDescription, len(msg.Fields))
154 for n, field := range msg.Fields {
155 dst.Fields[n] = FieldDescription{
156 Name: []byte(field.Name),
157 TableOID: field.TableOID,
158 TableAttributeNumber: field.TableAttributeNumber,
159 DataTypeOID: field.DataTypeOID,
160 DataTypeSize: field.DataTypeSize,
161 TypeModifier: field.TypeModifier,
162 Format: field.Format,
163 }
164 }
165 return nil
166 }
167
View as plain text