data_row.go (3243B)
1 package pgproto3 2 3 import ( 4 "encoding/binary" 5 "encoding/hex" 6 "encoding/json" 7 8 "github.com/jackc/pgx/v5/internal/pgio" 9 ) 10 11 type DataRow struct { 12 Values [][]byte 13 } 14 15 // Backend identifies this message as sendable by the PostgreSQL backend. 16 func (*DataRow) Backend() {} 17 18 // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message 19 // type identifier and 4 byte message length. 20 func (dst *DataRow) Decode(src []byte) error { 21 if len(src) < 2 { 22 return &invalidMessageFormatErr{messageType: "DataRow"} 23 } 24 rp := 0 25 fieldCount := int(binary.BigEndian.Uint16(src[rp:])) 26 rp += 2 27 28 // If the capacity of the values slice is too small OR substantially too 29 // large reallocate. This is too avoid one row with many columns from 30 // permanently allocating memory. 31 if cap(dst.Values) < fieldCount || cap(dst.Values)-fieldCount > 32 { 32 newCap := 32 33 if newCap < fieldCount { 34 newCap = fieldCount 35 } 36 dst.Values = make([][]byte, fieldCount, newCap) 37 } else { 38 dst.Values = dst.Values[:fieldCount] 39 } 40 41 for i := 0; i < fieldCount; i++ { 42 if len(src[rp:]) < 4 { 43 return &invalidMessageFormatErr{messageType: "DataRow"} 44 } 45 46 valueLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) 47 rp += 4 48 49 // null 50 if valueLen == -1 { 51 dst.Values[i] = nil 52 } else { 53 if len(src[rp:]) < valueLen || valueLen < 0 { 54 return &invalidMessageFormatErr{messageType: "DataRow"} 55 } 56 57 dst.Values[i] = src[rp : rp+valueLen : rp+valueLen] 58 rp += valueLen 59 } 60 } 61 62 return nil 63 } 64 65 // Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length. 66 func (src *DataRow) Encode(dst []byte) []byte { 67 dst = append(dst, 'D') 68 sp := len(dst) 69 dst = pgio.AppendInt32(dst, -1) 70 71 dst = pgio.AppendUint16(dst, uint16(len(src.Values))) 72 for _, v := range src.Values { 73 if v == nil { 74 dst = pgio.AppendInt32(dst, -1) 75 continue 76 } 77 78 dst = pgio.AppendInt32(dst, int32(len(v))) 79 dst = append(dst, v...) 80 } 81 82 pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) 83 84 return dst 85 } 86 87 // MarshalJSON implements encoding/json.Marshaler. 88 func (src DataRow) MarshalJSON() ([]byte, error) { 89 formattedValues := make([]map[string]string, len(src.Values)) 90 for i, v := range src.Values { 91 if v == nil { 92 continue 93 } 94 95 var hasNonPrintable bool 96 for _, b := range v { 97 if b < 32 { 98 hasNonPrintable = true 99 break 100 } 101 } 102 103 if hasNonPrintable { 104 formattedValues[i] = map[string]string{"binary": hex.EncodeToString(v)} 105 } else { 106 formattedValues[i] = map[string]string{"text": string(v)} 107 } 108 } 109 110 return json.Marshal(struct { 111 Type string 112 Values []map[string]string 113 }{ 114 Type: "DataRow", 115 Values: formattedValues, 116 }) 117 } 118 119 // UnmarshalJSON implements encoding/json.Unmarshaler. 120 func (dst *DataRow) UnmarshalJSON(data []byte) error { 121 // Ignore null, like in the main JSON package. 122 if string(data) == "null" { 123 return nil 124 } 125 126 var msg struct { 127 Values []map[string]string 128 } 129 if err := json.Unmarshal(data, &msg); err != nil { 130 return err 131 } 132 133 dst.Values = make([][]byte, len(msg.Values)) 134 for n, parameter := range msg.Values { 135 var err error 136 dst.Values[n], err = getValueFromJSON(parameter) 137 if err != nil { 138 return err 139 } 140 } 141 return nil 142 }