mirror of https://github.com/jackc/pgx.git
127 lines
2.8 KiB
Go
127 lines
2.8 KiB
Go
package pgtype
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// Record is the generic PostgreSQL record type such as is created with the
|
|
// "row" function. Record only implements BinaryEncoder and Value. The text
|
|
// format output format from PostgreSQL does not include type information and is
|
|
// therefore impossible to decode. No encoders are implemented because
|
|
// PostgreSQL does not support input of generic records.
|
|
type Record struct {
|
|
Fields []Value
|
|
Status Status
|
|
}
|
|
|
|
func (dst *Record) Set(src interface{}) error {
|
|
if src == nil {
|
|
*dst = Record{Status: Null}
|
|
return nil
|
|
}
|
|
|
|
if value, ok := src.(interface{ Get() interface{} }); ok {
|
|
value2 := value.Get()
|
|
if value2 != value {
|
|
return dst.Set(value2)
|
|
}
|
|
}
|
|
|
|
switch value := src.(type) {
|
|
case []Value:
|
|
*dst = Record{Fields: value, Status: Present}
|
|
default:
|
|
return fmt.Errorf("cannot convert %v to Record", src)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (dst Record) Get() interface{} {
|
|
switch dst.Status {
|
|
case Present:
|
|
return dst.Fields
|
|
case Null:
|
|
return nil
|
|
default:
|
|
return dst.Status
|
|
}
|
|
}
|
|
|
|
func (src *Record) AssignTo(dst interface{}) error {
|
|
switch src.Status {
|
|
case Present:
|
|
switch v := dst.(type) {
|
|
case *[]Value:
|
|
*v = make([]Value, len(src.Fields))
|
|
copy(*v, src.Fields)
|
|
return nil
|
|
case *[]interface{}:
|
|
*v = make([]interface{}, len(src.Fields))
|
|
for i := range *v {
|
|
(*v)[i] = src.Fields[i].Get()
|
|
}
|
|
return nil
|
|
default:
|
|
if nextDst, retry := GetAssignToDstType(dst); retry {
|
|
return src.AssignTo(nextDst)
|
|
}
|
|
return fmt.Errorf("unable to assign to %T", dst)
|
|
}
|
|
case Null:
|
|
return NullAssignTo(dst)
|
|
}
|
|
|
|
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
|
}
|
|
|
|
func prepareNewBinaryDecoder(ci *ConnInfo, fieldOID uint32, v *Value) (BinaryDecoder, error) {
|
|
var binaryDecoder BinaryDecoder
|
|
|
|
if dt, ok := ci.DataTypeForOID(fieldOID); ok {
|
|
binaryDecoder, _ = dt.Value.(BinaryDecoder)
|
|
} else {
|
|
return nil, fmt.Errorf("unknown oid while decoding record: %v", fieldOID)
|
|
}
|
|
|
|
if binaryDecoder == nil {
|
|
return nil, fmt.Errorf("no binary decoder registered for: %v", fieldOID)
|
|
}
|
|
|
|
// Duplicate struct to scan into
|
|
binaryDecoder = reflect.New(reflect.ValueOf(binaryDecoder).Elem().Type()).Interface().(BinaryDecoder)
|
|
*v = binaryDecoder.(Value)
|
|
return binaryDecoder, nil
|
|
}
|
|
|
|
func (dst *Record) DecodeBinary(ci *ConnInfo, src []byte) error {
|
|
if src == nil {
|
|
*dst = Record{Status: Null}
|
|
return nil
|
|
}
|
|
|
|
scanner := NewCompositeBinaryScanner(ci, src)
|
|
|
|
fields := make([]Value, scanner.FieldCount())
|
|
|
|
for i := 0; scanner.Next(); i++ {
|
|
binaryDecoder, err := prepareNewBinaryDecoder(ci, scanner.OID(), &fields[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = binaryDecoder.DecodeBinary(ci, scanner.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if scanner.Err() != nil {
|
|
return scanner.Err()
|
|
}
|
|
|
|
*dst = Record{Fields: fields, Status: Present}
|
|
|
|
return nil
|
|
}
|