...

Source file src/github.com/jackc/pgx/v5/pgproto3/data_row.go

Documentation: github.com/jackc/pgx/v5/pgproto3

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

View as plain text