github.com/jackc/pgx/v5@v5.5.5/pgproto3/row_description.go (about) 1 package pgproto3 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "errors" 8 "math" 9 10 "github.com/jackc/pgx/v5/internal/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 // MarshalJSON implements encoding/json.Marshaler. 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 // Backend identifies this message as sendable by the PostgreSQL backend. 54 func (*RowDescription) Backend() {} 55 56 // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message 57 // type identifier and 4 byte message length. 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 // Since buf.Next() doesn't return an error if we hit the end of the buffer 79 // check Len ahead of time 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 // Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length. 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 // MarshalJSON implements encoding/json.Marshaler. 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 // UnmarshalJSON implements encoding/json.Unmarshaler. 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 }