1package pgproto3 2 3import ( 4 "encoding/binary" 5 "encoding/hex" 6 "encoding/json" 7 8 "github.com/jackc/pgx/pgio" 9) 10 11type DataRow struct { 12 Values [][]byte 13} 14 15func (*DataRow) Backend() {} 16 17func (dst *DataRow) Decode(src []byte) error { 18 if len(src) < 2 { 19 return &invalidMessageFormatErr{messageType: "DataRow"} 20 } 21 rp := 0 22 fieldCount := int(binary.BigEndian.Uint16(src[rp:])) 23 rp += 2 24 25 // If the capacity of the values slice is too small OR substantially too 26 // large reallocate. This is too avoid one row with many columns from 27 // permanently allocating memory. 28 if cap(dst.Values) < fieldCount || cap(dst.Values)-fieldCount > 32 { 29 newCap := 32 30 if newCap < fieldCount { 31 newCap = fieldCount 32 } 33 dst.Values = make([][]byte, fieldCount, newCap) 34 } else { 35 dst.Values = dst.Values[:fieldCount] 36 } 37 38 for i := 0; i < fieldCount; i++ { 39 if len(src[rp:]) < 4 { 40 return &invalidMessageFormatErr{messageType: "DataRow"} 41 } 42 43 msgSize := int(int32(binary.BigEndian.Uint32(src[rp:]))) 44 rp += 4 45 46 // null 47 if msgSize == -1 { 48 dst.Values[i] = nil 49 } else { 50 if len(src[rp:]) < msgSize { 51 return &invalidMessageFormatErr{messageType: "DataRow"} 52 } 53 54 dst.Values[i] = src[rp : rp+msgSize] 55 rp += msgSize 56 } 57 } 58 59 return nil 60} 61 62func (src *DataRow) Encode(dst []byte) []byte { 63 dst = append(dst, 'D') 64 sp := len(dst) 65 dst = pgio.AppendInt32(dst, -1) 66 67 dst = pgio.AppendUint16(dst, uint16(len(src.Values))) 68 for _, v := range src.Values { 69 if v == nil { 70 dst = pgio.AppendInt32(dst, -1) 71 continue 72 } 73 74 dst = pgio.AppendInt32(dst, int32(len(v))) 75 dst = append(dst, v...) 76 } 77 78 pgio.SetInt32(dst[sp:], int32(len(dst[sp:]))) 79 80 return dst 81} 82 83func (src *DataRow) MarshalJSON() ([]byte, error) { 84 formattedValues := make([]map[string]string, len(src.Values)) 85 for i, v := range src.Values { 86 if v == nil { 87 continue 88 } 89 90 var hasNonPrintable bool 91 for _, b := range v { 92 if b < 32 { 93 hasNonPrintable = true 94 break 95 } 96 } 97 98 if hasNonPrintable { 99 formattedValues[i] = map[string]string{"binary": hex.EncodeToString(v)} 100 } else { 101 formattedValues[i] = map[string]string{"text": string(v)} 102 } 103 } 104 105 return json.Marshal(struct { 106 Type string 107 Values []map[string]string 108 }{ 109 Type: "DataRow", 110 Values: formattedValues, 111 }) 112} 113