github.com/jackc/pgx/v5@v5.5.5/pgproto3/function_call.go (about) 1 package pgproto3 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "math" 7 8 "github.com/jackc/pgx/v5/internal/pgio" 9 ) 10 11 type FunctionCall struct { 12 Function uint32 13 ArgFormatCodes []uint16 14 Arguments [][]byte 15 ResultFormatCode uint16 16 } 17 18 // Frontend identifies this message as sendable by a PostgreSQL frontend. 19 func (*FunctionCall) Frontend() {} 20 21 // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message 22 // type identifier and 4 byte message length. 23 func (dst *FunctionCall) Decode(src []byte) error { 24 *dst = FunctionCall{} 25 rp := 0 26 // Specifies the object ID of the function to call. 27 dst.Function = binary.BigEndian.Uint32(src[rp:]) 28 rp += 4 29 // The number of argument format codes that follow (denoted C below). 30 // This can be zero to indicate that there are no arguments or that the arguments all use the default format (text); 31 // or one, in which case the specified format code is applied to all arguments; 32 // or it can equal the actual number of arguments. 33 nArgumentCodes := int(binary.BigEndian.Uint16(src[rp:])) 34 rp += 2 35 argumentCodes := make([]uint16, nArgumentCodes) 36 for i := 0; i < nArgumentCodes; i++ { 37 // The argument format codes. Each must presently be zero (text) or one (binary). 38 ac := binary.BigEndian.Uint16(src[rp:]) 39 if ac != 0 && ac != 1 { 40 return &invalidMessageFormatErr{messageType: "FunctionCall"} 41 } 42 argumentCodes[i] = ac 43 rp += 2 44 } 45 dst.ArgFormatCodes = argumentCodes 46 47 // Specifies the number of arguments being supplied to the function. 48 nArguments := int(binary.BigEndian.Uint16(src[rp:])) 49 rp += 2 50 arguments := make([][]byte, nArguments) 51 for i := 0; i < nArguments; i++ { 52 // The length of the argument value, in bytes (this count does not include itself). Can be zero. 53 // As a special case, -1 indicates a NULL argument value. No value bytes follow in the NULL case. 54 argumentLength := int(binary.BigEndian.Uint32(src[rp:])) 55 rp += 4 56 if argumentLength == -1 { 57 arguments[i] = nil 58 } else { 59 // The value of the argument, in the format indicated by the associated format code. n is the above length. 60 argumentValue := src[rp : rp+argumentLength] 61 rp += argumentLength 62 arguments[i] = argumentValue 63 } 64 } 65 dst.Arguments = arguments 66 // The format code for the function result. Must presently be zero (text) or one (binary). 67 resultFormatCode := binary.BigEndian.Uint16(src[rp:]) 68 if resultFormatCode != 0 && resultFormatCode != 1 { 69 return &invalidMessageFormatErr{messageType: "FunctionCall"} 70 } 71 dst.ResultFormatCode = resultFormatCode 72 return nil 73 } 74 75 // Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length. 76 func (src *FunctionCall) Encode(dst []byte) ([]byte, error) { 77 dst, sp := beginMessage(dst, 'F') 78 dst = pgio.AppendUint32(dst, src.Function) 79 80 if len(src.ArgFormatCodes) > math.MaxUint16 { 81 return nil, errors.New("too many arg format codes") 82 } 83 dst = pgio.AppendUint16(dst, uint16(len(src.ArgFormatCodes))) 84 for _, argFormatCode := range src.ArgFormatCodes { 85 dst = pgio.AppendUint16(dst, argFormatCode) 86 } 87 88 if len(src.Arguments) > math.MaxUint16 { 89 return nil, errors.New("too many arguments") 90 } 91 dst = pgio.AppendUint16(dst, uint16(len(src.Arguments))) 92 for _, argument := range src.Arguments { 93 if argument == nil { 94 dst = pgio.AppendInt32(dst, -1) 95 } else { 96 dst = pgio.AppendInt32(dst, int32(len(argument))) 97 dst = append(dst, argument...) 98 } 99 } 100 dst = pgio.AppendUint16(dst, src.ResultFormatCode) 101 return finishMessage(dst, sp) 102 }