pgx/messages.go

215 lines
5.3 KiB
Go

package pgx
import (
"database/sql/driver"
"math"
"reflect"
"time"
"github.com/jackc/pgx/pgconn"
"github.com/jackc/pgx/pgio"
"github.com/jackc/pgx/pgtype"
)
const (
copyData = 'd'
copyFail = 'f'
copyDone = 'c'
varHeaderSize = 4
)
type FieldDescription struct {
Name string
Table pgtype.OID
AttributeNumber uint16
DataType pgtype.OID
DataTypeSize int16
DataTypeName string
Modifier int32
FormatCode int16
}
func (fd FieldDescription) Length() (int64, bool) {
switch fd.DataType {
case pgtype.TextOID, pgtype.ByteaOID:
return math.MaxInt64, true
case pgtype.VarcharOID, pgtype.BPCharArrayOID:
return int64(fd.Modifier - varHeaderSize), true
default:
return 0, false
}
}
func (fd FieldDescription) PrecisionScale() (precision, scale int64, ok bool) {
switch fd.DataType {
case pgtype.NumericOID:
mod := fd.Modifier - varHeaderSize
precision = int64((mod >> 16) & 0xffff)
scale = int64(mod & 0xffff)
return precision, scale, true
default:
return 0, 0, false
}
}
func (fd FieldDescription) Type() reflect.Type {
switch fd.DataType {
case pgtype.Float8OID:
return reflect.TypeOf(float64(0))
case pgtype.Float4OID:
return reflect.TypeOf(float32(0))
case pgtype.Int8OID:
return reflect.TypeOf(int64(0))
case pgtype.Int4OID:
return reflect.TypeOf(int32(0))
case pgtype.Int2OID:
return reflect.TypeOf(int16(0))
case pgtype.VarcharOID, pgtype.BPCharArrayOID, pgtype.TextOID:
return reflect.TypeOf("")
case pgtype.BoolOID:
return reflect.TypeOf(false)
case pgtype.NumericOID:
return reflect.TypeOf(float64(0))
case pgtype.DateOID, pgtype.TimestampOID, pgtype.TimestamptzOID:
return reflect.TypeOf(time.Time{})
case pgtype.ByteaOID:
return reflect.TypeOf([]byte(nil))
default:
return reflect.TypeOf(new(interface{})).Elem()
}
}
// Notice represents a notice response message reported by the PostgreSQL
// server. Be aware that this is distinct from LISTEN/NOTIFY notification.
type Notice pgconn.PgError
// appendParse appends a PostgreSQL wire protocol parse message to buf and returns it.
func appendParse(buf []byte, name string, query string, parameterOIDs []pgtype.OID) []byte {
buf = append(buf, 'P')
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
buf = append(buf, name...)
buf = append(buf, 0)
buf = append(buf, query...)
buf = append(buf, 0)
buf = pgio.AppendInt16(buf, int16(len(parameterOIDs)))
for _, oid := range parameterOIDs {
buf = pgio.AppendUint32(buf, uint32(oid))
}
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
return buf
}
// appendDescribe appends a PostgreSQL wire protocol describe message to buf and returns it.
func appendDescribe(buf []byte, objectType byte, name string) []byte {
buf = append(buf, 'D')
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
buf = append(buf, objectType)
buf = append(buf, name...)
buf = append(buf, 0)
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
return buf
}
// appendSync appends a PostgreSQL wire protocol sync message to buf and returns it.
func appendSync(buf []byte) []byte {
buf = append(buf, 'S')
buf = pgio.AppendInt32(buf, 4)
return buf
}
// appendBind appends a PostgreSQL wire protocol bind message to buf and returns it.
func appendBind(
buf []byte,
destinationPortal,
preparedStatement string,
connInfo *pgtype.ConnInfo,
parameterOIDs []pgtype.OID,
arguments []interface{},
resultFormatCodes []int16,
) ([]byte, error) {
buf = append(buf, 'B')
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
buf = append(buf, destinationPortal...)
buf = append(buf, 0)
buf = append(buf, preparedStatement...)
buf = append(buf, 0)
var err error
arguments, err = convertDriverValuers(arguments)
if err != nil {
return nil, err
}
buf = pgio.AppendInt16(buf, int16(len(parameterOIDs)))
for i, oid := range parameterOIDs {
buf = pgio.AppendInt16(buf, chooseParameterFormatCode(connInfo, oid, arguments[i]))
}
buf = pgio.AppendInt16(buf, int16(len(arguments)))
for i, oid := range parameterOIDs {
var err error
buf, err = encodePreparedStatementArgument(connInfo, buf, oid, arguments[i])
if err != nil {
return nil, err
}
}
buf = pgio.AppendInt16(buf, int16(len(resultFormatCodes)))
for _, fc := range resultFormatCodes {
buf = pgio.AppendInt16(buf, fc)
}
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
return buf, nil
}
func convertDriverValuers(args []interface{}) ([]interface{}, error) {
for i, arg := range args {
switch arg := arg.(type) {
case pgtype.BinaryEncoder:
case pgtype.TextEncoder:
case driver.Valuer:
v, err := callValuerValue(arg)
if err != nil {
return nil, err
}
args[i] = v
}
}
return args, nil
}
// appendExecute appends a PostgreSQL wire protocol execute message to buf and returns it.
func appendExecute(buf []byte, portal string, maxRows uint32) []byte {
buf = append(buf, 'E')
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
buf = append(buf, portal...)
buf = append(buf, 0)
buf = pgio.AppendUint32(buf, maxRows)
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
return buf
}
// appendQuery appends a PostgreSQL wire protocol query message to buf and returns it.
func appendQuery(buf []byte, query string) []byte {
buf = append(buf, 'Q')
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
buf = append(buf, query...)
buf = append(buf, 0)
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])))
return buf
}