gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }