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