We can't return an error in case a field is added to the UDT,
otherwise existing code would break by simply altering the UDT in the
database. For extra fields at the end of the UDT put nulls to be in
line with gocql, but also python-driver and java-driver.
In gocql it was fixed in d2ed1bb74f
81 lines
1.7 KiB
Go
81 lines
1.7 KiB
Go
// Copyright (C) 2017 ScyllaDB
|
|
// Use of this source code is governed by a ALv2-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gocqlx
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/gocql/gocql"
|
|
"github.com/scylladb/go-reflectx"
|
|
)
|
|
|
|
// UDT is a marker interface that needs to be embedded in a struct if you want
|
|
// to marshal or unmarshal it as a User Defined Type.
|
|
type UDT interface {
|
|
udt()
|
|
}
|
|
|
|
var (
|
|
_ gocql.UDTMarshaler = udt{}
|
|
_ gocql.UDTUnmarshaler = udt{}
|
|
)
|
|
|
|
type udt struct {
|
|
field map[string]reflect.Value
|
|
value reflect.Value
|
|
unsafe bool
|
|
}
|
|
|
|
func makeUDT(value reflect.Value, mapper *reflectx.Mapper, unsafe bool) udt {
|
|
return udt{
|
|
value: value,
|
|
field: mapper.FieldMap(value),
|
|
unsafe: unsafe,
|
|
}
|
|
}
|
|
|
|
func (u udt) MarshalUDT(name string, info gocql.TypeInfo) ([]byte, error) {
|
|
value, ok := u.field[name]
|
|
|
|
var data []byte
|
|
var err error
|
|
if ok {
|
|
data, err = gocql.Marshal(info, value.Interface())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return data, err
|
|
}
|
|
|
|
func (u udt) UnmarshalUDT(name string, info gocql.TypeInfo, data []byte) error {
|
|
value, ok := u.field[name]
|
|
if !ok && !u.unsafe {
|
|
return fmt.Errorf("missing name %q in %s", name, u.value.Type())
|
|
}
|
|
|
|
return gocql.Unmarshal(info, data, value.Addr().Interface())
|
|
}
|
|
|
|
// udtWrapValue adds UDT wrapper if needed.
|
|
func udtWrapValue(value reflect.Value, mapper *reflectx.Mapper, unsafe bool) interface{} {
|
|
if value.Type().Implements(autoUDTInterface) {
|
|
return makeUDT(value, mapper, unsafe)
|
|
}
|
|
return value.Interface()
|
|
}
|
|
|
|
// udtWrapSlice adds UDT wrapper if needed.
|
|
func udtWrapSlice(mapper *reflectx.Mapper, unsafe bool, v []interface{}) []interface{} {
|
|
for i := range v {
|
|
if _, ok := v[i].(UDT); ok {
|
|
v[i] = makeUDT(reflect.ValueOf(v[i]), mapper, unsafe)
|
|
}
|
|
}
|
|
return v
|
|
}
|