joao-r-reis commented on code in PR #1855:
URL:
https://github.com/apache/cassandra-gocql-driver/pull/1855#discussion_r2126636674
##########
frame.go:
##########
@@ -847,84 +850,96 @@ func (w *writePrepareFrame) buildFrame(f *framer,
streamID int) error {
return f.finish()
}
-func (f *framer) readTypeInfo() TypeInfo {
- // TODO: factor this out so the same code paths can be used to parse
custom
- // types and other types, as much of the logic will be duplicated.
- id := f.readShort()
-
- simple := NativeType{
- proto: f.proto,
- typ: Type(id),
- }
-
- if simple.typ == TypeCustom {
- simple.custom = f.readString()
- if cassType := getApacheCassandraType(simple.custom); cassType
!= TypeCustom {
- simple.typ = cassType
- }
- }
+var (
+ typeInfoType = reflect.TypeOf((*TypeInfo)(nil)).Elem()
+ typeType = reflect.TypeOf(Type(0))
+ typeInfoSliceType = reflect.TypeOf([]TypeInfo(nil))
+ stringSliceType = reflect.TypeOf([]string(nil))
+ udtFieldSliceType = reflect.TypeOf([]UDTField(nil))
+ stringType = reflect.TypeOf("")
+ shortType = reflect.TypeOf(uint16(0))
+ byteType = reflect.TypeOf(byte(0))
+ intType = reflect.TypeOf(int(0))
+)
- switch simple.typ {
- case TypeTuple:
+func (f *framer) readForType(typ reflect.Type) interface{} {
+ // check simple equality first
+ switch typ {
+ case stringType:
+ return f.readString()
+ case shortType:
+ return f.readShort()
+ case byteType:
+ return f.readByte()
+ case intType:
+ return f.readInt()
+ case stringSliceType:
+ return f.readStringList()
+ case udtFieldSliceType:
n := f.readShort()
- tuple := TupleTypeInfo{
- NativeType: simple,
- Elems: make([]TypeInfo, n),
- }
-
+ fields := make([]UDTField, n)
for i := 0; i < int(n); i++ {
- tuple.Elems[i] = f.readTypeInfo()
+ fields[i] = UDTField{
+ Name: f.readString(),
+ Type: f.readTypeInfo(),
+ }
}
-
- return tuple
-
- case TypeUDT:
- udt := UDTTypeInfo{
- NativeType: simple,
+ return fields
+ case typeInfoType:
+ return f.readTypeInfo()
+ case typeType:
+ return Type(f.readShort())
+ case typeInfoSliceType:
+ n := f.readShort()
+ types := make([]TypeInfo, n)
+ for i := 0; i < int(n); i++ {
+ types[i] = f.readTypeInfo()
}
- udt.KeySpace = f.readString()
- udt.Name = f.readString()
+ return types
+ }
+ // then check the kind and try to convert
+ switch typ.Kind() {
+ case reflect.String:
+ return reflect.ValueOf(f.readString()).Convert(typ).Interface()
+ case reflect.Int:
+ return reflect.ValueOf(f.readInt()).Convert(typ).Interface()
+ case reflect.Slice:
n := f.readShort()
- udt.Elements = make([]UDTField, n)
+ slice := reflect.MakeSlice(typ, int(n), int(n))
for i := 0; i < int(n); i++ {
- field := &udt.Elements[i]
- field.Name = f.readString()
- field.Type = f.readTypeInfo()
+
slice.Index(i).Set(reflect.ValueOf(f.readForType(typ.Elem())))
}
+ return slice.Interface()
+ }
+ panic(fmt.Errorf("unsupported type for reading from frame: %s",
typ.String()))
+}
- return udt
- case TypeMap, TypeList, TypeSet:
- collection := CollectionType{
- NativeType: simple,
- }
+func (f *framer) readTypeInfo() TypeInfo {
+ typ := Type(f.readShort())
+ if typ == TypeCustom {
+ name := f.readString()
+ return f.types.typeInfoFromString(int(f.proto), name)
+ }
- if simple.typ == TypeMap {
- collection.Key = f.readTypeInfo()
- }
+ if ti := f.types.fastTypeInfoLookup(typ); ti != nil {
+ return ti
+ }
- collection.Elem = f.readTypeInfo()
-
- return collection
- case TypeCustom:
- if strings.HasPrefix(simple.custom, VECTOR_TYPE) {
- spec := strings.TrimPrefix(simple.custom, VECTOR_TYPE)
- spec = spec[1 : len(spec)-1] // remove parenthesis
- idx := strings.LastIndex(spec, ",")
- typeStr := spec[:idx]
- dimStr := spec[idx+1:]
- subType :=
getCassandraLongType(strings.TrimSpace(typeStr), f.proto, nopLogger{})
- dim, _ := strconv.Atoi(strings.TrimSpace(dimStr))
- vector := VectorType{
- NativeType: simple,
- SubType: subType,
- Dimensions: dim,
- }
- return vector
- }
+ cqlt, ok := f.types.byType[typ]
+ if !ok {
+ panic(fmt.Errorf("unknown type id: %d", typ))
Review Comment:
This doesn't seem like a good idea. In a "normal" scenario this shouldn't
happen but if it does for some reason (bug somewhere for example) I think we
can just fail the request instead of causing a crash. Before this change
`readTypeInfo` would just return the `typ` without checking whether it is valid
and then (if I'm reading the old code correctly) `Marshal` would just return an
error because of the unknown type.
##########
helpers.go:
##########
@@ -542,19 +154,41 @@ func (iter *Iter) MapScan(m map[string]interface{}) bool {
return false
}
- rowData, err := iter.RowData()
- if err != nil {
- return false
- }
-
- for i, col := range rowData.Columns {
- if dest, ok := m[col]; ok {
- rowData.Values[i] = dest
+ cols := iter.Columns()
+ columnNames := make([]string, 0, len(cols))
+ values := make([]interface{}, 0, len(cols))
+ for _, column := range iter.Columns() {
Review Comment:
Why did you choose to drop the call to `RowData()` and rewrite this?
##########
marshal.go:
##########
@@ -131,60 +128,11 @@ func Marshal(info TypeInfo, value interface{}) ([]byte,
error) {
return v.MarshalCQL(info)
}
- switch info.Type() {
- case TypeVarchar, TypeAscii, TypeBlob, TypeText:
- return marshalVarchar(info, value)
- case TypeBoolean:
- return marshalBool(info, value)
- case TypeTinyInt:
- return marshalTinyInt(info, value)
- case TypeSmallInt:
- return marshalSmallInt(info, value)
- case TypeInt:
- return marshalInt(info, value)
- case TypeBigInt, TypeCounter:
- return marshalBigInt(info, value)
- case TypeFloat:
- return marshalFloat(info, value)
- case TypeDouble:
- return marshalDouble(info, value)
- case TypeDecimal:
- return marshalDecimal(info, value)
- case TypeTime:
- return marshalTime(info, value)
- case TypeTimestamp:
- return marshalTimestamp(info, value)
- case TypeList, TypeSet:
- return marshalList(info, value)
- case TypeMap:
- return marshalMap(info, value)
- case TypeUUID, TypeTimeUUID:
- return marshalUUID(info, value)
- case TypeVarint:
- return marshalVarint(info, value)
- case TypeInet:
- return marshalInet(info, value)
- case TypeTuple:
- return marshalTuple(info, value)
- case TypeUDT:
- return marshalUDT(info, value)
- case TypeDate:
- return marshalDate(info, value)
- case TypeDuration:
- return marshalDuration(info, value)
- case TypeCustom:
+ /*
if vector, ok := info.(VectorType); ok {
return marshalVector(vector, value)
- }
- }
-
- // detect protocol 2 UDT
- if strings.HasPrefix(info.Custom(),
"org.apache.cassandra.db.marshal.UserType") && info.Version() < 3 {
- return nil, ErrorUDTUnavailable
- }
-
- // TODO(tux21b): add the remaining types
- return nil, fmt.Errorf("can not marshal %T into %s", value, info)
+ }*/
Review Comment:
there's a commented section here
##########
marshal.go:
##########
@@ -1909,92 +2008,113 @@ func marshalMap(info TypeInfo, value interface{})
([]byte, error) {
buf := &bytes.Buffer{}
n := rv.Len()
- if err := writeCollectionSize(mapInfo, n, buf); err != nil {
+ if err := writeCollectionSize(c.proto, n, buf); err != nil {
return nil, err
}
keys := rv.MapKeys()
for _, key := range keys {
- item, err := Marshal(mapInfo.Key, key.Interface())
+ item, err := Marshal(c.Key, key.Interface())
if err != nil {
return nil, err
}
itemLen := len(item)
// Set the key to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
- item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface())
+ item, err = Marshal(c.Elem, rv.MapIndex(key).Interface())
if err != nil {
return nil, err
}
itemLen = len(item)
// Set the value to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
}
return buf.Bytes(), nil
}
-func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
- mapInfo, ok := info.(CollectionType)
- if !ok {
- return unmarshalErrorf("unmarshal: can not unmarshal none
collection type into map")
- }
-
+func (c CollectionType) unmarshalMap(data []byte, value interface{}) error {
rv := reflect.ValueOf(value)
if rv.Kind() != reflect.Ptr {
- return unmarshalErrorf("can not unmarshal into non-pointer %T",
value)
+ return unmarshalErrorf("can not unmarshal map into non-pointer
%T", value)
}
rv = rv.Elem()
t := rv.Type()
- if t.Kind() != reflect.Map {
- return unmarshalErrorf("can not unmarshal %s into %T", info,
value)
+ if t.Kind() == reflect.Interface {
+ if t.NumMethod() != 0 {
+ return unmarshalErrorf("can not unmarshal map into
non-empty interface %T", value)
+ }
+ var key interface{}
+ // this relies on Unmarshal marshalling default values when
presented with nil
+ if err := Unmarshal(c.Key, []byte(nil), &key); err != nil {
Review Comment:
This looks a bit odd to me, in my mind `nil` should result in a `nil` value
as output from unmarshal but we don't need to invoke the unmarshal function to
do it so it could just be a part of the "contract" of this interface that you
must return the zero value / default as output if `nil` is the input. On the
other hand having an explicit `NewDefault` or `NewZeroValue` or something like
this would make it more clear and we could just invoke that method here and in
`RowData()` and other places that rely on getting zero values from types.
##########
marshal.go:
##########
@@ -237,62 +185,12 @@ func Unmarshal(info TypeInfo, data []byte, value
interface{}) error {
return unmarshalNullable(info, data, value)
}
- switch info.Type() {
- case TypeVarchar, TypeAscii, TypeBlob, TypeText:
- return unmarshalVarchar(info, data, value)
- case TypeBoolean:
- return unmarshalBool(info, data, value)
- case TypeInt:
- return unmarshalInt(info, data, value)
- case TypeBigInt, TypeCounter:
- return unmarshalBigInt(info, data, value)
- case TypeVarint:
- return unmarshalVarint(info, data, value)
- case TypeSmallInt:
- return unmarshalSmallInt(info, data, value)
- case TypeTinyInt:
- return unmarshalTinyInt(info, data, value)
- case TypeFloat:
- return unmarshalFloat(info, data, value)
- case TypeDouble:
- return unmarshalDouble(info, data, value)
- case TypeDecimal:
- return unmarshalDecimal(info, data, value)
- case TypeTime:
- return unmarshalTime(info, data, value)
- case TypeTimestamp:
- return unmarshalTimestamp(info, data, value)
- case TypeList, TypeSet:
- return unmarshalList(info, data, value)
- case TypeMap:
- return unmarshalMap(info, data, value)
- case TypeTimeUUID:
- return unmarshalTimeUUID(info, data, value)
- case TypeUUID:
- return unmarshalUUID(info, data, value)
- case TypeInet:
- return unmarshalInet(info, data, value)
- case TypeTuple:
- return unmarshalTuple(info, data, value)
- case TypeUDT:
- return unmarshalUDT(info, data, value)
- case TypeDate:
- return unmarshalDate(info, data, value)
- case TypeDuration:
- return unmarshalDuration(info, data, value)
- case TypeCustom:
+ /*
if vector, ok := info.(VectorType); ok {
return unmarshalVector(vector, data, value)
}
- }
-
- // detect protocol 2 UDT
- if strings.HasPrefix(info.Custom(),
"org.apache.cassandra.db.marshal.UserType") && info.Version() < 3 {
- return ErrorUDTUnavailable
- }
-
- // TODO(tux21b): add the remaining types
- return fmt.Errorf("can not unmarshal %s into %T", info, value)
+ */
Review Comment:
also a commented section here
##########
marshal.go:
##########
@@ -1909,92 +2008,113 @@ func marshalMap(info TypeInfo, value interface{})
([]byte, error) {
buf := &bytes.Buffer{}
n := rv.Len()
- if err := writeCollectionSize(mapInfo, n, buf); err != nil {
+ if err := writeCollectionSize(c.proto, n, buf); err != nil {
return nil, err
}
keys := rv.MapKeys()
for _, key := range keys {
- item, err := Marshal(mapInfo.Key, key.Interface())
+ item, err := Marshal(c.Key, key.Interface())
if err != nil {
return nil, err
}
itemLen := len(item)
// Set the key to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
- item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface())
+ item, err = Marshal(c.Elem, rv.MapIndex(key).Interface())
if err != nil {
return nil, err
}
itemLen = len(item)
// Set the value to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
}
return buf.Bytes(), nil
}
-func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
- mapInfo, ok := info.(CollectionType)
- if !ok {
- return unmarshalErrorf("unmarshal: can not unmarshal none
collection type into map")
- }
-
+func (c CollectionType) unmarshalMap(data []byte, value interface{}) error {
rv := reflect.ValueOf(value)
if rv.Kind() != reflect.Ptr {
- return unmarshalErrorf("can not unmarshal into non-pointer %T",
value)
+ return unmarshalErrorf("can not unmarshal map into non-pointer
%T", value)
}
rv = rv.Elem()
t := rv.Type()
- if t.Kind() != reflect.Map {
- return unmarshalErrorf("can not unmarshal %s into %T", info,
value)
+ if t.Kind() == reflect.Interface {
+ if t.NumMethod() != 0 {
+ return unmarshalErrorf("can not unmarshal map into
non-empty interface %T", value)
+ }
+ var key interface{}
+ // this relies on Unmarshal marshalling default values when
presented with nil
+ if err := Unmarshal(c.Key, []byte(nil), &key); err != nil {
+ return err
+ }
+ if key == nil {
+ panic(fmt.Errorf("key was nil after unmarshalling from
%T", c.Key))
Review Comment:
a lot of panics here that should just be returned errors to avoid app
crashes (I understand that these particular checks are to make sure `Unmarshal`
is behaving as expected but still having an app with a lot of failed requests
is better than an app crashing most of the time.
##########
marshal.go:
##########
@@ -2623,251 +2990,23 @@ func unmarshalUDT(info TypeInfo, data []byte, value
interface{}) error {
return nil
}
-// TypeInfo describes a Cassandra specific data type.
-type TypeInfo interface {
- Type() Type
- Version() byte
- Custom() string
-
- // NewWithError creates a pointer to an empty version of whatever type
- // is referenced by the TypeInfo receiver.
- //
- // If there is no corresponding Go type for the CQL type, NewWithError
returns an error.
- NewWithError() (interface{}, error)
-}
-
-type NativeType struct {
- proto byte
- typ Type
- custom string // only used for TypeCustom
-}
-
-func NewNativeType(proto byte, typ Type) NativeType {
- return NativeType{proto, typ, ""}
-}
-
-func NewCustomType(proto byte, typ Type, custom string) NativeType {
- return NativeType{proto, typ, custom}
-}
-
-func (t NativeType) NewWithError() (interface{}, error) {
- typ, err := goType(t)
- if err != nil {
- return nil, err
- }
- return reflect.New(typ).Interface(), nil
-}
-
-func (s NativeType) Type() Type {
- return s.typ
-}
-
-func (s NativeType) Version() byte {
- return s.proto
-}
-
-func (s NativeType) Custom() string {
- return s.custom
-}
-
-func (s NativeType) String() string {
- switch s.typ {
- case TypeCustom:
- return fmt.Sprintf("%s(%s)", s.typ, s.custom)
- default:
- return s.typ.String()
- }
-}
-
-type CollectionType struct {
- NativeType
- Key TypeInfo // only used for TypeMap
- Elem TypeInfo // only used for TypeMap, TypeList and TypeSet
-}
-
-type VectorType struct {
- NativeType
- SubType TypeInfo
- Dimensions int
-}
-
-func (t CollectionType) NewWithError() (interface{}, error) {
- typ, err := goType(t)
- if err != nil {
- return nil, err
- }
- return reflect.New(typ).Interface(), nil
-}
-
-func (c CollectionType) String() string {
- switch c.typ {
- case TypeMap:
- return fmt.Sprintf("%s(%s, %s)", c.typ, c.Key, c.Elem)
- case TypeList, TypeSet:
- return fmt.Sprintf("%s(%s)", c.typ, c.Elem)
- case TypeCustom:
- return fmt.Sprintf("%s(%s)", c.typ, c.custom)
- default:
- return c.typ.String()
- }
-}
-
-type TupleTypeInfo struct {
- NativeType
- Elems []TypeInfo
-}
-
-func (t TupleTypeInfo) String() string {
- var buf bytes.Buffer
- buf.WriteString(fmt.Sprintf("%s(", t.typ))
- for _, elem := range t.Elems {
- buf.WriteString(fmt.Sprintf("%s, ", elem))
- }
- buf.Truncate(buf.Len() - 2)
- buf.WriteByte(')')
- return buf.String()
-}
-
-func (t TupleTypeInfo) NewWithError() (interface{}, error) {
- typ, err := goType(t)
- if err != nil {
- return nil, err
- }
- return reflect.New(typ).Interface(), nil
-}
-
-type UDTField struct {
- Name string
- Type TypeInfo
-}
-
-type UDTTypeInfo struct {
- NativeType
- KeySpace string
- Name string
- Elements []UDTField
-}
-
-func (u UDTTypeInfo) NewWithError() (interface{}, error) {
- typ, err := goType(u)
- if err != nil {
- return nil, err
- }
- return reflect.New(typ).Interface(), nil
-}
-
-func (u UDTTypeInfo) String() string {
- buf := &bytes.Buffer{}
-
- fmt.Fprintf(buf, "%s.%s{", u.KeySpace, u.Name)
- first := true
- for _, e := range u.Elements {
- if !first {
- fmt.Fprint(buf, ",")
- } else {
- first = false
+// NewNativeType returns a TypeInfo from the global registered types.
+// Deprecated.
+// TODO: make sure this works with known public usages
+func NewNativeType(proto byte, typ Type, custom string) TypeInfo {
+ if typ == TypeCustom {
+ // TODO: do we need to parse custom? what if its a composite
type?
+ rt := GlobalTypes.custom[custom]
Review Comment:
Yeah custom types are weird especially now that we have composite custom
types like Vector... Adding a type registration by custom type name was the way
to go until Vector came along... Now I'm not so sure.
The mechanism to provide a custom type registration in the C# and Java
drivers are very similar to this, the full type name is matched so I'm pretty
sure composite custom types are not supported. However, in the java driver you
can provide a codec for the Vector Type and it will be recognized even if the
vector type is implemented as a custom type internally so maybe it's fine to
keep the custom type registration as it is here as long as it is possible for
users to register a marshaler for the Vector type (also note that vector in v6
will be a native type not custom so that's something worth considering as we're
designing this atm).
##########
marshal.go:
##########
@@ -2404,12 +2660,139 @@ type UDTUnmarshaler interface {
UnmarshalUDT(name string, info TypeInfo, data []byte) error
}
-func marshalUDT(info TypeInfo, value interface{}) ([]byte, error) {
- udt := info.(UDTTypeInfo)
+type udtCQLType struct {
+ types *RegisteredTypes
+}
Review Comment:
Also, proper UDT support will have to include reading the system tables to
get the information about the fields of the UDT so a UDT marshaler will need
access to a connection or some kind of API that allows the marshaler to obtain
that information from the session.
With that in mind it is very likely that the interface for a UDT marshaler
will be different from the other ones or alternatively we only allow users to
provide "go type <-> cql user type" mappings instead of a complete udt
marshaler. This is another reason to just return an error if users try to do
anything with UDTs when using this new API for now.
##########
marshal.go:
##########
@@ -2404,12 +2660,139 @@ type UDTUnmarshaler interface {
UnmarshalUDT(name string, info TypeInfo, data []byte) error
}
-func marshalUDT(info TypeInfo, value interface{}) ([]byte, error) {
- udt := info.(UDTTypeInfo)
+type udtCQLType struct {
+ types *RegisteredTypes
+}
Review Comment:
I'm still not sure what the goal of the UDT registration is with this API, I
don't think users can leverage this API to replace the usage of `UDTMarshaler`
and `UDTUnmarshaler` right? I wonder if we should just return an error when a
user tries to register a UDT at the moment and create a new ticket to add
proper UDT support to this API later in a 2.x minor release.
##########
marshal.go:
##########
@@ -1909,92 +2008,113 @@ func marshalMap(info TypeInfo, value interface{})
([]byte, error) {
buf := &bytes.Buffer{}
n := rv.Len()
- if err := writeCollectionSize(mapInfo, n, buf); err != nil {
+ if err := writeCollectionSize(c.proto, n, buf); err != nil {
return nil, err
}
keys := rv.MapKeys()
for _, key := range keys {
- item, err := Marshal(mapInfo.Key, key.Interface())
+ item, err := Marshal(c.Key, key.Interface())
if err != nil {
return nil, err
}
itemLen := len(item)
// Set the key to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
- item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface())
+ item, err = Marshal(c.Elem, rv.MapIndex(key).Interface())
if err != nil {
return nil, err
}
itemLen = len(item)
// Set the value to null for supported protocols
- if item == nil && mapInfo.proto > protoVersion2 {
+ if item == nil && c.proto > protoVersion2 {
itemLen = -1
}
- if err := writeCollectionSize(mapInfo, itemLen, buf); err !=
nil {
+ if err := writeCollectionSize(c.proto, itemLen, buf); err !=
nil {
return nil, err
}
buf.Write(item)
}
return buf.Bytes(), nil
}
-func unmarshalMap(info TypeInfo, data []byte, value interface{}) error {
- mapInfo, ok := info.(CollectionType)
- if !ok {
- return unmarshalErrorf("unmarshal: can not unmarshal none
collection type into map")
- }
-
+func (c CollectionType) unmarshalMap(data []byte, value interface{}) error {
rv := reflect.ValueOf(value)
if rv.Kind() != reflect.Ptr {
- return unmarshalErrorf("can not unmarshal into non-pointer %T",
value)
+ return unmarshalErrorf("can not unmarshal map into non-pointer
%T", value)
}
rv = rv.Elem()
t := rv.Type()
- if t.Kind() != reflect.Map {
- return unmarshalErrorf("can not unmarshal %s into %T", info,
value)
+ if t.Kind() == reflect.Interface {
+ if t.NumMethod() != 0 {
+ return unmarshalErrorf("can not unmarshal map into
non-empty interface %T", value)
+ }
+ var key interface{}
+ // this relies on Unmarshal marshalling default values when
presented with nil
+ if err := Unmarshal(c.Key, []byte(nil), &key); err != nil {
+ return err
+ }
+ if key == nil {
+ panic(fmt.Errorf("key was nil after unmarshalling from
%T", c.Key))
Review Comment:
I've found other panics in this file so I recommend reevaluating all of them
but I can specifically call out them in separate comments if you'd like
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]