Skip to content

Commit 6b5029b

Browse files
committed
CASSGO-43: externally-defined type registration
The new RegisterType function can be used to register externally-defined types. You'll need to define your own marshalling and unmarshalling code as well as a TypeInfo implementation. The name and id MUST not collide with existing and future native CQL types. Additionally, a lot of the type handling was refactored to use the new format for native types. Performance should be slightly improved thanks to some simplification. Benchmarks are coming soon.
1 parent 37030fb commit 6b5029b

8 files changed

+856
-516
lines changed

frame.go

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"io"
3232
"io/ioutil"
3333
"net"
34+
"reflect"
3435
"runtime"
3536
"strings"
3637
"time"
@@ -869,68 +870,98 @@ func (w *writePrepareFrame) buildFrame(f *framer, streamID int) error {
869870
return f.finish()
870871
}
871872

872-
func (f *framer) readTypeInfo() TypeInfo {
873-
// TODO: factor this out so the same code paths can be used to parse custom
874-
// types and other types, as much of the logic will be duplicated.
875-
id := f.readShort()
876-
877-
simple := NativeType{
878-
proto: f.proto,
879-
typ: Type(id),
880-
}
881-
882-
if simple.typ == TypeCustom {
883-
simple.custom = f.readString()
884-
if cassType := getApacheCassandraType(simple.custom); cassType != TypeCustom {
885-
simple.typ = cassType
886-
}
887-
}
873+
var (
874+
typeInfoType = reflect.TypeOf((*TypeInfo)(nil)).Elem()
875+
typeType = reflect.TypeOf(Type(0))
876+
typeInfoListType = reflect.TypeOf([]TypeInfo(nil))
877+
stringListType = reflect.TypeOf([]string(nil))
878+
udtFieldListType = reflect.TypeOf([]UDTField(nil))
879+
stringType = reflect.TypeOf("")
880+
shortType = reflect.TypeOf(uint16(0))
881+
byteType = reflect.TypeOf(byte(0))
882+
intType = reflect.TypeOf(int(0))
883+
)
888884

889-
switch simple.typ {
890-
case TypeTuple:
885+
func (f *framer) readForType(typ reflect.Type) interface{} {
886+
// check simple equality first
887+
switch typ {
888+
case stringType:
889+
return f.readString()
890+
case shortType:
891+
return f.readShort()
892+
case byteType:
893+
return f.readByte()
894+
case intType:
895+
return f.readInt()
896+
case stringListType:
897+
return f.readStringList()
898+
case udtFieldListType:
891899
n := f.readShort()
892-
tuple := TupleTypeInfo{
893-
NativeType: simple,
894-
Elems: make([]TypeInfo, n),
895-
}
896-
900+
fields := make([]UDTField, n)
897901
for i := 0; i < int(n); i++ {
898-
tuple.Elems[i] = f.readTypeInfo()
899-
}
900-
901-
return tuple
902-
903-
case TypeUDT:
904-
udt := UDTTypeInfo{
905-
NativeType: simple,
902+
fields[i] = UDTField{
903+
Name: f.readString(),
904+
Type: f.readTypeInfo(),
905+
}
906906
}
907-
udt.KeySpace = f.readString()
908-
udt.Name = f.readString()
909-
907+
return fields
908+
case typeInfoType:
909+
return f.readTypeInfo()
910+
case typeType:
911+
return Type(f.readShort())
912+
case typeInfoListType:
910913
n := f.readShort()
911-
udt.Elements = make([]UDTField, n)
914+
types := make([]TypeInfo, n)
912915
for i := 0; i < int(n); i++ {
913-
field := &udt.Elements[i]
914-
field.Name = f.readString()
915-
field.Type = f.readTypeInfo()
916+
types[i] = f.readTypeInfo()
916917
}
918+
return types
919+
}
917920

918-
return udt
919-
case TypeMap, TypeList, TypeSet:
920-
collection := CollectionType{
921-
NativeType: simple,
921+
// then check the kind and try to convert
922+
switch typ.Kind() {
923+
case reflect.String:
924+
return reflect.ValueOf(f.readString()).Convert(typ).Interface()
925+
case reflect.Int:
926+
return reflect.ValueOf(f.readInt()).Convert(typ).Interface()
927+
case reflect.Slice:
928+
n := f.readShort()
929+
slice := reflect.MakeSlice(typ, int(n), int(n))
930+
for i := 0; i < int(n); i++ {
931+
slice.Index(i).Set(reflect.ValueOf(f.readForType(typ.Elem())))
922932
}
933+
return slice.Interface()
934+
}
935+
panic(fmt.Errorf("unsupported type for reading from frame: %s", typ.String()))
936+
}
923937

924-
if simple.typ == TypeMap {
925-
collection.Key = f.readTypeInfo()
938+
func (f *framer) readTypeInfo() TypeInfo {
939+
typ := Type(f.readShort())
940+
941+
cqlct, ok := registeredCompositeTypes[typ]
942+
if ok {
943+
paramsTypes := cqlct.Params(int(f.proto))
944+
var params []interface{}
945+
if len(paramsTypes) > 0 {
946+
params = make([]interface{}, len(paramsTypes))
947+
for i, paramType := range paramsTypes {
948+
params[i] = f.readForType(paramType)
949+
}
926950
}
951+
return cqlct.TypeInfoParams(int(f.proto), params)
952+
}
927953

928-
collection.Elem = f.readTypeInfo()
929-
930-
return collection
954+
// custom is a special case, we need to read the name then get the type info
955+
if typ == TypeCustom {
956+
name := f.readString()
957+
return (customCQLType{}).TypeInfoParams(int(f.proto), []interface{}{name})
931958
}
932959

933-
return simple
960+
cqlt, ok := registeredTypes[typ]
961+
if !ok {
962+
panic(fmt.Errorf("unknown type id: %d", typ))
963+
}
964+
return cqlt.TypeInfo(int(f.proto))
934965
}
935966

936967
type preparedMetadata struct {

0 commit comments

Comments
 (0)